Articles

Refactor vs Rewrite: How to Choose Without Torching Your Roadmap.

10 min read

Split decision graphic weighing a full software rewrite against an incremental refactor of a legacy codebase

A client came to us last year framing their problem as a software rewrite vs refactor decision, certain of one thing: their ten-year-old order-management system had to be thrown out and rebuilt from scratch. It was slow, nobody on the current team had written the original PHP, and every new feature took a month. They had a budget, a target date, and a slide that said "greenfield." What they did not have was a reason the architecture was broken — only proof that the code was hard to read. We talked them out of the rewrite. Eighteen months later they were shipping features in days, on the same core system, for roughly a third of what the rebuild would have cost.

The choice between a software rewrite vs refactor is the choice between rebuilding a system from scratch and improving its internal structure without changing what it does. It is one of the most expensive decisions an engineering leader makes, and it is almost always made emotionally — because reading someone else's code feels worse than writing your own. Getting it right is squarely within CodeStringers capabilities, and it starts with refusing to treat "this code is ugly" as a synonym for "this system is finished."

The "clean rewrite" is usually the sunk-cost fallacy wearing a lab coat: you throw away a decade of paid-for bug fixes because the code is hard to read, then spend two years rediscovering every one of them.

Software rewrite vs refactor at a glance

A refactor keeps the running system and improves it from the inside; a rewrite replaces the system with a new one. The trade is risk for freedom. Refactoring is low-risk and incremental but constrained by the existing design. A rewrite unlocks a new stack and clean architecture — and carries the highest failure rate in software. The table below is the version we sketch on a whiteboard in the first meeting.

Refactor

Full rewrite

Risk

Low — behavior is preserved, changes are small and reversible

High — the new system must re-earn every behavior the old one already had

Timeline

Continuous; value ships weekly

Long; often zero user-visible value until the "big switch"

Cost

Predictable, funded from normal delivery

Large, front-loaded, and routinely underestimated

When it's right

The architecture is sound; the pain is code quality, tests, and debt

The platform is a genuine dead end, or the design can no longer support the business

Failure mode

Endless polishing with no real structural change

The old system keeps evolving while the new one never catches up

What refactoring actually is

Refactoring is a disciplined technique for restructuring code without changing its observable behavior. Martin Fowler's definition is worth quoting exactly: refactoring is "a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior" (refactoring.com). That last clause is the whole discipline. If a change alters what users see, it is not a refactor — it is a rewrite of one part, and it needs to be treated with a rewrite's caution.

The reason refactoring is safe is the reason a rewrite is dangerous. Every refactor is small, verifiable, and backed by the tests you already have. You are not gambling the roadmap; you are paying down debt one payment at a time while the system stays live. Our order-management client thought they needed a new system. What they actually needed was a test harness around the payment module, a few extracted services, and someone willing to rename the variables. The architecture — a straightforward transactional monolith — was fine.

Refactoring is also where most teams should be spending their modernization energy, because most legacy pain is debt, not design. McKinsey's research on technical debt found it accounts for roughly 40 percent of the value of companies' entire technology estates, and that CIOs see 10 to 20 percent of the budget meant for new products quietly diverted to servicing that debt (McKinsey). Debt at that scale is a refactoring problem. You do not need a new house because the gutters are clogged.

When a rewrite is the right call

A rewrite earns its place when the problem is structural, not cosmetic — when the platform is a dead end, the team genuinely cannot change the system safely, or the business has diverged so far from the original design that no amount of internal restructuring can close the gap. Those are real conditions, and we have recommended rewrites when they held.

Concretely, we reach for a rewrite when: the runtime or framework is unsupported and unpatchable (a security clock you cannot stop); the system is written in a language nobody will hire for; the data model encodes assumptions the business abandoned years ago; or every change, no matter how small, risks a production outage because there is no safe way to test. Notice the pattern — these are all cases where incremental improvement is blocked at the foundation. If you can safely make the system better a little at a time, you almost never need to replace it. If you genuinely cannot, that inability is the signal.

The cost of getting this wrong runs in both directions, which is why the decision deserves data rather than instinct. The Standish Group's CHAOS research, drawn from a database of over 50,000 projects, put software project success at just 31 percent, with 50 percent "challenged" — late, over budget, or short on scope — and 19 percent failing outright (Henny Portman's CHAOS 2020 review). A ground-up rewrite is the largest, riskiest kind of project there is. You are not beating those odds by wanting it badly.

