The rewrite that never lands

The proposal is familiar. The existing system is difficult to change, so a team is assigned to build a replacement while everyone else keeps the original running. Eighteen months later there are two systems: one that does everything and is hated, and one that does forty per cent and is not finished.

The failure is structural rather than a matter of effort. During a full rewrite the original does not stand still; it accumulates fixes and features that the replacement must also absorb, so the target keeps moving away at roughly the speed the team approaches it.

Strangler migration: replace around the edges

The alternative replaces the system incrementally while it stays in production. A routing layer sits in front, and each capability is moved behind it one at a time. Traffic for a migrated capability goes to the new implementation; everything else continues to the original.

  • Put a seam in front. A gateway or facade that can route per capability.
  • Pick the first slice carefully. Valuable enough to matter, isolated enough to be safe.
  • Move it, route it, verify it. Often running both and comparing before switching.
  • Repeat. Each slice reduces the original's surface area.
  • Retire what is left. The original shrinks until removing it is uneventful.

Every step is shippable and reversible, which is what makes the approach survivable when priorities change mid-migration.

Choosing boundaries that hold

The most consequential decision is where the seams go. Boundaries drawn around technical layers produce services that cannot change independently: every feature still touches all of them.

Boundaries drawn around business capabilities hold up better. Ordering, billing, scheduling and notification each own their data and expose a contract. A change to how billing calculates tax stays inside billing.

Worth saying plainly

If two services must always be deployed together, they are one service with extra network calls between them. That is a boundary in the wrong place.

Modular does not have to mean distributed

Extracting modules and extracting services are separate decisions, and conflating them is a common and expensive mistake.

A modular monolith (clear internal boundaries, separate schemas, enforced module contracts, deployed as one unit) delivers most of the maintainability benefit without distributed transactions, network failure modes, or the operational burden of running many services.

Split into separate services when there is a specific reason: independent scaling, independent deployment cadence, or team ownership that genuinely requires isolation. Splitting without one of those reasons buys complexity and no benefit.

Data is the hard part

Code is comparatively easy to move. Data is where migrations stall, because a shared database is the tightest coupling in most legacy systems.

Pragmatic sequence: keep reads shared initially while writes move to the new module, introduce a synchronisation path, then cut reads over once the new store is authoritative. Slower than a clean split, and considerably less likely to lose records.

Key takeaways

  • Full rewrites fail because the original keeps moving.
  • Route through a seam and replace capability by capability.
  • Draw boundaries around business capabilities, not technical layers.
  • Services that always deploy together are one service.
  • Modular monolith first; distribute only for a specific reason.

Frequently asked questions

What is the strangler fig pattern?

It is an incremental migration approach where a routing layer sits in front of a legacy system and capabilities are moved behind it one at a time. The old system shrinks gradually until it can be retired, and every step ships independently.

Should we split a monolith into microservices?

Only for a specific reason: independent scaling, independent deployment cadence, or team ownership requiring isolation. A modular monolith with enforced internal boundaries delivers most of the maintainability benefit without distributed system complexity.

Modernise Without a Rewrite?

Modernise Without a Rewrite
Share this insight