Software Rewrite Strategy
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:
- This system is unpleasant to work with.
- This system is preventing the business from operating or growing.
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:
- What fails, exactly, and how often?
- Is it data size, traffic, timeouts, memory, or workflow?
- Is the bottleneck technical, operational, or organizational?
- What part of this pain is customer-facing versus internal?
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:
- You cannot meet customer demand without unacceptable reliability risk.
- Operating cost is rising faster than revenue or growth.
- Incident frequency is increasing and root causes are systemic.
- Delivery speed is constrained by architecture.
- Hiring and retention are taking real hits because of the system.
- The platform faces a forcing function: end-of-life, a compliance deadline, an infrastructure limit.
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:
- Low risk. Failure will not bring down the system.
- High visibility. Users should notice if it works.
- Well understood. The team knows how it behaves and why.
- Painful enough. It is clearly causing trouble now.
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?
- Isolate the boundary. Wrap the target behind a clean API. If you cannot isolate it, you are not ready to replace it.
- Capture behavior in tests. Characterization tests describe what the code does, quirks included, not what it should do.
- Replace behind a flag. Match behavior, not internals.
- Run in parallel. Same inputs to both, compare until they agree.
- Flip gradually. Internal users, a slice of traffic, then everyone, with a fast rollback.
- 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:
- Straddling two horses. Parallel running with no exit condition.
- Expanding the scope. Early wins invite ambition, a full roadmap appears, and the rewrite trap returns under a new name.
- Premature abstraction. Building for a future that may never arrive. Require evidence before you generalize.
- 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.
- Freezing in fear. The module too dangerous to touch. Characterization tests and boundaries are the way in.
- Overcorrecting for the past. Rigor should match stakes; a low-traffic report does not need billing-engine treatment.
- 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.