Why most rewrites fail

Most rewrites fail because they throw away years of embedded knowledge and then spend years rediscovering it. This is the argument Joel Spolsky made in 2000 in Things You Should Never Do, Part I, and two decades of our own project scars have not dented it. He called rewriting code from scratch "the single worst strategic mistake that any software company can make," using Netscape's multi-year rewrite — a gap that let a competitor eat its market — as the cautionary tale (joelonsoftware.com).

His core insight is deceptively simple: it is harder to read code than to write it. That is why the existing codebase always looks like a "big hairy mess" to the engineer inheriting it — the mess is in the difficulty of reading, not in the software. And that old, ugly code carries value you cannot see. As Spolsky put it, "It has been used. It has been tested. Lots of bugs have been found, and they've been fixed." Each of those fixes represents a real production incident somebody debugged at 2 a.m. Delete the code and you delete the fixes, then meet every one of those bugs again on the new stack.

The second failure mode is quieter and, in our experience, more common: the old system does not politely wait. While the rewrite team works toward a distant switch date, the business keeps demanding features, and those features land in the old codebase because that is what is in production. Now two systems are diverging. The rewrite is aiming at a target that keeps moving, the "big switch" slips a quarter, then two, and the appetite to finish drains away. Those are exactly the conditions behind the 50-percent-challenged number above. A rewrite is not one big project; it is a race against a system that is still running.

The strangler-fig middle path

The pragmatic answer, most of the time, is neither a pure refactor nor a big-bang rewrite — it is the strangler fig, incrementally replacing the old system piece by piece while it stays live. Martin Fowler named the pattern after the vine that grows around a tree and gradually takes its place. In software it means "a gradual process of modernization... it begins with small additions, often new features, that are built on top of, yet separate to the legacy code base. As we do this we move bits of behavior from the legacy system into the new code base" (martinfowler.com). Investment and returns arrive gradually and visibly, instead of all at the end.

This is how we handled a different client's aging monolith — a logistics platform where a full rewrite would have parked the roadmap for a year. Instead we stood up a thin routing facade in front of the monolith and moved one module at a time behind it. The rate-quoting engine went first: we built it fresh as a separate service, pointed the facade at it for quote requests, and left every other request flowing to the old code untouched. When quotes were stable in production, we took the next module. Each step was independently shippable, independently reversible, and delivered real value the week it went out. There was never a scary switch date, because there was never a switch — the new system grew and the old one shrank until, one quiet sprint, the monolith was gone.

The strangler fig is not free. It requires clean seams, a facade you trust, and the discipline to actually retire the strangled code instead of leaving it running "just in case." But it converts the rewrite's single catastrophic bet into a series of small, survivable ones — which is exactly the trade you want when the odds on big projects are what CHAOS says they are. Our full playbook lives in our guide to legacy software modernization.

How to decide: a short checklist

Decide by testing the architecture, not the code's readability. Run through these before anyone builds a greenfield slide. If you answer "yes" to the first block, refactor. If the second block is your reality, a rewrite — ideally an incremental one — is on the table.

Lean refactor if:

  • The core architecture would be a reasonable choice if you were starting today.

  • You can add automated tests around the risky parts without heroics.

  • The stack is supported, patched, and hireable.

  • The pain is "changes are slow and scary," not "changes are impossible."

Consider a rewrite if:

  • The platform, language, or framework is unsupported and cannot be patched.

  • No one can change the system safely, and no amount of testing fixes that.

  • The data model or domain design contradicts how the business now operates.

  • You have honestly tried to refactor and hit a foundational wall — not a wall of ugliness.

One more rule we hold to: even when a rewrite is justified, default to strangling the old system module by module rather than replacing it all at once. The question is rarely rewrite or not — it is how incrementally can we do this. That framing sits alongside the same discipline we bring to build-versus-buy decisions, where the expensive mistake is also usually the all-or-nothing one.

Get a second opinion before you commit the budget

