Knowledge Guide
HomeSystem DesignMicroservices Patterns

The Strangler Pattern A Solution

The strangler pattern replaces a legacy system without a risky big-bang cutover by inserting a facade (router/proxy) in front of it and rerouting one capability at a time to new services, so the old system keeps serving every request that has not yet been migrated and is deleted only once nothing routes to it anymore.

The name comes from the strangler fig, which grows around a host tree, gradually takes over its structure, and leaves a hollow shell where the original stood. Martin Fowler borrowed it in 2004 for the same shape of migration: the new system grows around the old one at the network edge, and risk is bounded to the single slice you are moving rather than the whole rewrite.

How the mechanism actually works

Every request first hits a facade — an API gateway, a reverse proxy (nginx/Envoy), or a thin routing service. The facade holds a routing table keyed by path (or header, or feature flag). A route is flipped from "monolith" to "new service" the moment that capability is live in the new system and its data is available there. Until then the default route sends the request to the legacy monolith untouched, so callers never see the migration.

The four moves, in order:

  1. Insert the facade first, routing 100% to the monolith. This is a no-op release that proves the proxy layer works before any behaviour changes.
  2. Carve one seam. Pick a capability with a clean boundary and light data coupling; build it as a new service.
  3. Flip its route (often behind a canary: 1% → 10% → 100%, with instant rollback by reverting the route).
  4. Repeat until the monolith serves nothing, then delete it — code, infra, and licence.
diagram
diagram

Worked example: strangling an e-commerce monolith

The monolith shop.example.com serves four capabilities from one codebase and one Postgres DB: /catalog, /cart, /reviews, /checkout. Traffic is ~2,000 req/s. We migrate over a series of sprints, flipping routes in the facade. Trace the routing state:

SprintFacade route changeGoes to monolithGoes to new serviceData reality
0Insert facade, all → monolith100%0%1 shared DB
3GET /reviews/* → reviews-svc (canary 1→100%)~93%~7% (reads)reviews-svc reads a replica of the reviews table
5POST /reviews → reviews-svc~90%~10%reviews-svc now owns reviews; backfill + CDC feed to monolith so /catalog can still show avg rating
9/catalog/* → catalog-svc~55%~45%catalog-svc consumes rating events from reviews-svc instead of a SQL join
14/cart, /checkout → new svcs; last route flipped0%100%monolith DB frozen, then dropped; monolith deleted

The instructive step is sprint 5. Once reviews-svc owns writes, the reviews table stops being the source of truth inside the monolith — but /catalog (still in the monolith) rendered each product's average star rating with a SQL join to that table. That join no longer exists across the service boundary. You must replace it: reviews-svc emits a ReviewPosted event (or you run change-data-capture), and the monolith maintains a local product_rating projection it can join to. This cross-boundary read is the dashed line in the diagram, and it is where most strangler migrations get hard.

The facade rule, and why the naive version is wrong

With nginx-style prefix locations, the longest matching prefix wins regardless of declaration order, so the specific migrated route beats the catch-all default:

# API gateway = the strangler facade
upstream reviews_svc   { server 10.0.2.11:8080; }
upstream legacy_mono   { server 10.0.1.10:8080; }

server {
  listen 80;

  # migrated capability — longest prefix, wins over "/"
  location /reviews/ {
    proxy_pass http://reviews_svc;
  }

  # default: everything not yet migrated
  location / {
    proxy_pass http://legacy_mono;
  }
}

Why a naive hand-rolled router is wrong: if you instead iterate an ordered list and return the first matching prefix, putting the catch-all "/" before "/reviews/" silently sends every review request to the monolith — the migration looks deployed but no traffic ever reaches the new service. A prefix router must match by specificity (longest prefix), not list order, or the flip is a no-op that only surfaces when someone notices the new service has zero traffic.

Pitfalls a working engineer hits

When to use it — and when not to

Reach for the strangler when the system is large, business-critical, and cannot take downtime; you must keep shipping features during the migration; and you can find clean seams (HTTP/RPC or capability boundaries) to carve along. These are the concrete go-signals: a monolith too big to rewrite in one release, a revenue path you can't freeze, and identifiable modules with limited data entanglement.

Trade-offs versus a big-bang rewrite. The strangler's cost is real: you run and maintain two systems plus a facade, you build data-sync plumbing (events/CDC/dual-write) that is throwaway, and the intermediate architecture is uglier than either endpoint. A big-bang rewrite avoids all of that — no facade, no sync, a clean end state — but pays with high risk (one flawed cutover breaks everything), a long stretch delivering zero user value, and the second-system effect. Choose the strangler when the system is large and must stay live; prefer a big-bang rewrite when the codebase is small enough to re-build in a few weeks, or so entangled that building the facade and sync layer costs more than just rewriting it.

Versus branch-by-abstraction: that pattern does the same incremental replacement but inside a single deployable, behind a code-level abstraction, with no network facade. Prefer it when the seam is within one process/repo; prefer the strangler when the seam is across a network boundary between deployables.

Versus parallel run: parallel run executes both old and new implementations for every request and compares outputs to prove parity before cutover. Prefer it (often combined with the strangler) when correctness must be provably identical — pricing, billing, tax — and a subtle behavioural difference is unacceptable.

Takeaways


Re-authored and deepened for this guide. Sources: Martin Fowler, "StranglerFigApplication" (martinfowler.com, 2004); Sam Newman, Monolith to Microservices (O'Reilly, 2019), chapters on the strangler fig, branch-by-abstraction, and parallel run; Microsoft Azure Architecture Center, "Strangler Fig pattern"; and nginx location matching semantics from the official nginx documentation.

🤖 Don't fully get this? Learn it with Claude

Stuck on The Strangler Pattern A Solution? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🪜 Hint ladder (no spoilers)

Progressively stronger hints — you still solve it.

I'm working on the problem **The Strangler Pattern A Solution** (System Design). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🎨 Explain the approach visually

See the technique, not just code.

Explain the optimal approach to **The Strangler Pattern A Solution** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔍 Review my solution

Catch bugs, edge cases, sub-optimality.

I'll paste my solution to **The Strangler Pattern A Solution**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔁 Drill the pattern

Lock in recognition with look-alikes.

Give me 2 problems that use the SAME underlying pattern as **The Strangler Pattern A Solution**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes