Rewrite vs refactor: how to decide
In the rewrite vs refactor decision, refactoring wins most of the time. Improving a working system in small steps keeps revenue flowing, preserves the behavior customers depend on, and delivers value every week. A full rewrite only makes sense in a few specific conditions, and even then it should be done incrementally rather than as one big-bang project.
“Should I rewrite my app?” is one of the most common questions founders and engineering leads ask, and both answers can go badly. This page is the framework I use to decide.
Why big-bang software rewrites fail
Joel Spolsky called rewriting from scratch “the single worst strategic mistake that any software company can make” in 2000, using Netscape as the example. The specifics have aged; the mechanics haven’t. Rewrites fail for predictable reasons:
- The old code holds knowledge nobody wrote down. That weird conditional in the invoicing code exists because of a tax rule, a customer contract or a bug in a partner’s API. The rewrite drops it, and you rediscover why it was there in production.
- The target keeps moving. While the new system is being built, the old one still needs features and fixes. Either you build everything twice, or you freeze the product and lose ground to competitors.
- No value until the end. A rewrite delivers nothing to customers until cutover. When it runs late – and it will – there’s nothing to show for the months spent.
- Second-system effect. Fred Brooks described it in The Mythical Man-Month: the second system gets every feature and abstraction the team wished they’d had the first time, and it bloats.
- Cutover is a cliff. Data migration, URL compatibility, integrations and customer workflows all change on one day. Rolling back is often impossible.
- The same team, the same habits. If the original code decayed because of rushed deadlines and no review, the rewrite will decay the same way unless something else changes.
What refactoring can fix
More than most teams think. With characterization tests, branch by abstraction and expand/contract schema changes, you can replace a framework, restructure a domain model, extract modules, swap a database, and change the tenancy model, all while shipping every week. The strangler fig pattern extends this to replacing whole subsystems.
The honest framing is that “refactor” and “rewrite” are the two ends of a spectrum. An incremental migration that eventually replaces every line of code is a rewrite in outcome, but a refactor in risk profile. That’s usually what you want.
When a rewrite is actually right
There are real cases for a software rewrite. In my experience they look like this:
- The platform is a dead end. The language, framework or runtime is unsupported, there’s no upgrade path, and hiring for it has become impractical. Even then, migrate piece by piece where you can.
- The system is small. A component a couple of engineers can rebuild in a few weeks, with clear inputs and outputs, is cheaper to rewrite than to untangle.
- The product has changed fundamentally. You pivoted, and the existing code models a business you’re no longer in. Most of it would be deleted by a refactor anyway.
- It’s a prototype that never had a design. A vibe-coded or hackathon MVP with no tests, no migrations and no structure can sometimes be faster to rebuild once you know what the product is. See vibe-coded app rescue – though even there, more is usually salvageable than people expect.
- Nobody understands it and it’s small enough to specify. If you can write down everything it does, you can rebuild it. If you can’t, a rewrite will miss things.
Notice what isn’t on the list: “the code is messy”, “it’s in an unfashionable language”, and “the new CTO prefers a different stack”. None of those justify the risk.
A decision checklist
Answer these honestly before committing to an app rewrite. The more “yes” answers, the stronger the case for refactoring:
- Is the system earning revenue today?
- Do customers rely on behavior that isn’t fully documented or tested?
- Will the business need new features on the existing product during the next six to twelve months?
- Is the language and framework still supported, with a realistic upgrade path?
- Is there a natural interception point – an HTTP layer, an API, a queue – where you could route traffic between old and new?
- Is the data model broadly right, even if the code around it is poor?
- Would a rewrite take more than one quarter?
- Is the team that wrote the original still doing most of the work, with the same process?
If most answers are yes, refactor, and use strangling for the bigger structural pieces. If most are no – small system, dead platform, no revenue dependency, clear spec – a rewrite is defensible. Either way, the last question is about process: fix how the team works before you fix the code, or you’ll be having this conversation again in three years.
If you do rewrite, do it this way
- Never freeze the old system completely. Ship urgent fixes; keep customers whole.
- Cut over incrementally. Route a slice of traffic or a cohort of customers to the new system first. Internal accounts, then new signups, then existing customers.
- Replay production traffic. Send real requests to both systems and diff the responses. It’s the fastest way to find the undocumented behavior.
- Migrate data continuously, not once. Run the migration repeatedly against production copies until it’s boring.
- Time-box it. Set a budget and a date. If you’re halfway through the budget and less than halfway through the scope, stop and reassess.
- Delete the old system. Running both indefinitely is the most expensive outcome of all.
What I’d do first
Before either decision, spend two weeks finding out where the pain actually is. In most codebases, a handful of hotspot files account for most of the incidents and slowdowns, and fixing those changes the rewrite conversation entirely. The technical debt reduction playbook covers how to find them.
If you want an outside view from someone who doesn’t have a stake in either answer, that’s a good use of a code audit or the first sprint of a legacy modernization engagement. I’ll tell you if a rewrite is the right call. It occasionally is.
Questions
Should I rewrite my app from scratch?
Probably not, if it’s earning revenue, customers depend on its behavior and you need to keep shipping features. Refactor incrementally instead. A rewrite is defensible when the system is small, the platform is a dead end, the product has fundamentally changed, or the code is a prototype with no design.
Why do software rewrites fail?
They lose undocumented behavior encoded in the old code, deliver no value until cutover, chase a moving target as the old product keeps changing, and end in a risky one-day migration. They also tend to bloat with every feature the team wanted the first time, which Fred Brooks called the second-system effect.
How long does an app rewrite take?
Longer than estimated, nearly always. A useful rule: if a rewrite looks like more than one quarter of work, it’s too big to do in one go and should be broken into incremental migrations that ship to production along the way.
Is refactoring cheaper than rewriting?
Usually, once risk is included. Refactoring keeps the product shipping and spreads cost over time, while a rewrite front-loads cost and risk with no return until cutover. For small, well-specified components, a rewrite can be cheaper – the size of the system is the deciding factor.
Stuck in a rewrite debate?
I’ll spend a two-week sprint in the codebase, find where the pain really is, and give you a written recommendation – plus a first fix shipped either way.
GMT+2 · Full EU overlap · US East mornings