If your team is convinced it needs a rewrite, that conviction is worth pressure-testing before the budget is spent — because the cost of being wrong is measured in quarters, not weeks. We do this for a living as a Custom software developer, and the first thing we look at is whether the architecture is sound or the code is simply hard to read. Where a system genuinely needs to keep running while it evolves, our Managed technical operations team runs the strangle-and-ship work without stalling your roadmap. Book a free modernization consultation and we will tell you honestly which side of this line you are on — including the times the answer really is "rebuild it."

The short version

The software rewrite vs refactor decision is not about which codebase looks nicer; it is about whether the architecture is sound. When the design holds and the pain is debt, refactor — it is lower risk, ships continuously, and preserves years of paid-for bug fixes that a rewrite throws away. When the platform is a genuine dead end, a rewrite is defensible, but the data on project failure and Spolsky's Netscape warning both argue against the big bang. The strangler fig splits the difference: replace the old system module by module, live, until it is gone. Most of the time, that is the answer. When you are staring at a greenfield slide and not sure it is yours, book a free modernization consultation — a second opinion is far cheaper than a two-year detour.

By the CodeStringers Team — Zoho Experts & Custom Software. CodeStringers is a custom software engineering firm with a dedicated Zoho practice, writing from work we've actually shipped for clients.


Related reading

Find out what a connected system would change in your business.

In a no-risk discovery we look at how your work moves between your systems today and show what one connected system would do differently. You pay only if you proceed. Or see how we approach it.

Book the no-risk discovery

More on the same problem:

Frequently asked questions.

How long does a strangler fig migration take?

It depends on how many modules the system has and how cleanly they separate, but a mid-sized business application typically takes one to three years to replace fully, with value shipping from the first month. The pace is set by the team's normal delivery capacity, because the new modules go out with ordinary releases. There is no cutover date to miss; the old system shrinks until it can be switched off.

What is the difference between a rewrite and a replatform?

A replatform moves the existing application to a new runtime, hosting or database with minimal code change, such as moving a server application into containers or onto a managed database. A rewrite replaces the code itself. Replatforming is cheaper and lower risk and often removes the unsupported-platform problem that was driving the rewrite conversation, leaving the code to be refactored at leisure.

How do you estimate the cost of a software rewrite?

Count every behavior the current system has, including the undocumented ones users rely on, and price rebuilding and testing each. Then add the cost of running two systems in parallel, migrating data, and the features the business will demand during the rewrite. Compare that against three years of refactoring at the current team's pace. Most honest estimates land at two to three times the first guess.

What tests do you need before refactoring legacy code?

Characterization tests that capture what the system does now, correct or not, at the boundaries you intend to change: API responses, database writes, generated documents. They do not need to cover everything, only the module you are about to touch. Record real inputs and outputs from production where you can. With those in place, each refactor is verifiable and reversible, which is what makes the approach safe.

Should you rewrite software in a new programming language?

Only when the current language is a hiring or support dead end, which is rare for mainstream languages. Nobody here knows it is a staffing problem with a cheaper fix than a rewrite. If a change of language is warranted, do it module by module behind a facade, so each piece ships and the two languages coexist during the transition rather than meeting at a single risky switch.

Where we're not the right answer

We'll tell you if we're a fit. If we're not, we'll tell you that too.

  • Your current stack works and nobody wants to change it
  • You want licences resold at a discount and nothing else
  • Your internal team owns the operating model and is not handing it over
  • You want hours of configuration work and nothing run for you: that is on our services pages, and it is not a managed solution
How we start

Most of our best clients come to us with a feeling, not a plan.

"Something isn't working." "We're outgrowing our tools." "We're afraid to make the wrong move." No-Risk Discovery is a short, practical conversation that gets you clarity before you commit to anything big. We'll tell you if we're a fit. If we're not, we'll tell you that too.

Talk to us

Have a question about this?

Say who you are and ask. A person replies within one business day.

The rest of the form follows your answer.

A new client, or thinking about becoming one

Never shared. Never sold.

What are you looking for?An agentic solution (AI OS) is licensed and sold with managed services. An integrated solution is built for a professional services fee. A Service is professional work, done by the hour, the month or the project. Not sure yet is a fine answer.

Tick every one that applies, or not sure yet.

Agentic solutions (AI OS)

Integrated solutions

Services

Platforms you run today

Pick every one that applies. Choosing one is an answer too.