Ometa Systems logoOmetaSystems

When to rebuild vs. refactor a legacy platform

A decision framework for legacy modernization — how to tell whether to refactor in place, strangle incrementally, or rebuild, and how to migrate without freezing the business.

Published
Reading time
6 min read
Author
Ometa Systems

The big rewrite is the most seductive and most dangerous decision in software.

Every engineer who has inherited a painful codebase has felt the pull: throw it all away, start clean, do it right this time. And almost every team that gives in discovers the same thing eighteen months later — the rewrite is late, the old system is still running, the new one doesn't have feature parity, and the business has been frozen the entire time.

The question is rarely "should we modernize?" The answer to that is usually yes. The real question is how: refactor in place, strangle incrementally, or rebuild from scratch. This article is the decision framework we use, and the migration patterns that make the chosen path survivable.

First, separate the pain from the cause

Teams often want to rebuild because the system feels bad to work in. But "feels bad" has many causes, and only some of them are fixed by a rewrite:

  • Code quality — tangled modules, no tests, copy-paste everywhere. This is refactorable. A rewrite is not required.
  • Architecture — the wrong boundaries, a monolith that can't scale a hot path, a data model that fights every feature. Sometimes refactorable, sometimes not.
  • Technology — a framework that's end-of-life, a language nobody will hire for, a database that can't do what you now need. This may force a platform change but rarely a full rebuild.
  • Knowledge — the people who understood it are gone, and nobody dares touch it. This is an organizational problem that a rewrite makes worse, not better, because you discard the one artifact that still encodes the original requirements: the running code.

Write down which of these you actually have. A surprising amount of "we need to rebuild" turns out to be "we need tests and three weeks of focused refactoring."

The three options, honestly

Refactor in place keeps the system running and improves it incrementally — add tests, untangle modules, fix the data model with migrations, upgrade dependencies. Lowest risk, fastest payoff, but it can't cross certain chasms (a fundamentally wrong architecture, a dead platform).

Strangler-fig migration builds the new system around the old one. New functionality and migrated slices run in the new stack; the legacy system keeps serving everything not yet moved. Traffic is routed piece by piece until the old system handles nothing and is switched off. Moderate risk, value every sprint, but requires discipline and a routing seam.

Full rebuild starts a parallel system and cuts over when it reaches parity. Highest risk by a wide margin, longest time to value, and the only option that asks the business to wait. Occasionally unavoidable — but it should be the conclusion of an argument, not its premise.

The decision framework

We run a candidate system through a short set of questions. The answers point clearly most of the time.

1. Is the data model salvageable? The data outlives the code. If the schema is sound, refactoring or strangling is viable — you can change the code around stable data. If the data model itself is the problem, you're facing a data migration regardless of which code path you choose, and that raises the cost of every option.

2. Can you carve seams? Strangler-fig needs a place to intercept and route — an API gateway, a proxy, a facade. If the system has identifiable boundaries (or you can introduce them), incremental migration works. If it's a true big ball of mud with no seams and no way to add them, your options narrow.

3. Is the platform alive? A framework or runtime that's actively maintained buys you time to refactor or strangle. One that's end-of-life or a security liability sets a hard deadline and may force a platform move on the parts you migrate.

4. What does a wrong move cost the business? A back-office tool with ten internal users tolerates more risk than a system processing live payments. The higher the cost of an outage or a regression, the more you bias toward incremental approaches with continuous validation.

5. Do you have parity tests? You cannot safely replace what you cannot verify. If there's no way to prove the new behavior matches the old, that's the first thing to build — for any path — before serious migration work begins.

If the data model is sound, seams exist, and the platform is alive: refactor. If the architecture or platform must change but seams exist and the business can't freeze: strangle. Reserve rebuild for the rare case where the data model is broken, no seams can be carved, and the platform is dead — and even then, migrate the data and strangle behind a facade wherever you can.

The strangler-fig pattern in practice

Because incremental migration is the right answer most of the time, it's worth making concrete. The pattern has four moving parts:

  • A routing seam. A gateway or proxy sits in front of the system and decides, per request, whether it goes to the legacy or the new implementation. This seam is the whole game — it lets you move one endpoint at a time and roll back instantly.
  • Dual-write or shared data. While a slice is mid-migration, both systems may need to read and sometimes write the same data. Dual-writes with reconciliation, or a shared database during the transition, keep the two consistent until the legacy path is retired.
  • Parity validation. For critical paths, run the new implementation in shadow mode — serve the legacy response to the user, run the new one in parallel, and compare. Differences are bugs to fix before you flip traffic.
  • Incremental cutover. Move read traffic first, then writes, slice by slice, with the ability to revert each step. The legacy system stays authoritative until the new one is provably better.

The payoff: value ships every sprint, risk is bounded to the slice in flight, and there is never a single terrifying cutover weekend. The legacy system runs until the day it's genuinely no longer needed.

The migration runbook

Whichever path you choose, the same artifacts make it survivable:

  1. An assessment. A risk-ranked written analysis of the current system, its data model, its seams, and its failure modes. This is where the refactor/strangle/rebuild decision is justified on paper.
  2. A parity test suite. The safety net that lets you change behavior with confidence.
  3. A phased plan with exit points. Six-to-twelve-week phases, each delivering working value, each with a go/no-go gate. If the business needs to pause, it can — without abandoning a half-finished rewrite.
  4. Cutover and rollback drills. Practice the switch before it's real. Know exactly how to revert each slice.

The honest cost of the rewrite

The seductive thing about a rewrite is that it promises a clean slate. The dishonest thing is what it hides: the existing system, however ugly, encodes years of accumulated requirements, edge cases, and hard-won bug fixes. Every quirk you sneer at is often a scar from a real production incident. A rewrite throws all of that away and rediscovers it the hard way — usually in front of users.

That's why we don't pitch rewrites. We design migrations that ship value every sprint with the legacy system running until the new one is provably better. Lower risk, faster payoff, no heroics. The goal is never the satisfying drama of a clean rebuild. It's a modern system, a business that never had to wait, and an on-call rotation that finally gets some sleep.


We lead legacy modernizations as phased migrations with explicit exit points. Let's plan a safe path out.

Continue reading

Related articles

Work with us

Building something similar?

Whether it's Postgres architecture, AI integration, or a greenfield MVP — a senior engineer will reply within one business day.

Schedule Call