All insights Architecture · 8 min read

Retiring a monolith one slice at a time

A big-bang rewrite asks the business to stop changing while engineering catches up. The strangler pattern avoids that.

Engineer mapping a migration plan on a whiteboard

Most legacy systems we are asked to replace still work. They are slow to change, hard to test and expensive to host, but they process real transactions every day. Turning them off for a rewrite is rarely an option, and rewrites that run for two years without shipping usually get cancelled.

The strangler pattern takes a different route. You put a routing layer in front of the old system, build one capability at a time in the new architecture, and move traffic over only when each piece is proven. The old system shrinks until nothing routes to it.

Step 1: put a front door in place

Before writing any new service, we add a gateway or reverse proxy that every request passes through. On day one it sends everything to the legacy system. That sounds like no progress, but it gives you the one control you need later: the ability to send a single route somewhere else.

# routes.yaml: day one, everything still goes to legacy
routes:
  - match: /claims/*
    upstream: legacy-claims
  - match: /*
    upstream: legacy-claims

Step 2: pick the first slice carefully

The first slice should be valuable enough to matter and small enough to finish in a few sprints. Read-only features are a good start because a mistake can't corrupt data. We avoid starting with the most complex workflow, even if it is the most painful one.

Step 3: run both paths and compare

For each slice, the new service runs in shadow mode first. It receives a copy of real traffic, and we compare its answers with the legacy system's answers. Traffic only moves once the results match on real data, not just test data.

  • Shadow: the new path receives copies of requests; users still see legacy results.
  • Canary: a small share of real users get the new path, with a rollback switch.
  • Cutover: the route moves fully, and the legacy code for that slice is deleted.

Step 4: delete the old code

The step teams most often skip is removing the legacy code once a slice has moved. Without it, the old system never gets smaller and nobody trusts which path is live. We treat deletion as part of done.

When not to use it

If the legacy system is small, well understood and has few integrations, a direct rewrite can be faster. The strangler pattern earns its overhead on systems that are large, busy and impossible to freeze.