Software Rewrite Strategy

By Jeffrey Sherman and Isaac Askew ·

Strategy here is mostly a sequence of decisions: whether to do large work at all, what to touch first, how to make each change safe, and how to prove it is working to people who cannot read the code. This is the framework from The Rewrite Trap, in the order you need it.

Decision 1: Is the system actually the problem?

Separate two statements that get conflated constantly:

If the second is not true, stop. Do not approve large work. "This feels gross" is not a business case, and rewriting code purely because it is sloppy is how teams end up spending a year to arrive where they started.

Decision 2: Diagnose before you prescribe

Teams love to jump to solutions before understanding causes. "We have scaling problems" can mean a performance bottleneck, a fragile script, a manual process that cannot keep up with headcount, or a design constraint that made sense early and hurts now. If you do not understand why something breaks, replacing it is a gamble.

Ask:

If your team cannot answer those clearly, there is not enough information to decide anything. Diagnosis used to be a roadblock; it is the thing AI is good at, so there is less excuse for skipping it than there was.

At a fintech company, a team planned to rewrite a large microservice wrapping a banking API: sprawling, poorly tested, and especially risky where it moved customer money. The estimate for a tested rewrite was eight months. One question derailed it. Which endpoints directly handle money movement? It was a small subset. The team fixed those and deferred the rest. Meaningful improvements shipped in three weeks, risk dropped where it mattered, and the broader rewrite stopped being urgent. "Deferred until later" outlasted everyone's tenure.

Decision 3: What does fixing it cost?

Before committing to large, irreversible work, check whether cheaper leverage is still available. If a process has 200 steps and a small change eliminates 180 of them, that may be enough for a long time. It has not been solved forever, but pain is down and you have bought time to learn what the business actually needs next.

Compare over time: what does it cost to fully automate a manual workflow? What does it cost to make it 80–90% less painful? What is the opportunity cost of leaving it manual? Aim for the smallest change with the biggest impact, and stop at diminishing returns. It is fine to leave part of a workflow manual.

Decision 4: Is modernization earned?

Signals that it is:

Even with these present, pick the narrowest modernization that relieves the constraint.

Decision 5: What do you touch first?

A rewrite answers this for you: start at line one. Iterative work does not, and the obvious answers are wrong. "Everywhere" is the same as nowhere. "Whatever feels exciting" puts the team's interest ahead of the system's. "Whatever's most broken" confuses urgency with importance.

Approach it scientifically. Form a theory about which single problem, once fixed, would help most. The theory does not have to be right; it has to find leverage. Look at modules with frequent changes, repeated outages, slow performance, or a heavy support burden. Reducing support burden often has the highest leverage, because it improves customer happiness and frees resources at the same time.

Then gather evidence by talking to the people who feel the pain: which complaints recur, what costs time or money, what slows releases. And go to the actual source. One team spent months on a framework-first plan because "the CEO is personally embarrassed by the load time," which turned out to be an intermediary's reading of a stakeholder nobody had checked with. The plan collapsed into a rewrite and derailed four separate times. One conversation would have saved months.

Pick a first target that is:

Good candidates: reporting modules, individual UI screens, load-time problems, email and notification systems, permissions logic, ingest pipelines, anything already behind an API. Avoid core billing flows and fragile infrastructure. They matter more, which is exactly why they make bad experiments.

Decision 6: How do you replace it safely?

  1. Isolate the boundary. Wrap the target behind a clean API. If you cannot isolate it, you are not ready to replace it.
  2. Capture behavior in tests. Characterization tests describe what the code does, quirks included, not what it should do.
  3. Replace behind a flag. Match behavior, not internals.
  4. Run in parallel. Same inputs to both, compare until they agree.
  5. Flip gradually. Internal users, a slice of traffic, then everyone, with a fast rollback.
  6. Tear out the old code. Delete it; source control remembers. Remove toggles and scaffolding. Update the docs.

Set an exit condition on step 4 before you start it. Parallel runs are a safety mechanism, not an architecture. Without a defined "what has to be true before we turn the old system off," temporary becomes indefinite and you are paying for both while committing to neither.

Decision 7: How do you prove it is working?

The biggest risk in the incremental path is perception. A rewrite comes with a bold narrative and manufactures a feeling of momentum even when no customer sees anything for months. Steady improvement only looks impressive in hindsight, but it is measurable while it happens.

Track the four DORA metrics: deployment frequency, change lead time, change failure rate, and mean time to recovery. All four are legible to non-engineers and commercial tools will chart them for you.

Three softer signals track the team, not the system: fewer "do not touch" areas; feature work no longer requires apology (listen for hedging, "this might take longer," "there may be surprises," and notice when it fades); and maintenance stops dominating the roadmap.

The seven ways this strategy goes wrong

Teams rarely fail because they started in the wrong place. Failure creeps in as discipline erodes:

  1. Straddling two horses. Parallel running with no exit condition.
  2. Expanding the scope. Early wins invite ambition, a full roadmap appears, and the rewrite trap returns under a new name.
  3. Premature abstraction. Building for a future that may never arrive. Require evidence before you generalize.
  4. Mistaking invisible progress for no progress. Build times drop and incidents fall before anything is visible outside the team. Report the DORA metrics or it looks like wheel-spinning.
  5. Freezing in fear. The module too dangerous to touch. Characterization tests and boundaries are the way in.
  6. Overcorrecting for the past. Rigor should match stakes; a low-traffic report does not need billing-engine treatment.
  7. Losing sight of the business. If you cannot say whether the work reduces incidents, shortens delivery, opens a market, or lowers cost, reconsider the priority.

What to leave alone

The discipline that makes this affordable is knowing what not to touch. Three kinds of problem justify going into a module: the code does not work consistently, the code needs updating for new business needs, or the code does not run fast enough. "The code is bad and developers hate it" is not on the list.

Bad code is an ambush predator. It does not come looking for you; it sits still until the day you have to walk into it, and if nothing forces you in it never bites. When something does force you in, a bug or a feature or a speed problem, that is the trigger, and you clean up while you are there.

Common questions

How do you decide whether to rewrite or refactor?

Ask whether the system is preventing the business from operating or growing, or merely unpleasant to work in. Only the first justifies large work — and even then the right answer is usually the narrowest modernization that relieves the specific constraint.

Where should you start modernizing a legacy system?

With a target that is low risk, high visibility, well understood, and painful enough to matter. Reporting modules, individual UI screens, load-time problems, notification systems, and anything already behind an API are good first choices. Avoid core billing flows and fragile infrastructure.

How do you show progress on incremental modernization?

Track the four DORA metrics — deployment frequency, change lead time, change failure rate, and mean time to recovery — plus three cultural signals: shrinking 'do not touch' areas, feature estimates that no longer come with apologies, and maintenance taking a smaller share of the roadmap.

Episodes on this

From the Never Rewrite podcast archive.

Keep reading

Browse the archive by topic