Este artículo aún no está disponible en tu idioma. Mostrando versión en inglés.
Legacy iOS Modernization: Rewrite, Modularize, or Leave It Alone
How to decide between a rewrite, incremental modularization, or leaving a legacy iOS app as it is, from a year-long Objective-C migration that shipped every release on schedule.
The question usually arrives as “should we rewrite the app?” and the useful answer starts somewhere else: what is the legacy code costing you per release, and is that cost the thing actually slowing you down? Until that’s answered, all three options are guesses dressed up as plans.
I’ve been on each side of this call. I spent a year migrating a consumer-hardware company’s app off a single-target Objective-C codebase, module by module, while the team kept shipping the whole time. No feature freeze, no six-month rewrite branch nobody could merge. +30% on performance and UX metrics by the end, shorter release cycles, and every release in between went out on schedule. Here’s how I’d separate the three options.
Leave it alone: when the cost is cosmetic
A lot of “legacy” is code people dislike rather than code that blocks them. Ugly code that ships is a different category from code that blocks you, and a stuck team tends to lump them together.
So before anything else, sort the codebase into three buckets: what’s blocking releases, what’s fine, and what’s merely ugly. If the blocker list is short and specific, fix the blockers and leave the rest. That counts as modernization too.
Rewrite: a narrow case
The rewrite is tempting because it’s easy to picture: a clean new target, the right architecture, no history. What it costs is the interim: a branch that drifts further from production every week, a team split between the old app and the new one, and a roadmap that has to pause. If your roadmap can’t pause, the rewrite gets abandoned around the time someone asks why nothing shipped this quarter.
I’d still consider it where the product has changed enough that the old code answers a different question, and where leadership has agreed in writing to a feature freeze with a date. Without that, you have a modularization project that hasn’t admitted it yet.
Modularize: the default, and harder than it’s pitched
Modularization gets pitched as a build-time problem: split the app so Xcode compiles faster. That’s real. The hard part is deciding where the boundaries go, because a wrong boundary costs you for years, and a stop-the-world rewrite to fix it later is exactly what you were trying to avoid.
Three things decided whether the migration held together.
Boundaries follow ownership, not folders. The instinct is to pull Networking/ into a module, then UI/, then Models/. That gives you modules that compile faster and change nothing else, because the coupling is still there, now with an extra build step. The split that survived was the one where a screen’s data layer, business logic and UI moved together as one unit, because that’s the unit a single engineer or pair actually owned. A “shared utilities” module everyone imports and nobody owns is where modularization efforts go to die.
Extract the smallest isolated module first. The design system and the networking layer are the natural first picks and the wrong ones, because they have the most call sites. Get one small feature module fully extracted, its public interface defined before its implementation moves, and shipped in a real release. That’s the pattern the rest of the migration reuses.
Budget the interim as its own workstream. This is the one I got wrong. I scoped the work around “get to the target architecture” and the dual-stack interim ran longer than planned. A half-modularized app has a monolith target and a growing set of modules depending on each other in ways nobody fully mapped. Set an explicit exit criterion. “Modularization is 60% done” on a dependency graph nobody diagrammed is not a real number.
Expect three things to break delivery along the way: build-time regressions while modules settle into their real dependency graph, merge conflicts in the app’s root composition point until that file gets its own owner, and a dip in the team’s confidence for a few sprints, before the new boundaries pay the speed back. Say so to the team before it happens. The full account is in modularizing a legacy iOS app without stopping delivery.
How to actually decide
Measure by release cadence, not lines migrated. The debt worth paying down is the debt actually slowing releases. Pick the option that moves cadence for the team you have.
The team you have matters more than it used to. Indeed Hiring Lab reported on 8 July 2026 that senior roles made up 71% of the net increase in software postings between May 2025 and May 2026, and on 30 July 2025 that iOS and Android developer postings were each down more than 60% against early 2020. Both are US figures. The people who can lead a migration like this are scarce, so choose a path the current team can execute without a long hiring search in the middle. The numbers with caveats are on the mobile market page.
And if the codebase isn’t big yet, draw the boundaries now. Boundaries drawn on purpose are far cheaper than boundaries excavated later.
What’s the module in your codebase that everyone agrees should be split out, and what’s actually stopping the team from starting?