A rewrite vs refactor decision is rarely a purely technical choice. Get it wrong and the company funds two systems at once: the old platform that still runs the business, and the new platform that is late, incomplete, and politically protected because so much money has already been spent. According to McKinsey and the University of Oxford (2012), large IT projects in their dataset ran 45% over budget on average and delivered 56% less value than predicted. (mckinsey.com)
TL;DR
- Most legacy system rewrite proposals confuse architecture risk with knowledge risk.
- No tests means you cannot refactor safely, but it also means you cannot verify a rewrite.
- A full rewrite is justified only when the current system cannot be made safe, supported, or economically changeable.
- The strangler fig pattern often fits mid-market cash flow better because it replaces risk in slices.
- Before approving budget, assess code, dependencies, business logic, delivery process, and cutover cost.
Why is “the architecture is wrong” usually not the real reason?
“The architecture is wrong” often means the team no longer understands the system well enough to change it confidently. That is a key-person and knowledge-transfer problem first. A rewrite may hide that problem for a year, then recreate it in a newer stack with fewer people understanding the replacement.
The strongest anti-rewrite argument is still Joel Spolsky’s “Things You Should Never Do, Part I”, published on Joel on Software in April 2000. Spolsky used Netscape 6.0, Borland, dBase for Windows, Quattro Pro, Microsoft Access, Microsoft Word for Windows, and Project Pyramid to argue that teams underestimate the knowledge embedded in working code. (joelonsoftware.com)
His central point has aged well: old code contains bug fixes, edge cases, operational scars, and business decisions that nobody documented. When engineers call it “messy”, they may be reacting to cognitive load rather than objective failure. Reading a system is harder than writing a new one; that does not make the new one cheaper.
In mid-market companies, rewrite pressure usually comes from four sources:
- Nobody understands the code. The original authors left, and current engineers avoid high-risk modules.
- The stack feels unfashionable. Hiring is harder, tooling feels old, and new leaders associate modernity with credibility.
- The original team left no map. Business rules live in tickets, database constraints, scheduled jobs, and support folklore.
- A new lead wants a clean start. This is human, but it is not a business case.
These motives are not irrational. They are just incomplete. A CTO should translate them into risk language a CEO or CFO can evaluate: dependency risk, continuity risk, hiring risk, defect risk, release risk, and opportunity cost.
The danger is that a legacy system rewrite creates the illusion of control. The first six months feel productive because the team is building greenfield components quickly. Then the hard parts arrive: exceptions, reporting quirks, customer-specific workflows, integrations, data migration, permission rules, reconciliation, and cutover.
According to Flyvbjerg, Budzier, Lee, Keil, Lunn, and Bester (2022), IT project cost overruns follow a fat-tailed distribution, meaning extreme overruns occur more often than managers assuming normal risk would expect. (arxiv.org) That is why “we will rewrite it properly this time” is not enough. The question is not whether the existing architecture is ugly. The question is whether replacing it reduces total business risk faster than understanding and improving it.
What can refactoring fix — and where does it stop?
Refactoring can improve changeability, readability, modularity, testability, and local design without changing external behaviour. It cannot rescue a fundamentally wrong domain model, remove all platform risk, or make an unsupported runtime safe forever. A refactor legacy application plan must respect those limits or it becomes a rewrite by stealth.
Martin Fowler defines refactoring as restructuring software without changing its observable behaviour. (martinfowler.com) That definition matters because many “refactoring” proposals are not refactoring at all. They are feature changes, data model changes, framework migrations, integration rewrites, and deployment redesigns bundled under a word that sounds less expensive.
Good refactoring works when the system’s behaviour is broadly correct but the cost of change is rising. It can help when a checkout workflow is too tangled, a pricing module has duplicated rules, or a service boundary has become unclear. It can also reduce onboarding risk by turning implicit structure into named concepts and smaller seams.
Refactoring is the right default when:
- the business rules are valid but hard to find;
- the platform is still supportable;
- defects come from local complexity rather than systemic failure;
- the team can add tests around critical flows;
- change demand is steady rather than existential.
It is not magic. Refactoring cannot make a dead database vendor safe, change a data model that contradicts the business, or undo a decade of incorrect product assumptions. It also cannot guarantee delivery if the organisation keeps interrupting engineers with urgent production fixes.
A full rewrite becomes more credible when the current system blocks necessary business operations rather than merely slowing engineering. Examples include a domain model that cannot represent current contracts, a data architecture that prevents statutory reporting, or a runtime that cannot receive security updates. Even then, “full rewrite” should be treated as the burden of proof, not the default.
Spolsky’s warning is useful here because he does not claim all code is worth saving. He argues that teams throw away working knowledge too casually. (joelonsoftware.com) For a CTO, the practical move is to separate salvageable complexity from structural impossibility. The former deserves refactoring. The latter may justify replacement, but only after evidence shows that incremental repair would cost more than controlled migration.
Which five questions decide when to rewrite legacy code?
Five questions decide when to rewrite legacy code: can you verify behaviour, do you understand the business rules, is the platform supported, can you afford parallel running, and does revenue depend on it today? The answers convert an emotional architecture debate into a cash-flow, continuity, and delivery-risk decision.
Start with verification. If there is no automated coverage around critical workflows, you cannot safely change the old system. But the same fact weakens the rewrite case: you also cannot prove the new system behaves correctly. The first investment is often test coverage as the precondition for safe refactoring, not a new codebase.
Second, ask where the business logic lives. If pricing, eligibility, fulfilment, claims, settlement, or reporting rules exist only in people’s heads, a rewrite will become an expensive archaeology project. Engineers will rediscover rules through production defects, stakeholder arguments, and late-stage acceptance testing.
Third, assess platform support. Unsupported runtimes, unpatched libraries, old operating systems, and abandoned database versions change the risk profile. This does not automatically mean “rewrite everything”. It may mean rehosting, runtime upgrade, dependency isolation, or replacing the thinnest unsafe layer first.
Fourth, test the parallel-run assumption. A full legacy system rewrite usually needs a period where old and new systems coexist. That means duplicate infrastructure, duplicate support, reconciliation tooling, extra QA, migration scripts, rollback plans, and business users who can compare outputs. If the CFO cannot fund that overlap, the delivery plan is already undercapitalised.
Fifth, determine whether revenue flows through the system now. A revenue-critical platform should not become a laboratory unless the current state is already intolerable. If the system invoices customers, accepts orders, prices contracts, manages stock, or supports regulated processes, the migration strategy must protect continuity before elegance.
These questions also expose false certainty. A team may say “rewrite” because there are no tests. But no tests increase rewrite risk too. A product leader may say “the old system blocks growth”, but the actual bottleneck may be deployment process, data quality, or manual approval workflow.
According to McKinsey and the University of Oxford (2012), large IT projects become riskier as duration increases; their analysis reported that every additional year in project length increased cost overruns by 15%. (mckinsey.com) That matters for rewrites because they compress value into a late cutover. Refactoring and strangler fig approaches can release risk reduction earlier, giving executives better control over spend.
Which rewrite vs refactor decision matrix should a CTO use?
Use a decision matrix that weighs verification, knowledge, platform support, parallel-run affordability, and revenue criticality together. No single answer decides the outcome. Refactor when behaviour is understood and supportable; use strangler fig when risk must be reduced incrementally; rewrite only when the current foundation cannot meet business needs safely.
| Outcome | Test coverage | Business logic | Platform support | Parallel running | Revenue exposure |
|---|---|---|---|---|---|
| Refactor | Critical flows can be covered before change | Rules are discoverable in code, tests, users, or documentation | Runtime and dependencies are still supportable | Not required beyond normal release controls | Revenue flows through it, so minimise behavioural change |
| Strangler fig | Coverage can be built around seams and migrated slices | Rules can be captured domain by domain | Some components are risky, but not all require replacement | Affordable for selected capabilities, integrations, or user groups | Revenue exposure is high, so reduce cutover scope |
| Full rewrite | Existing behaviour cannot be verified or is no longer desired | Rules must be redesigned, not copied | Core platform is unsupported or blocks essential change | Business can fund dual running, reconciliation, and rollback | Revenue exposure is manageable, isolated, or already at unacceptable risk |
The matrix is deliberately conservative. A rewrite must clear several thresholds because it carries concentrated delivery risk. If only one column looks bad, the answer is usually not a full rebuild. If three or more columns are red, the CTO should investigate replacement seriously.
For example, an unsupported runtime alone may justify technical migration, but not necessarily a new product architecture. Poor test coverage alone should trigger test harnesses and characterisation tests, not a blank repository. Undocumented business logic alone should trigger discovery, documentation, and shadow calculations.
The most dangerous cell is revenue exposure. If money flows through the legacy system every hour, the rewrite plan must include operating cost for the old system until the new system proves itself. That is where rewrites run into finance. The delivery budget is not just “build the replacement”. It is build, validate, reconcile, migrate, train, operate, monitor, and retire.
This is why CEOs and CFOs should ask for option economics, not technical labels. What is the smallest investment that reduces the largest risk? What decision will become irreversible? Which option preserves cash if market conditions change? Which option lets the company stop early with useful value?
According to Spolsky (2000), Microsoft avoided a strategic disaster with Word for Windows because it kept working on the old codebase while Project Pyramid failed. (joelonsoftware.com) That lesson applies directly to mid-market rewrites: never assume the replacement will arrive before the old system needs another business-critical change.
Why is strangler fig the third option mid-market teams should consider?
The strangler fig pattern replaces a legacy system incrementally by building new capabilities around the edges, routing selected behaviour to the new system, and retiring old components over time. It often fits mid-market cash flow better than pure refactoring or a full rewrite because value and risk reduction arrive in smaller releases.
Martin Fowler introduced the original Strangler Fig Application post in 2004 and later refreshed the terminology in 2024, explaining the metaphor of gradual replacement around an existing host system. (martinfowler.com) In practical terms, you place a controlled boundary around the legacy application, intercept selected events or requests, and move one business capability at a time.
This approach works well when the current system is too important to stop but too costly to keep unchanged. It lets a CTO sequence migration by business value: customer onboarding first, reporting next, pricing later, or whichever capability creates the most risk. It also gives the CFO staged funding gates instead of one large bet.
A useful strangler plan usually includes:
- a routing layer or API boundary;
- event interception or data synchronisation;
- shared identity and permission handling;
- reconciliation between old and new outputs;
- migration metrics for each capability;
- a retirement plan for legacy modules.
The pattern is not easy. It introduces temporary complexity because two worlds coexist. You need disciplined architecture, automated tests, observability, and careful cutover planning. That is why parallel-run infrastructure and cutover planning matter as much as application code.
Fowler’s original argument was not that strangler fig is cheaper in every narrow accounting sense. It was that frequent releases reduce risk and avoid many unnecessary features that cutover rewrites generate. (martinfowler.com) For a 50–500 person company, that distinction is vital. The board can inspect progress every quarter instead of waiting 18 months for a high-drama launch.
Strangler fig is the right answer when the system contains both valuable business logic and genuine technical constraints. It accepts that some legacy code should die, but refuses to treat the whole application as disposable. If the replacement needs to be built, use a focused team for building the replacement capability by capability, not as an isolated parallel universe.
What should you do before deciding to rewrite or refactor?
Before deciding, run an independent assessment of the codebase, dependencies, business rules, tests, delivery process, infrastructure, and financial cutover model. A rewrite vs refactor decision is unmakeable without evidence. The audit should produce options, risks, sequencing, and cost ranges — not a predetermined verdict.
A credible assessment starts by mapping the system as it is, not as people remember it. Review repositories, deployment pipelines, database schemas, scheduled jobs, integrations, logs, incident history, support tickets, and release cadence. Interview engineers, product owners, finance users, operations, customer support, and anyone who knows the exceptions.
Then classify the findings:
- Knowledge risk: few people understand key modules.
- Verification risk: tests do not protect critical behaviour.
- Platform risk: dependencies or infrastructure are unsupported.
- Data risk: the model cannot represent current business reality.
- Delivery risk: releases are slow, manual, or fragile.
- Commercial risk: revenue, contracts, or compliance depend on the system.
The output should not be a 90-page PDF nobody reads. It should be a decision pack a CTO can forward to a CEO or CFO: current-state evidence, recommended path, rejected alternatives, migration sequence, budget assumptions, staffing model, and the cost of doing nothing.
The assessment should also separate “rewrite” from “modernise”. Modernisation may mean better tests, modularisation, dependency upgrades, DevOps automation, cloud migration, observability, or a strangler fig programme. A full rewrite is only one tool inside that wider set.
According to McKinsey and the University of Oxford (2012), successful large IT initiatives require alignment between technology content, business value, stakeholders, teams, and project controls. (mckinsey.com) That is exactly why the audit must inspect delivery process as well as code. A company that rewrites with the same unclear ownership, weak QA, and overloaded product governance will reproduce the same failure patterns.
The board-level question is simple: what evidence would change your mind? If no evidence would stop the rewrite, the decision has already become ideological. If evidence can change the path, you can choose rationally among refactoring, strangler fig, and full legacy system rewrite.
For most mid-market companies, the right next step is not permission to rebuild. It is a short, independent software audit that turns frustration into facts. Once you know what is actually broken, you can spend money on the constraint rather than the story around it.
Before you approve a rewrite budget, get an independent, evidence-led audit of the system you are arguing about. We map the code, dependencies, business rules, delivery process and cutover cost, then give you a decision pack you can put in front of a CEO or CFO.
Talk to our engineering team →
Sources
- Joel Spolsky, “Things You Should Never Do, Part I”, Joel on Software, 2000 — joelonsoftware.com
- Martin Fowler, “Original Strangler Fig Application”, 2004 — martinfowler.com
- Martin Fowler, “Strangler Fig”, 2024 — martinfowler.com
- Martin Fowler, “Definition Of Refactoring” — martinfowler.com
- McKinsey & Company and the University of Oxford, “Delivering large-scale IT projects on time, on budget, and on value”, 2012 — mckinsey.com
- Bent Flyvbjerg, Alexander Budzier, Jong Seok Lee, Mark Keil, Daniel Lunn, Dirk W. Bester, “The Empirical Reality of IT Project Cost Overruns: Discovering A Power-Law Distribution”, 2022 — arxiv.org

