Modernising a legacy system without a two-year rewrite

Big-bang rewrites fail at a famous rate. The strangler pattern, how to pick the first slice, and how to keep a legacy system running while you replace it.

Every organisation running a system older than its current engineering team faces the same question: replace it, or keep patching it. The instinct is a clean rewrite. The evidence says clean rewrites of business-critical systems fail or overrun at a rate that should make anyone cautious.

Why big-bang rewrites fail

  • The old system encodes undocumented rules. Twenty years of edge cases live in that code, and nobody remembers which ones matter until they break.
  • The business keeps moving. You are rebuilding a moving target while the old system still needs changes.
  • No value ships for months. Support evaporates when there is nothing to show.
  • Cutover is all-or-nothing. One weekend, everything at once, no way back.

The strangler pattern

Instead of replacing the system, you put a routing layer in front of it and move one capability at a time. New code handles what has been migrated; anything else passes through to the legacy system. Over time the old system handles less and less, until it handles nothing and can be switched off.

The advantages are structural, not stylistic:

  • Value ships in weeks, not years
  • Every step is individually reversible
  • The business can keep changing the legacy system meanwhile
  • You can stop at any point and still be better off

Choosing the first slice

The first migration sets the pattern for everything after, so pick carefully. Good candidates are:

  1. Well-bounded. Clear inputs and outputs, few tendrils into the rest.
  2. Genuinely painful. Something users complain about, so the win is visible.
  3. Not the riskiest thing you own. Do not start with billing.
  4. Read-heavy before write-heavy. Reads are far easier to run in parallel and verify.

Reporting and search are often ideal first slices: visible, bounded, and low blast radius.

Run both and compare. For the first slice, run old and new in parallel and diff the outputs on real traffic. You will find undocumented behaviour you would otherwise have discovered from an angry customer.

Data is the hard part

The code is usually not what makes this difficult. Two systems that both need the truth is. Decide explicitly, per entity, which system owns it during the transition, and make the other read from it rather than keeping its own copy. Two systems that both think they own customer records will diverge, and reconciling that afterwards is worse than any migration.

Knowing when to stop

Not every legacy system needs to disappear. If a stable component does its job, has no pending changes and nobody complains about it, leaving it behind the routing layer indefinitely is a legitimate outcome. The goal is a business that can change, not a codebase with a consistent birth date.

Deciding what to build versus licence in the replacement is a related question — custom versus off-the-shelf covers that framework.

Frequently asked questions

How long does legacy modernisation take?

With a strangler approach the first slice should be live within a few months. Full replacement can take years — but the point is that value arrives continuously rather than at the end, so the total duration matters far less.

Should we lift and shift to the cloud first?

It can buy breathing room on infrastructure risk, but it does not address the actual problem: code nobody can safely change. Treat it as an infrastructure decision, not modernisation.

What if nobody understands the old system?

Start by instrumenting it. Logging and traffic analysis tell you what is actually used — which is usually far less than what exists. It is common to discover that a large share of the system has not been called in years.