Skip to content
All essays

Mobile architecture

Rewrite or refactor a mobile app? Write the kill criteria first

"We should rewrite the app" is usually said in a planning meeting by someone who is tired of the old code, and decided by someone who cannot see it. How to make the case either way: what a rewrite hides, which unit you are really changing, the evidence to gather first, the conditions that stop the work, and how to put it to a manager who wants a clean slate.

13 min read

Someone in the planning meeting says the app needs a rewrite. Sometimes it is you. The codebase is old, the build is slow, every change breaks something two screens away, and a clean slate sounds like the only way out. Then the person who owns the budget asks the fair question: why would the second version go better than the first?

This article is for the engineer who has to answer that, for or against, and then live with the result. I have run 500+ technical interviews from the hiring side over 15 years in mobile, and the rewrite question comes up in staff loops in a recognizable form. The answers that hold up never start with a framework. Rewrite or refactor is the second question. The first is which unit you are changing, and what would make you stop.

My book Controlled Change gives the rewrite its own section in the chapter on legacy migration, and its verdict fits in one line: a rewrite "should not be the default response to aesthetic discomfort." The method below follows that chapter, and the parts of the book around it.

  • The decision: rewrite or refactor, and at what size.
  • The evidence: what a rewrite hides, where the time actually goes, and what the current code protects.
  • The argument: kill criteria, a one-page decision record, and the sentence to say to a manager who wants a clean slate.

Decide the unit before the method

"Rewrite or refactor" sounds like one choice about one thing. In practice it is a choice about one of three units, and the case for each is different.

UnitWhat changesWhat keeps running beside itHow hard to reverse
A module or capabilityThe networking layer, the storage engine, one vendor SDKThe old one, behind the same port, until the new one is provenModerate, if the port speaks product language
A journeyCheckout, onboarding or message composition, rebuilt end to endThe old journey, still the default for everyone outside the cohortCheap while the old path and its flag still exist
The whole appA new codebase, often a new framework, sometimes a new teamThe old app, which keeps shipping until the new one reaches parityHard, and the new app still inherits every installed old version

Rebuilding one journey behind a seam is a rewrite at the journey level and a refactor at the app level. Often what the team wants to replace is two or three journeys that hurt, and "the app" is simply easier to say than their names.

The unit also sets how much review the decision deserves. Controlled Change rates a cross-platform runtime or a local database schema as hard to reverse, and hard decisions get prototypes, exit clauses, data export and executive awareness. A whole-app rewrite into a new framework sits at that end before anyone writes a line.

What a rewrite costs that the estimate leaves out

A rewrite estimate counts screens. The book lists what that count hides, and every item on it is work the old app already did:

  • years of undocumented behavior;
  • accessibility and localization edge cases;
  • store and device compatibility;
  • analytics and experiment semantics;
  • offline data and the migrations it went through;
  • operational dashboards and support procedures;
  • security controls added after incidents;
  • feature delivery that has to continue at the same time;
  • parity criteria that keep expanding.

The last item is the one that sinks rewrites. Parity is a moving target, because the old app keeps shipping while the new one chases it. Freeze the old app and the business pays for the freeze. Do not freeze it and the finish line moves every sprint.

Mobile adds one more line. The old binary does not disappear when the new one ships: people who have not updated keep running it, and neither store makes an update mandatory on its own. The backend has to keep serving the old app, and the new one has to read or migrate what the old one stored on the device, starting with what the user wrote and what is still pending.

The three reasons that justify a rewrite

Controlled Change accepts a rewrite for three reasons, and each one comes with evidence you can bring to the meeting:

  • The platform is unsupported. Name the framework, SDK or toolchain, the support end and its date, and what breaks after it.
  • The runtime blocks a capability the product needs. Name the capability on the roadmap and the prototype that showed the current stack cannot deliver it.
  • Incremental change is demonstrably more expensive. Compare both paths on the same unit, with the hidden costs above included in the rewrite column. "Demonstrably" is the word doing the work.

Look at what is missing. "The code is ugly" is not on the list. Neither is "nobody understands it any more": the book files that under knowledge debt, behavior that depends on undocumented experts, and the cure is to capture the behavior, not to throw it away. A framework the team would like to learn is not on it either.

Discomfort with old code is a real signal. It is a signal to measure, not a budget line. If none of the three reasons applies to the unit on the table, the answer is to refactor, and the rest of this article is about doing that without stalling.

Measure where the time goes before you pick the cure

