Software Migration

Migrating Legacy Systems Without a Big-Bang Rewrite

The big-bang rewrite is where legacy migrations go to die. Here is the phased alternative — the strangler-fig pattern, careful data migration, and reconciliation that proves it worked.

T
The Codememory Team
Codememory
Feb 24, 2026 5 min read
Migrating Legacy Systems Without a Big-Bang Rewrite

Every legacy system reaches a point where it costs more to keep than to replace: the original developers are gone, changes are slow and scary, integrations are held together with workarounds, and the technology is years past support. The instinct is to rebuild it from scratch and flip a switch. That instinct — the big-bang rewrite — is also the single most common way legacy modernisation fails. This guide covers the phased alternative that actually ships.

Why the big-bang rewrite fails

A big-bang rewrite means building a complete replacement in parallel, then cutting over everything on one date. It is appealing on paper and brutal in practice:

  • Scope never freezes. The old system keeps needing changes while you rebuild, so you are chasing a moving target.
  • No feedback until the end. The new system is unproven in production until go-live, which is the worst possible moment to discover it is wrong.
  • No clean rollback. Once everyone is on the new system and the old one is off, going back is rarely an option.
  • All risk lands on one day. Months of work are validated in a single high-stakes cutover.

The phased approach trades that one terrifying event for a series of small, reversible, low-risk steps.

The strangler-fig pattern

The most reliable phased strategy is the strangler-fig pattern, named after a vine that grows around a tree and slowly replaces it. The legacy system keeps running while you grow the new one around it and gradually divert work to it.

The mechanism is a routing layer placed in front of the legacy system:

        Clients / other systems
                  │
         Routing / facade layer
            │              │
   New service      Legacy system
   (grows over time) (shrinks over time)

Every request hits the facade. Initially it forwards everything to the legacy system. As you rebuild a capability — say, customer lookup, then order entry, then reporting — you point that slice of traffic at the new service. The legacy system shrinks function by function until nothing routes to it and it can be retired. There is never a single cutover; there are many tiny ones, each easy to validate and reverse.

Slicing the work

The art is choosing the order of slices. Good early candidates are:

  • Self-contained capabilities with few dependencies, so the first slice is genuinely shippable.
  • High-pain areas where the legacy system hurts most, so early work delivers visible value.
  • Read-only views before writes. Serving a report or a screen from the new system is lower risk than taking over a transaction, and it builds confidence.

Avoid starting with the most entangled core, where everything touches everything. Begin at the edges, learn the domain, and work inward as the new system earns trust.

Data migration is the hard part

Routing is the easy half. The data underneath is where migrations succeed or fail. Three phases matter:

  1. Profile the source. Real legacy data is messy: duplicate keys, missing values, dates stored as text, business rules encoded in quirks nobody documented. Profile it before you move anything, because the migration has to handle what is actually there, not the idealised schema.
  2. Migrate in controlled batches. Move data in repeatable, scriptable runs you can test, rerun, and roll back — not one irreversible dump. Keep the transformation logic in version control like any other code.
  3. Decide how the two stores stay consistent while both run. During the transition the legacy and new systems often both hold data. Choose deliberately: one-way sync, two-way sync with a clear master, or an event stream that keeps them aligned.

Reconciliation: proving it worked

You cannot declare a migration done because the script finished without errors. You prove it through reconciliation — comparing source and target until they agree:

  • Row counts and control totals. Do the number of records and the sum of key financial fields match between systems?
  • Record-level comparison. Sample or fully compare individual records, not just aggregates, to catch transformation errors.
  • Parallel running. The strongest proof is running both systems on real input for a period and comparing their outputs. When they agree consistently, you can trust the new one and switch the old one off.

Reconciliation is not a formality. It is the evidence that lets stakeholders sign off without crossing their fingers.

Keep the lights on during the transition

A phased migration can run for months, so the experience has to stay seamless:

  • Stable contracts at the facade so clients do not care which system answers.
  • Feature flags to route traffic gradually — a small percentage first, then more as confidence grows.
  • Observability on both systems so you can compare behaviour and catch regressions immediately.
  • A rehearsed rollback for each slice, so reversing a step is a known, calm procedure rather than an emergency.

A realistic approach

The teams that modernise legacy systems successfully resist the urge to rewrite everything at once. They put a facade in front of the old system, rebuild one well-chosen slice at a time, treat data migration and reconciliation as first-class work, and run the two systems in parallel until the evidence says the new one is correct.

This is the approach we take on migration projects at Codememory: a strangler-fig routing layer so the old system shrinks instead of being switched off overnight, data moved in tested batches with reconciliation that proves nothing was lost, and feature-flagged cutovers small enough to reverse without drama — so modernisation is a steady series of safe steps, not one terrifying weekend.

The bottom line

Legacy migration does not have to be a high-stakes gamble. Replace the system gradually with the strangler-fig pattern, slice the work from the edges inward, treat data migration and reconciliation as the core of the effort, and keep both systems running in parallel until the new one has earned trust. Done this way, you reach a fully modern system through many small reversible steps, and the old one quietly fades out instead of going down in a big bang.

Frequently asked questions

It is a migration strategy named after a vine that grows around a tree and gradually replaces it. You put a routing layer in front of the legacy system, then rebuild one capability at a time in the new system and redirect that slice of traffic to it. The legacy system keeps running and shrinking until the last piece is replaced and it can be switched off — with no single high-risk cutover.

A big-bang rewrite replaces everything at once after a long build with no production feedback. Scope is hard to freeze, the new system is unproven until go-live, and there is usually no clean rollback if something fails. Meanwhile the old system still needs changes, so you maintain two moving targets. Phased migration avoids this by shipping and validating small pieces continuously.

Through reconciliation. You profile the source data first, migrate in controlled batches, and then compare source and target with row counts, control totals, and record-level checks until they agree. Running both systems in parallel for a period and comparing their outputs is the strongest proof that the migration preserved the data and the behaviour that depends on it.

T
The Codememory Team
Codememory