Tax / Fiscal · Strangler

API Strangler Fig Pattern

API Strangler Fig Pattern is the work that makes the systems talk. The API is the contract between the producer and the consumer; the contract is what determines whether the.

John Kihiu12 min read

The strangler fig pattern is named after the vine that grows around a host tree, gradually taking over its structural role until the original tree can be removed and the vine stands on its own. Applied to APIs, it means routing traffic to a new implementation incrementally — endpoint by endpoint, or route by route — behind a facade, rather than attempting a big-bang cutover. It's the pattern of choice for migrating a legacy API without a multi-month freeze on new features.

The facade is the whole pattern

The mechanism is a routing facade — usually the API gateway itself — that sits in front of both the legacy system and the new one, and decides per-request which backend handles it. Clients keep calling the same base URL throughout the migration; they have no visibility into which system actually served the request. This is what makes the pattern low-risk compared to a rewrite-and-cutover: at every point in the migration, the system is in a valid, fully-functioning state, just with an evolving split of traffic between old and new.

YAML · GATEWAY ROUTING DURING MIGRATION
routes:
  - path: /api/v1/invoices/*
    upstream: legacy-monolith     # not yet migrated
  - path: /api/v1/customers/*
    upstream: customers-service   # migrated, new implementation
  - path: /api/v1/orders/*
    upstream: orders-service
    canary:
      legacy-monolith: 90%        # in-flight migration
      orders-service: 10%

Picking the first slice to migrate

The migration order matters more than the migration mechanics. Start with a route that's low-risk but real — enough traffic to prove the new implementation under genuine load, but not the endpoint that processes payments. A common mistake is migrating the easiest code first regardless of risk profile, which proves the pattern works but doesn't retire any of the complexity that made the legacy system painful in the first place. The better heuristic: migrate the pieces that are both frequently changed and well-isolated, since those are where the legacy system is costing you the most in velocity.

Migrate by capability, not by convenience

Order the migration by which parts of the legacy system generate the most change requests and bug reports, not by which are easiest to extract. The whole point is to stop paying the legacy tax on the code that hurts most, as early as possible.

Dual writes and the data consistency problem

The part of a strangler migration that goes wrong most often isn't the routing — it's data. If the new service needs its own data store, you either dual-write to both systems during the transition (with all the consistency risk that implies if one write succeeds and the other fails) or have the new service read from the legacy database until its own store is proven out. Dual writes need a reconciliation job checking the two stores agree, because "eventually consistent, we hope" is not a real consistency guarantee — it's an unmonitored assumption.

Never delete legacy data access until reconciliation is silent for a full cycle

Cutting the legacy write path the moment the new service looks like it's working removes your fallback and your ability to detect silent divergence. Keep both paths live and monitored until the discrepancy count has been zero for at least one full business cycle.

Knowing when to stop, and actually decommissioning

The strangler pattern has a natural endpoint — the day traffic to the legacy system hits zero — but teams routinely stop short of it, leaving the legacy system running "just in case" indefinitely. That's not a completed migration, it's two systems in permanent parallel, doubling your operational surface. Set an explicit decommission date tied to a traffic threshold (e.g., zero requests for 30 consecutive days) and treat removing the legacy code path as part of the migration's definition of done, not an optional cleanup task for later.

PhaseRiskWhat to watch
First slice migratedLow, if chosen wellNew service under real load
Dual writes activeHighest — data can diverge silentlyReconciliation job results
Traffic fully cut overLowLegacy system truly idle, not just quiet
Legacy decommissionedNone, if done lastNothing — this is the finish line

Wrapping up

The strangler pattern trades migration speed for migration safety — every stage leaves you with a working system, which is exactly why it's the default choice over a rewrite for anything customers depend on. The discipline it demands is in the data layer and in actually finishing: reconcile before you cut over, and decommission the legacy path on purpose rather than letting it linger.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.