The book's casebook has a chapter on exactly this proposal, set in its fictional reference system, Atlas. Atlas has mature native iOS and Android apps and sixty mobile engineers. Product leadership believes duplicate feature work is slowing delivery and proposes a full cross-platform rewrite in eighteen months.

Before choosing a framework, the team measures how much roadmap work is truly duplicate, parity delay by feature and cause, build and CI time, quality by journey, backend contract inconsistencies, and the native code that cannot be abandoned. The analysis attributes 30 percent of the delay to duplicate domain and data logic, 25 percent to backend and API inconsistency, 20 percent to release and process differences, and the rest to UI and platform work. A shared UI rewrite would address only part of it.

Atlas rejects the full rewrite and chooses a portfolio instead: unify contracts, fixtures and analytics taxonomy, share selected pure domain rules, keep native UI for the platform-heavy journeys, pilot one shared surface only if a benchmark justifies it, and revisit after two production slices. The chapter ends on the line I would put on the slide: "Rewrite rhetoric gives way, under Staff leadership, to a portfolio of reversible bets."

The numbers are fiction; the method is not. Split the pain by cause before you argue about the cure. If most of the delay sits in the backend contract or the release process, a new client codebase does not touch it, and you will have spent a year to learn that.

Characterize first, because both options need it

Characterization means recording what the current code actually does: state transitions, request and response fixtures, database snapshots, screenshots at large text sizes, analytics contracts, and the edge cases support already knows about. The book adds the line that keeps this honest: "Characterization tests are not an endorsement." You record behavior so you can decide what is intentional, what is accidental and what is obsolete.

For the decision, this is the cheapest step on the table, and the one step both camps need. A refactor needs it as a safety net. A rewrite needs it as the definition of parity; without it, "feature parity" means whatever the last person to look remembered. So do it before the decision, not after. It often changes the argument, because it shows how much the old code was quietly protecting.

The step-by-step procedure, from characterization to the last deletion, is in the architecture migration answer. Here it matters as evidence, not as a plan.

Seams and slices turn one big bet into small ones

A seam is a place where you can observe or substitute behavior without rewriting the system around it: a repository facade, a navigation entry point, a network interceptor, a flag. It makes the choice smaller. Put a product-shaped facade in front of one journey, keep the old implementation behind it, build the new one beside it, and let a flag decide which runs. You are now rewriting one journey, with the old one still there if you are wrong.

Then slice vertically: one booking type, one deep link entry, one message composition path. The plan that fails is horizontal, the whole data layer first, then the domain, then the UI, with two systems running everywhere and no journey better for months. That matters for the argument too: a vertical slice gives you something to show at the next planning review, and a horizontal one is easy to cancel.

Keep the seam small. The book's warning holds for either camp: do not start with a universal dependency injection container and fifty protocols. If the seam should become a real module boundary, the test for that is in when to modularize a mobile app.

Write the kill criteria before you start

Kill criteria are the conditions that pause or stop the work, agreed before the first commit. Written later, they get negotiated against sunk cost. A date on its own is not enough; the book's rule for revisiting any decision is that "Time alone is a weak trigger." Pair the date with evidence.

CriterionWhat to write downTemplate wording
DateA checkpoint, and the evidence expected by then"By [date], the first journey runs on the new path for [cohort]. If not, we stop and review."
MetricField quality on the migrated journey, compared with the old path on the same journey"Crash-free sessions and completion rate no worse than the old path for [N] releases."
BudgetCapacity spent before users see anything"No more than [N] engineer-weeks before the first slice reaches users."
ParityA fixed list, taken from characterization"Parity is the listed behaviors. New requests go on a separate list."
OwnerWho reviews the evidence and can call stop"[Name] decides at each checkpoint, with [sponsor] informed."

Controlled Change adds stop conditions that no table row catches on its own: the bridge becomes the dominant complexity, product assumptions change, parity work is worth less than keeping a bounded legacy island, or the business case no longer pays.

Stopping is allowed to be the right result. "Stopping a migration can be responsible architecture if the remaining boundary is owned and contained." The refactor needs kill criteria just as much, because its failure is quieter: it never ends. My book Before the Room puts the risk plainly: "A migration frozen at ninety percent can cost more than the problem it set out to solve." Give the incremental path an end date and a deletion owner, or it becomes a permanent second system.

How to decide: one page, seven lines

