Refactor or Rewrite the Legacy Codebase, and How to Actually Decide
The decision that eats months of engineering time when it's made wrong — a working framework and the failure modes on each side.
Every engineering team eventually stares at a legacy codebase and asks the same question: do we refactor this, or do we rewrite it. The wrong answer costs six months of runway. The right one is often neither of the two extremes.
This post is the working framework we use when a founder or CTO calls us about a system that's slowing down every new feature. It's not a manifesto for either camp. It's what actually holds up.
The instinct to rewrite, and why it's almost always wrong
Every engineer who joins an existing codebase wants to rewrite it. The old code is unfamiliar, the tests are missing, the abstractions look weird. A greenfield rewrite feels faster because you're not carrying anyone else's decisions.
The problem is that the old code contains thousands of undocumented decisions about edge cases the original team fixed. A rewrite forgets all of them, and you rediscover them in production over the next twelve months. Joel Spolsky wrote about this in 2000 and it's still the most important essay in software.
Rewrites are the right call in specific situations. In most, they aren't.
When refactoring wins
Refactoring is the right move when the architecture is fundamentally sound but the code is messy. That's most legacy systems, even the ones that feel unbearable.
Signals to refactor
- The product works and customers use it.
- The core data model still fits the domain.
- Most bugs are in specific files, not spread across the whole codebase.
- Adding a small feature takes days, not weeks.
- The team can articulate what "better" looks like for each module.
If you can name three specific files that cause 80% of the pain, refactor. You know where the problem is. You just haven't scheduled it.
When a rewrite is actually justified
Sometimes the code isn't the problem — the architecture is. In those cases, refactoring is putting a new engine in a car with a broken chassis.
Signals to rewrite
- The framework or platform is end-of-life and unsupported.
- The data model no longer fits the product's core operations.
- Every new feature requires touching a dozen files across unrelated modules.
- Nobody on the team can explain how a specific part works.
- Security or compliance requirements can't be met in the current architecture.
The last two are the strongest signals. If knowledge is lost, no amount of refactoring recovers it — you're editing code you don't understand. If compliance is impossible, refactor is a delay, not a solution.
The middle path most teams miss
Between full refactor and full rewrite lies a strategy that works for most teams: the strangler fig.
You build the new system alongside the old one, route new features through it, and gradually pull functionality from old to new. The old system keeps serving live traffic. Users notice nothing. Engineering makes measurable progress every sprint.
This is how big platforms have replaced monoliths for two decades. It works for a five-person startup too. The tooling is easier now — reverse proxies, feature flags, and edge routing let you move traffic in slices without a maintenance window.
What each option really costs
The dollar cost is the smallest part. The real costs are time and risk.
Refactor
- Time: 20-40% of engineering time over 3-6 months, alongside feature work.
- Risk: low, if tests exist. Regressions are localized.
- Business cost: features slow down during the effort.
Rewrite
- Time: 6-18 months, typically double the original estimate.
- Risk: high. Feature parity is harder to reach than teams predict.
- Business cost: no new features during the rewrite, plus a launch risk when you switch over.
Strangler fig
- Time: 6-12 months, in parallel with feature work.
- Risk: moderate. Complexity of running two systems at once.
- Business cost: some feature slowdown, but no cliff.
Rewrites go over budget more predictably than any other engineering decision. If a team says the rewrite will take three months, expect nine.
The tests conversation
The biggest hidden variable in this decision is test coverage. If the legacy system has decent tests, refactoring is safe and often quick. If it has almost none, both refactor and rewrite become high-risk.
Before deciding, spend a week writing characterization tests — tests that document the current behavior, warts and all. These aren't good tests. They're insurance. They let you change code without breaking things users depend on.
Teams that skip this step end up rediscovering their own product's behavior via bug reports.
Common misconceptions
"AI coding tools make rewrites cheap now." They make writing new code cheaper. They don't make understanding what to build cheaper, and that's still the hard part. Our post on AI coding assistants covers where they help and where they don't.
"We'll rewrite in a modern framework and everything will be faster." Frameworks don't cause slowness. Architecture does. A slow app in Rails becomes a slow app in Next.js if the queries and abstractions are the same.
"We can do it in parallel with normal feature work." You can, but at half speed for both. Founders who don't accept this end up with neither a good refactor nor good new features.
The decision framework we use with clients
Six questions, in order. If you answer "no" to any of the first three, refactor. If you answer "yes" to three or more of the last three, consider rewriting.
- Is the product still generating revenue?
- Do the core abstractions still match the domain?
- Can new engineers ramp up in a reasonable time?
- Is the underlying platform end-of-life?
- Do compliance or security needs require an architecture the code can't support?
- Has the original team completely left, taking all context with them?
Most legacy codebases fail question 3 but pass 1 and 2. That's a refactor project, not a rewrite. Our rebuilding legacy admin panels post covers a common variant of this.
What to do first, either way
Regardless of which path you choose, three things should happen in the first two weeks.
Write characterization tests for the critical paths. Not comprehensive tests — critical ones. Payment flow, auth, data integrity. This is your safety net.
Document what nobody knows. Interview the longest-tenured engineer, take notes, and put them in a wiki. If they leave next month, you still have the knowledge.
Set a measurable goal. "Reduce feature ship time from two weeks to three days" is measurable. "Make the code cleaner" is not.
What we tell founders
If the product works, the team knows it, and the pain is localized: refactor. If the product works but the architecture is dead: strangle. If the product barely works and nobody knows how: characterize first, then decide.
Full rewrites are almost always the wrong answer. They're right just often enough to be tempting.
Frequently asked questions
A final note
The decision is rarely urgent, even when it feels urgent. Take two weeks to write tests, document what's known, and set a measurable goal. Then decide. Rushing this call is how six-month projects turn into two-year projects.
If you're staring at a legacy codebase and want a second set of eyes before you commit engineering time, book a call. We'll walk through the code with you and tell you what we'd do.
FAQ
Frequently asked questions
When should I refactor versus rewrite a legacy codebase?+
Refactor when the product works, the architecture is sound, and pain is localized to specific files. Rewrite only when the platform is end-of-life, compliance can't be met, or the original team's knowledge is completely gone. Most legacy systems that feel unbearable are refactor candidates, not rewrite ones.
How long does a full rewrite usually take?+
Roughly double the initial estimate. A team predicting three months typically delivers in nine. Rewrites go over more predictably than any other engineering decision because they underestimate the number of edge cases baked into the old code.
What is the strangler fig pattern?+
Building a new system alongside the old one and gradually routing traffic and features to the new one. The old system keeps serving until every part has a replacement. It gives you continuous progress and avoids a big-bang switchover, which is usually where rewrites die.
Do modern AI coding tools make rewrites more viable?+
They make writing new code faster, but the hard part of a rewrite is understanding the old system's edge cases and reproducing them. AI tools don't help much with that. They can speed refactoring inside the existing system more than they speed a full rewrite.
What should I do before deciding to refactor or rewrite?+
Write characterization tests for critical paths, document what only long-tenured engineers know, and set one measurable goal. Two weeks of that work will tell you more about the right decision than any code review or architecture diagram will.
Building something similar?
Let's talk in 30 minutes.

