·6 min read·iOS, Release Engineering, Quality

Shipping iOS apps to millions of users without breaking things

At small scale, a bad release is embarrassing. At a few million users, it's a business incident. The difference is process, not heroics.

The apps I've worked on have reached somewhere north of three and a half million people. None of that came from being especially careful on the day of a release. It came from making careless releases structurally difficult.

Make the pipeline boring

Any release step performed by a human on a laptop will eventually be performed wrong. Fastlane lanes for build, sign, test and upload; certificates and profiles managed centrally; environment configuration in build settings rather than someone's memory. A release should be a command, and a release-day rollback should be equally routine.

Ship to a fraction first

Phased release on the App Store exists and is under-used. Combine it with feature flags for anything risky and you get a real answer to the only question that matters: is the new version behaving worse than the old one? Watch crash-free sessions and your key funnel for a day before opening the gates.

Test where the bugs live

  • Unit tests on business rules, pricing, date handling and anything with edge cases — these are cheap and catch the bugs that reach production.
  • A small set of UI tests on the critical path only: launch, log in, complete the main action.
  • Snapshot or manual checks at the largest Dynamic Type sizes, which is where layouts quietly break.
  • Deliberate testing on poor networks and airplane mode, not only on WiFi.

Chasing a coverage percentage is how teams end up with thousands of tests that assert nothing. I'd rather have two hundred tests I trust.

Offline-first is a stability feature

Treating the network as optional rather than assumed removes a whole category of production problems. Show cached data immediately, refresh in the background, and make every loading state answer 'what do I do now?' rather than just spinning. Users forgive stale data far more readily than a blank screen.

Watch the right numbers

Crash-free users, cold launch time, main-thread hangs, and the completion rate of your single most important flow. Four numbers, checked after every release. If one of them moves in the wrong direction, that's your next sprint regardless of what was planned.

Where AI actually helps

I use Claude and Cursor daily — for exploring an unfamiliar module, drafting tests, refactoring mechanically, and rubber-ducking architecture. It removes real friction. What it doesn't do is own the outcome. Every line still goes through review with the same bar it always had, because the users on the other end don't grade on effort.

Building something like this?

I'm a senior mobile engineer working remotely with teams worldwide, on contract or full-time.