Before the meeting, write one page. It works for either recommendation, and it is short enough to be read in the meeting itself:

  • The pressure, as an outcome a product owner would recognize, with its evidence. "Move to a new architecture" is not a pressure; "a change to checkout touches four teams and takes six weeks" is.
  • The unit: module, journey or app, and why that one.
  • The justification: which of the three reasons applies, with evidence, or "none, so incremental".
  • The options, including keeping the current code. The book counts "options without keep-current" among the ways proposals fail.
  • The first reversible step: characterization, one seam, one slice.
  • The kill criteria: date, metric, budget, parity list, and who can call stop.
  • The end: who deletes the old path, and when. Making the new path the default is not the end; the old code, schemas, flags, dashboards and support paths still have to go.

If line 3 is empty, the answer is refactor. If line 6 is empty, neither option is ready to approve.

How to present it to a manager who wants a clean slate

A manager who asks for a clean slate is often asking for something underneath it: faster delivery, fewer incidents, a team that stops losing weeks to the same module, or a story for their own leadership. Answer that, not the framework question.

Speak in their terms. The book's list is release speed, reliability, feature capability, support cost and regulatory need; "cleaner architecture" is not on it. Name the debt by its consequence, such as unrelated parts that always move together or behavior that lives in one person's head, and keep the language neutral: calling the old code names insults people who worked under constraints you have not seen yet.

Then bring options, not a verdict. Three options with their trade-offs and one recommendation invite a decision; a single proposal invites a debate about your judgment. A sentence that works in either direction:

"I agree the current app is costing us, and I can show where. Before we commit to a rewrite, I want two weeks to measure where the time goes and to capture what checkout does today. Then I will bring three options: a full rewrite, a journey-by-journey rebuild behind a seam, and targeted fixes to the current code. Each comes with its cost, a first step we can reverse, and the conditions that would make us stop. The decision I need from you is which outcome we are buying, not which framework."

If the change spans several teams, add the part most plans leave out: who pays. Before the Room's chapter on migrations names the pattern, a benefit that is global and a cost that lands locally on whoever moves first, and its sharpest line is "A sponsor blessing the target does not fund the move." The decision to put in front of the sponsor is whether adoption is funded as portfolio work or competes against each team's roadmap.

And if the answer is no, or the rewrite was decided above you, treat it as a decision. Record who decided, what was chosen, the risks accepted, and what would reopen it. Controlled Change has a chapter on exactly this case, and its advice is to neither relitigate nor comply in silence: "The architect's job is to decide everything the decision did not." If there is no one who can own the decision at all, that is an escalation, covered in how to escalate as a tech lead.

If the answer is a rewrite

Sometimes the evidence says rewrite, and then the work is to keep it honest. A justified rewrite still coexists with the installed old versions, the data already on devices and the backend contract they depend on, so the coexistence plan does not go away. It only gets longer.

Shape it in waves, each delivering product or risk value with its own stop criteria. The book's eighteen-month roadmap for Atlas starts with evidence, contracts and fixtures, and expands only proven boundaries in the last wave. The shared UI, the part the original proposal was about, is an optional pilot in the fourth wave.

And define done the way the book does: the old path is deleted, with evidence. A rewrite declared finished while the old app still ships is two apps.

Questions engineers ask about rewriting mobile apps

Should we rewrite our mobile app or refactor it?

Decide the unit first: a module, a journey or the whole app. Then check the three reasons that justify a rewrite: the platform is unsupported, the runtime blocks a capability the product needs, or incremental change is demonstrably more expensive. If none applies, refactor behind a seam, one journey at a time. If one applies, rewrite the smallest unit that removes it, with kill criteria agreed before you start.

How do I convince management to approve a rewrite, or to drop one?

Do not argue about frameworks or code quality. Bring the pressure as an outcome they recognize, such as release speed, incidents, support cost or a capability on the roadmap, with evidence. Offer three options including keeping the current code, recommend one, give each a first reversible step and kill criteria, and ask for a decision on the outcome, not the technology.

What are the risks of a big bang rewrite of a mobile app?

The cost the estimate does not count: undocumented behavior, accessibility and localization edge cases, analytics semantics, on-device data, security controls added after incidents, and parity criteria that keep expanding while the old app keeps shipping. On mobile, the old binary also stays installed on devices that have not updated.

What are kill criteria for a rewrite or a migration?

Conditions written and agreed before the work starts that pause or stop it: a dated checkpoint with the evidence expected by then, a field quality metric compared with the old path on the same journey, a capacity budget, a fixed definition of parity, and a named person who can call stop. Written after the work starts, they get negotiated against sunk cost.

Share this essay