SystimaNX
All articles
ArchitectureMicroservicesDevOps

Strangling a Monolith: Decomposition Without Freezing Delivery

Facade routing, bounded contexts, and incremental extraction—practical steps to peel capabilities off a monolith safely.

S
SystimaNX
April 9, 202612 min read
Strangling a Monolith: Decomposition Without Freezing Delivery

Big-bang rewrites rarely finish. The strangler fig pattern keeps the monolith running while new capabilities grow at the edges, routed through a thin facade (API gateway, reverse proxy, or feature router) that can send traffic to legacy or modern implementations.

Start with a seam that is already logically isolated—reporting, notifications, pricing—even if the code still lives in one repo. Extract behind an interface the monolith calls synchronously, then move callers to HTTP or async events once the new service proves stable.

Data is the hard part. Prefer read replicas and change-data-capture for read-heavy slices; for writes, design compensating transactions and idempotency keys early. Dual-write phases are risky—keep them short and instrumented.

Keep deployment cadence: each extraction should be shippable behind flags. Dark launch new paths, compare latency and error budgets, then shift percentage traffic gradually.

Team topology follows: a vertical slice team owns the extracted service end to end while the monolith team maintains compatibility shims until cutover. Shared coding standards and observability reduce integration surprises.

Know when to stop strangling: not every module deserves a service. Sometimes modular monolith boundaries plus clear ownership are enough until load or org scale demands finer separation.

A worked example: pulling notifications out first

Notifications is a favorite first extraction for a reason: it is usually fire-and-forget, has few callers, and a bug in it degrades gracefully—a missed email is bad, a missed checkout is a business emergency. We typically start by wrapping the existing notification code in an interface inside the monolith, even before any service exists, so every call site goes through one seam. That refactor alone, done with no new infrastructure, often takes a week and surfaces every hidden caller nobody remembered—a batch job, an admin script, a webhook handler that all called the mailer directly.

Once the seam is clean, stand up the new service and have the facade route a single low-risk call path to it—password reset emails, say, rather than the marketing digest. Run both implementations in parallel for two weeks, diffing delivery logs, before moving anything higher-stakes. The temptation is to declare victory after the first successful cutover and rush the rest of the call sites over in one sprint; resist it. Each call site has slightly different retry semantics, and the ones you migrate last are last for a reason—they are usually the ones nobody fully understands yet.

Watch latency budgets closely during this phase. A synchronous call that used to be an in-process function call is now a network hop, and if the monolith was already close to its own latency SLO, adding 5-15ms of gRPC or HTTP overhead per notification can tip a checkout flow over budget if notifications were ever accidentally on the critical path. This is usually the moment teams discover a notification call was blocking a response it had no business blocking, and the extraction forces a fix that should have happened years earlier.

Common pitfalls we see repeatedly

The single most expensive mistake is extracting a service around a database table rather than around a business capability. Teams look at a monolith's schema, see a cleanly named `orders` table, and assume that is the seam. But the pricing logic, the inventory checks, and the fulfillment triggers that all touch that table often belong to different bounded contexts entirely. Extracting along table lines instead of capability lines just moves the coupling from in-process function calls to a distributed transaction across a network—strictly worse, because now it is a distributed transaction with none of the tooling to reason about it.

Second: dual-write windows that get left open indefinitely because 'it is working fine.' Every week a dual-write path stays live is a week where a partial failure can silently diverge the two data stores, and nobody notices until a customer complains that their order total does not match what support sees. Put a hard expiration date on any dual-write phase before you start it, with an owner accountable for either finishing the cutover or explicitly extending the date in writing—an open-ended dual write is technical debt that compounds in a way that is genuinely difficult to detect from dashboards alone.

Third: treating the facade as a one-time routing decision instead of a long-lived piece of infrastructure. The facade itself needs the same rigor as any other production service—its own SLOs, its own on-call rotation, its own capacity planning. We have seen strangler-fig migrations stall for months because the facade became the new single point of failure and nobody had budgeted time to harden it, since on paper it was 'just routing.'

A related trap: assuming the extraction is done once traffic is fully cut over. The old code path in the monolith should be deleted, not commented out or left behind a permanently-off flag. We have seen 'temporary' fallback code survive for two years because removing it felt risky and nobody owned the cleanup ticket. Schedule the deletion as part of the extraction plan itself, with the same priority as the cutover, and treat a lingering dead code path as an open finding in the next architecture review rather than a curiosity.

Cost is worth tracking explicitly during this phase too. A newly extracted service often gets over-provisioned out of caution—two replicas becomes four, a small database becomes a larger one just in case. That is a reasonable short-term hedge, but it should be revisited with real production metrics within a month, not left as a permanent tax on the migration. Teams that skip this step are often surprised, six months later, why the simpler extracted service costs more to run than the slice of the monolith it replaced.

Finally, agree up front on what evidence would tell you to stop the migration entirely, not just how to run the next extraction. If a slice turns out to be more entangled than expected—shared transactions across three tables, a report job with a hard dependency on in-process state—it is cheaper to fold the seam back into the monolith cleanly than to push through with a service that will need constant firefighting. Treating a stalled extraction as a rollback decision rather than a personal failure keeps the whole program credible the next time you propose pulling something else out.

The teams that pull this off well tend to share one habit: they narrate the migration publicly, in a changelog visible to the whole engineering org, not just the team doing the extraction. A short weekly note—what moved, what traffic percentage shifted, what broke and how it was caught—turns a multi-quarter effort into something the rest of the company can follow and trust, instead of a black box that occasionally causes an unexplained incident.

Related: DevOps consulting, case studies, and resources.

Frequently Asked Questions

Ready to transform your infrastructure?

Let's discuss how we can help you implement these strategies in your organization.

Book a consultation
Strangling a Monolith: Decomposition Without Freezing Delivery | SystimaNX Blog