The Strangler Fig Facade & Routing Layer

ModernLift · ·8 min read
Part 3 of 8

The strangler fig facade is a routing layer placed in front of a legacy system that intercepts every request and sends it to whichever implementation now owns it — the legacy system or a modernized slice. Early on, almost everything routes to legacy; as each slice is proven and promoted, the facade shifts that slice's traffic to the new implementation. It is the seam that holds a half-old, half-new system together and makes incremental cutover possible.

Part 2 settled the decision: for a large, entangled, business-critical system, you migrate slice by slice rather than rewrite and switch. That raises an immediate engineering question. If half the system is old and half is new, and both are serving production at once, what decides which one answers a given request — and lets that decision change without a deploy? The answer is a single piece of infrastructure, and almost everything else in this series depends on it: the strangler fig facade.

What the facade is

The strangler fig facade is a routing layer placed in front of the legacy system. Every request the migration touches passes through it, and for each one it makes a single decision: does the legacy implementation handle this, or a modernized slice?

Early in a program, the answer is almost always “legacy” — the facade is a near-transparent pass-through, and the system behaves exactly as it did before you installed it. As each slice is built, proven equivalent, and promoted, the facade begins routing that slice’s traffic to the new implementation. Over the program, the share of requests answered by modern code climbs while the legacy system is progressively emptied of work — strangled — until nothing routes to it and it can be retired.

The facade is the seam. It’s the thing that lets you operate on the system while it’s awake.

Where it sits

The facade has to live at a point where it can intercept traffic, and the right point depends entirely on the system’s shape:

Interception pointGood fit forWatch out for
API gateway / reverse proxyHTTP services, request/response APIsAsync and streaming traffic don’t map cleanly
Service-mesh sidecarContainerized microservicesMesh must support per-route policy and mirroring
Message-bus consumerEvent-driven, queue-based systemsOrdering and at-least-once delivery semantics
Application-level dispatchMonoliths with no clean network seamCouples routing logic into code you’re retiring

The first three are network seams — clean places to insert a layer that the system already routes through. The last is the hard case: a monolith with no network boundary, where the “facade” is a dispatch point inside the application that hands a call to either the old code path or a new module. It works, but it couples migration logic into the very code you’re trying to retire, so it’s a means to create a seam where none exists, not a destination.

The facade is a role, not a product. A gateway, a proxy, a mesh, or in-application dispatch can all play it. What defines the pattern is the job — migration routing — not the tool you implement it on.

What the routing decision actually keys on

“Route to legacy or to the slice” sounds binary, but the decision is richer in practice. The facade routes on:

  • The slice that owns the request. The first axis is what the request is — which capability it touches — because only some capabilities have been modernized yet. The facade needs a map from request shape (path, message type, operation) to the owning slice.
  • The promotion state of that slice. A slice in shadow gets mirrored but never answers. A slice ramping gets a percentage of live traffic. A fully promoted slice gets all of it. The facade reads that state at request time.
  • A stable routing key. When a slice is mid-ramp, the facade often needs to send a given user or account consistently to one side, so behavior stays coherent across a session. That sticky assignment lives at the facade too.

This is why the facade is real engineering rather than a config file. It’s holding the live map of which capabilities are where, in which promotion state, for which population — and applying it correctly thousands of times a second.

The facade is also where evidence is gathered

A subtle and powerful property: the same layer that routes live traffic can also mirror it. For a slice still being validated, the facade forwards the request to legacy as normal — and returns that response — while asynchronously sending a copy to the modern slice, whose output is captured and compared but never shown to a user. That mechanism is shadow traffic, and it’s how each slice earns the right to be promoted before it carries any live load.

This series doesn’t re-derive that mechanism — it has its own deep treatment in Shadow Traffic Testing Explained. The point here is architectural: the facade is the single place where both jobs happen. It routes the traffic that counts and mirrors the traffic that’s still being proven, which is why it sits at the center of both the migration and the validation that makes the migration safe.

The rules the facade must obey

Because every request passes through it, the facade’s reliability is the system’s reliability. A few rules are non-negotiable:

  • Fail-open. If a modern slice or its shadow path is slow or errors, the production path must not notice. Mirrored copies are fired asynchronously; the live response never waits on them. A dead shadow degrades to “no evidence collected for that window” — never to a degraded user experience.
  • Minimal latency. The facade is on the hot path of every request. The routing decision has to be cheap — a lookup, not a computation — or it taxes the whole system.
  • Sane defaults. When the facade is unsure — a routing-config failure, an unreachable slice — it falls back to the known-good legacy path, which is still live precisely so it can catch the request.
  • Auditable. Every routing change is a deliberate act with consequences. The facade logs which population went to which implementation, so divergences are legible and changes are attributable.

The facade has a cost

The facade is not free, and a buyer should weigh that squarely. It adds a layer — a routing decision and a configuration surface — to the hot path of every request, and that layer can fail in ways the original system couldn’t. It’s also a piece of infrastructure that has to be operated and maintained for the life of the migration. On a small or genuinely isolated system, where a direct, well-rehearsed cutover is safe, a full facade can be more machinery than the risk warrants — match the control surface to the stakes.

There’s a longer-term hazard too, which Part 6 takes up in full: a facade that’s never retired becomes its own piece of legacy. The layer exists to enable the strangle, and once the strangle is complete for a given seam, the facade’s routing for that seam should collapse away. A permanent facade is a sign the migration stopped halfway.

Where this leads

The facade decides which implementation answers. But routing a request to a modern slice raises a second problem: that slice has to talk to a legacy system whose data model and assumptions you’re trying to leave behind — and if the old model leaks into the new code, you’ve just rebuilt the legacy system in a new language. Part 4, Anti-Corruption Layer Explained, is about the translation layer that keeps that from happening — the facade’s necessary companion, and the thing that lets new code stay clean while the old system is still alive underneath it.

Frequently asked questions

Where does the strangler fig facade physically sit?
At whatever choke point can intercept the system's traffic — an API gateway or reverse proxy for HTTP services, a service-mesh sidecar for containerized microservices, a message-bus consumer for event-driven systems, or, for a monolith with no network seam, a dispatch point inside the application itself. The right location depends on the system's shape; the requirement is that every request the migration touches passes through one controllable point.
Is the facade the same as an API gateway?
An API gateway is one common place to implement a facade, but the facade is a role, not a product. Its job is migration routing — deciding per request whether legacy or a modern slice answers, and shifting that decision gradually. A gateway, a proxy, a mesh, or application-level dispatch can all play that role. The pattern is defined by what the layer does, not by the tool you build it on.
Doesn't adding a facade introduce a single point of failure?
It introduces a layer that must be engineered for reliability, yes. The mitigations are standard but non-negotiable: the facade must be fail-open so a problem in a shadowed slice never degrades the production path, it must add minimal latency on the hot path, and it must have sane defaults that fall back to the known-good legacy path. A facade that can take down production isn't a facade — it's a second production system you forgot to budget for.
All 8 parts of The Strangler Fig Pattern & Incremental Migration →