Your legacy system is bleeding money. The question isn’t whether to act – it’s whether you rewrite from scratch or refactor what you have. Most teams frame this as binary. It’s not. It’s a spectrum, and picking the wrong point on that spectrum costs you months, budget, and sometimes the project itself.
We’ve delivered both rewrites and refactors. We’ve also delivered the thing in between – the incremental replacement that borrows from both approaches. Here’s how we decide, and how you should too.
The spectrum nobody talks about
The industry loves a clean dichotomy. Rewrite = start over. Refactor = improve in place. In practice, every modernization project lands somewhere on a continuum:
- Pure refactor – restructure internals, preserve interfaces. Zero downtime risk. Slow compounding gains.
- Strangler Fig – replace components one at a time behind the existing system’s facade. The old system shrinks as the new one grows. Middle of the spectrum.
- Full rewrite – greenfield build, then cut over. Maximum design freedom. Maximum risk.
Most successful modernizations don’t sit at the extremes. They start somewhere on this spectrum and slide toward one end as the team learns more about the codebase, the constraints, and the business pressure.
When to rewrite
A rewrite is the right call when the existing system’s architecture actively prevents the outcome you need. Not when it’s ugly. Not when the previous team made choices you disagree with. When the architecture cannot support the target state.
Signals that point toward rewrite:
- The data model is wrong. If the core entities are misaligned with the business domain, every feature you build on top is a workaround. Refactoring a broken data model while keeping the system running is surgery on a moving patient – possible, but the risk profile is brutal.
- The tech stack is end-of-life. Framework unmaintained, language version unsupported, hosting platform sunsetting. Refactoring within a dying ecosystem is investing in a depreciating asset.
- The system has no tests and no contracts. If you can’t verify behavior before and after a change, refactoring is guesswork. At least a rewrite lets you build the test harness alongside the new code.
- The team that built it is gone. No documentation, no institutional knowledge, no one who can explain why that one service restarts itself every 4 hours. The cost of understanding the existing system exceeds the cost of replacing it.
When to refactor
Refactoring is the right call when the system’s core is sound but its implementation is holding you back. The architecture works; the code quality, performance, or maintainability doesn’t.
Signals that point toward refactor:
- The data model is correct. Entities match the business domain. The problem is how they’re accessed, transformed, or presented – not what they represent.
- The system is in production and revenue-critical. A rewrite means running two systems in parallel or accepting a cutover window. If the business can’t tolerate either, refactor lets you improve without the big-bang risk.
- You have test coverage. Tests are the safety net that makes refactoring viable. If you can verify behavior after each change, you can move fast without breaking production.
- The bottleneck is localized. If 80% of your pain comes from one module, one service, or one integration – replace that piece, not the whole system.
The Strangler Fig: the middle path
Named after the tropical fig that grows around a host tree until the host disappears, the Strangler Fig pattern lets you replace a legacy system incrementally. You build new functionality alongside the old system, routing traffic to the new components as they’re ready. The old system shrinks over time until it can be decommissioned.
This is not a compromise. It’s a strategy. You get the design freedom of a rewrite for each component you replace, with the risk profile of a refactor because the old system stays live until you’re confident in the replacement.
The Strangler Fig works best when:
- The system has clear boundaries between components (APIs, message queues, well-defined modules).
- You can route requests to either the old or new implementation (feature flags, proxy layer, load balancer rules).
- The team needs to keep shipping features while modernizing – which is almost always the case.
Case study: Dango’s reporting system
Dango’s analysts were spending 4 days per week on manual reporting. Three legacy systems, no shared schema, no automation. The reports were correct, painstakingly so, but the process was unsustainable.
A pure refactor wouldn’t work. The three source systems used incompatible data models, and the “pipeline” was a collection of Excel macros and manual copy-paste workflows. There was nothing to refactor – the architecture was human-in-the-loop at every step.
A full rewrite of all three source systems was out of scope. Dango didn’t own two of them, and the third was too deeply embedded in other business processes to replace.
We used the Strangler Fig approach. We built an automated ingestion layer that sat between the legacy systems and the reporting output. Each source system got an adapter that normalized its data into a canonical schema. The legacy systems stayed untouched. The manual process stayed available as a fallback. But the new pipeline handled the heavy lifting.
The result:
- 75% reduction in reporting time – from 4 days to 1 day per reporting cycle.
- 120 hours saved per month across the analyst team.
- Zero production incidents – the legacy systems kept running undisturbed.
- 12 weeks from kickoff to production.
The key insight: we didn’t need to replace the legacy systems. We needed to decouple the reporting workflow from them. The Strangler Fig let us do that without touching systems we didn’t control.
Decision framework: cost and risk comparison
Here’s how the three approaches compare across the dimensions that actually matter when you’re signing off on a modernization budget:
| Dimension | Refactor | Strangler Fig | Rewrite |
|---|---|---|---|
| Upfront cost | Low | Medium | High |
| Ongoing cost | Medium (tech debt compounds) | Low (debt retired incrementally) | Low (clean slate) |
| Time to first value | Days to weeks | Weeks to months | Months to quarters |
| Production risk | Low (changes are incremental) | Low (old system stays live) | High (big-bang cutover) |
| Design freedom | Constrained by existing architecture | Free per component | Fully unconstrained |
| Team velocity during project | Normal (refactoring alongside features) | Slightly reduced (two systems) | Severely reduced (parallel tracks) |
| Rollback difficulty | Easy (revert individual changes) | Easy (route back to old system) | Hard (old system may be decommissioned) |
| Best for | Sound architecture, localized problems | Legacy integrations, incremental migration | Broken data model, dead tech stack |
The questions that actually decide it
Forget the theory. When you’re staring at a legacy system and a deadline, ask these five questions:
- Is the data model fundamentally wrong? If yes, lean toward rewrite. A broken data model poisons everything built on top of it.
- Can you keep the system running during the transition? If the business demands zero downtime, the Strangler Fig or incremental refactor are your only options.
- Do you own all the systems involved? If not, like Dango, you can’t rewrite what you don’t control. Work around the boundaries you have.
- How much test coverage exists? Tests make refactoring safe. No tests make rewriting safer than refactoring blind.
- What’s the timeline? A 12-week window (like Dango) rules out a full rewrite of complex systems. It’s enough for a targeted Strangler Fig or aggressive refactor.
The answer is almost never “obviously rewrite” or “obviously refactor.” It’s a weighted decision based on your constraints. The teams that get it right are the ones that assess honestly instead of defaulting to the approach they’re most comfortable with.
Common mistakes in the rewrite-vs-refactor decision
After years of modernization projects, we see the same mistakes repeat:
Rewriting because the code is “ugly.” Ugly code that works is not a business case for a rewrite. Rewrites are justified by architectural constraints, not aesthetic preferences. If the system does what it needs to do and can be extended to do what it will need to do, refactor the ugly parts and move on.
Refactoring without acceptance tests. Every refactor session should start with a characterization test: capture what the system does now, then verify it still does that after your changes. Without this, you’re not refactoring – you’re rewriting in place, with all the risk and none of the design freedom.
Underestimating the “second system effect.” Fred Brooks warned about this in 1975 and it’s still true. The team building the replacement tends to over-engineer it, adding features the old system never had. Scope the rewrite to parity first. Enhancements come after cutover.
Treating the Strangler Fig as “just a migration.” The Strangler Fig pattern requires active architectural investment – routing layers, adapter interfaces, feature flags, parallel testing infrastructure. If you treat it as a quick migration project, you end up with two half-systems and no clean path to decommission either one.
Not setting a decommission date. Whatever approach you choose, define when the old system dies. Without a deadline, legacy systems have a way of surviving indefinitely, and you end up paying maintenance costs on both the old and new systems.
The real risk: doing nothing
Every month you delay, the legacy system gets harder to change. Technical debt compounds. The team that understood the original design gets smaller. The gap between what the system does and what the business needs grows wider.
Dango’s analysts were losing 120 hours per month to manual work before they acted. That’s not a technology problem – it’s a business problem with a technology solution. The longer you wait, the more expensive every option becomes.
Whether you need a surgical refactor, a full rebuild, or a Strangler Fig migration – the first step is an honest assessment of where your system sits on the spectrum. We’ve been doing this for over a decade.
