Development
The Boring Parts of iOS Apps That Actually Matter
Modern iOS development is full of exciting things to talk about: new Swift features, shiny UI frameworks, clever architecture diagrams. But most production issues don’t come from those places.
January 21, 2026

On naming, structure, error states, and defaults—the unglamorous work that keeps apps stable and understandable

They come from the boring parts.

The parts no one writes blog posts about.
The parts that feel obvious until they aren’t.
The parts that quietly determine whether an app is understandable six months later.

This article is about those parts—and why they matter more than we like to admit.

Naming: The First Line of Documentation

Names are the most frequently read part of your code. Long before anyone opens a README or a design doc, they read your variable names, type names, and function signatures.

Good naming reduces the need for comments. Bad naming multiplies confusion.

What good naming looks like

Good names describe intent, not implementation. They reflect the problem domain, not the mechanics of how something works.

// ❌ Poor naming
let dataManager = UserManager()
dataManager.load()
// ✅ Better naming
let userRepository = UserRepository()
userRepository.fetchCurrentUser()

In the second example, we know:

  • What kind of data is involved
  • Where it comes from conceptually
  • What the operation actually does

Common iOS naming pitfalls

Some names should make you pause immediately:

class NetworkManager { }
class DataHelper { }
class Utils { }

These names grow endlessly because they communicate nothing about responsibility. They become dumping grounds.

A good litmus test:

If you can’t describe what a type does without using the word “stuff,” the name is wrong.

Renaming is not free—but neither is letting confusion compound indefinitely.

Structure: Code Organization as a Communication Tool

Code structure is not about aesthetics. It’s about how fast someone can answer questions:

  • Where does this logic live?
  • What happens when this screen loads?
  • What breaks if I change this?

Well-structured code answers those questions without searching.

A structural smell: the god view controller

class ProfileViewController: UIViewController {    
	func viewDidLoad() {        
  		fetchUser()        
  		configureUI()        
  		trackAnalytics()        
  		validateSession()        
  		handleErrors()    
  	}
}

This controller is doing everything—and therefore explaining nothing.

A more communicative structure

class ProfileViewController: UIViewController { 
	private let viewModel: ProfileViewModel    
  
	func viewDidLoad() {        
  		super.viewDidLoad()        
		viewModel.load()    
  	}
}


Now responsibility is obvious:

  • The view controller displays
  • The view model coordinates behavior
  • Side effects have a clear home

Structure becomes a map. When the map is predictable, onboarding is faster and refactors are safer.

Error States: Designing for the App You Don’t Want to See

Most apps are designed for the happy path. Real users do not live on that path.

They lose connectivity.
They log in on old devices.
They receive incomplete data.

Ignoring error states does1 not make them disappear—it just makes them surface as crashes or silent failures.

The silent failure problem

func loadProfile() {    
	api.fetchProfile { profile in        
		self.nameLabel.text = profile.name    
	}
}

What happens if the request fails?
What happens if profile is nil?

Nothing—and that’s the problem.

Making errors explicit and visible1

func loadProfile() {
	api.fetchProfile { result in        
        switch result {        
      	case .success(let profile):            
      		self.render(profile)        
      	case .failure(let error):            
      		self.showErrorState(error)
        }  
    }
}

Now failure is part of the design—not an afterthought.

Well-designed error states:

  • Explain what went wrong
  • Offer recovery when possible
  • Fail loudly during development

An app that handles failure gracefully feels reliable, even when things go wrong.

Defaults: Decisions You Make Even When You Don’t

Defaults are unavoidable. Every optional, every configuration value, every initial state has one—even if you never wrote it down.

The danger lies in implicit defaults.

Implicit defaults cause invisible bugs

struct FeatureFlags {    
	var isNewOnboardingEnabled: Bool?
}

if featureFlags.isNewOnboardingEnabled == true {    
	showNewOnboarding()
}

What happens when the flag is nil?
The behavior is undefined—and likely undocumented.

Explicit defaults communicate intent

struct FeatureFlags {    
	let isNewOnboardingEnabled: Bool    
  
  init(isNewOnboardingEnabled: Bool = false) {        
  	self.isNewOnboardingEnabled = isNewOnboardingEnabled    
  }
}

Now the system has:

  • A predictable baseline
  • A documented decision
  • Fewer edge cases

Defaults should be conservative. When in doubt, choose the option that fails safely and visibly.

The Compound Effect of “Boring” Decisions

None of these practices are impressive in isolation. No one applauds a well-named variable.

But together, they compound:

  • Clear naming reduces mental overhead
  • Good structure limits blast radius
  • Explicit error handling prevents cascading failures
  • Thoughtful defaults eliminate ambiguity

Over time, these decisions determine whether a codebase accelerates—or collapses under its own weight.

Conclusion: Professionalism Over Novelty

Boring code is not a lack of skill.
It is the result of discipline.

The best production iOS apps are not clever—they are understandable. They favor clarity over novelty and stability over shortcuts.

When something breaks, the cause is obvious.
When something changes, the impact is contained.
When someone new joins the team, they can find their footing quickly.

These qualities are invisible when present—and painfully obvious when absent.

The boring parts are not optional.
They are the foundation.

Sign up for our newsletter

Thoughts and reflections from our creative journey.
Learn how we handle your data in our privacy policy.
Thank you. Your message has been sent.
Something went wrong with your submission.