Animated Architecture Patterns

Strangler Fig

Put a facade in front of the legacy system and move routes to new services one at a time.

Also known as Strangler pattern, Strangler application

Clients web · mobile Strangler facade routes requests by path route goes to /catalog /orders /users /billing Legacy monolith one big deployable catalog catalog no traffic orders orders no traffic users users no traffic billing billing no traffic legacy legacy legacy legacy catalog service orders service users service billing service new new new new decommissioned

Step 1 · Add a facade. Place a facade (an API gateway, reverse proxy or routing layer) in front of the legacy system. Clients now talk to the facade, which forwards every route to the monolith unchanged. Nothing has migrated yet, but you now control where each request goes.

Step 2 · Extract /catalog. Pick one capability with clear boundaries, rebuild it as a new service, and flip its route in the facade. The matching code in the monolith stops receiving traffic from the facade and can be deleted later, once calls to it from other legacy modules are redirected to the new service too.

Step 3 · Peel off more. Repeat, one route at a time, in whatever order delivers value or reduces risk. Each step is small and reversible: if the new service misbehaves, flip the route back to legacy.

Step 4 · Retire the monolith. When the last route has moved, the monolith serves nothing and can be switched off. The new system has grown around the old one and replaced it, like the strangler fig vine the pattern is named after.

The problem

A legacy system is too important to switch off and too costly to keep changing. A big-bang rewrite looks tempting, but it means months or years with no new value, two systems to keep in sync, and one terrifying cut-over day at the end. Many rewrites never reach that day.

How it works

  1. Intercept. Put a facade in front of the legacy system so that every request passes through something you control.
  2. Replace one slice. Build the new implementation of a single capability, usually one route or one bounded context.
  3. Redirect. Change the facade's routing so that slice goes to the new implementation, and redirect calls to it from other legacy modules as well. Legacy code for it then goes dark.
  4. Repeat, then retire. Keep going until nothing is routed to the old system, then decommission it.

The migration becomes a long series of small, reversible releases instead of one irreversible leap.

When to use it

  • Replacing or re-platforming a large system that must stay in service throughout.
  • When the system can be intercepted at a boundary: HTTP routes, message topics, a UI shell, file drops.
  • When you want to deliver new value during the migration rather than only at the end.

Trade-offs

  • Shared data is the hard part. Routes are easy to move, but new services and the monolith often share a database for a while. Plan data ownership, synchronization (for example CDC) and eventual cut-over explicitly.
  • The facade is critical infrastructure. It must be highly available and fast, and must not grow business logic of its own.
  • Two worlds for a while. Expect duplicated concerns (auth, logging, deployment) until the migration ends. Keep momentum so "temporary" doesn't become permanent.
  • Not every system can be sliced. Tightly coupled batch jobs or UIs without seams may need branch by abstraction or a parallel run instead.

Implementation notes

  • The facade is often something you already run: an API gateway, an ingress controller, a CDN or load balancer with path-based routing, or a BFF.
  • Put an anti-corruption layer between new services and legacy models so the old domain model doesn't leak into the new code.
  • Use feature flags or weighted routing to move a route gradually (1% → 10% → 100%), and shadow traffic or a parallel run to compare results before switching.
  • Track progress visibly: routes migrated, traffic share on legacy, modules deleted.