Strangler Fig Pattern: Incremental Monolith-to-Microservices Migration Without Big-Bang Rewrite

This article details the Strangler Fig Pattern for migrating legacy monoliths to microservices, using a facade layer, dual-write with local message tables, traffic mirroring, and one-click rollback, illustrated with a 14-month retail migration and the TSB bank failure case.

Internet Backend Architecture Stack
Internet Backend Architecture Stack
Internet Backend Architecture Stack
Strangler Fig Pattern: Incremental Monolith-to-Microservices Migration Without Big-Bang Rewrite

Phenomenon Review

Most monolith migration failures stem from wrong strategy, not technology. Big-bang rewrites face two hard constraints: implicit business rules buried in years of code and operations memory that only real traffic triggers, and continuous business demands that force parallel development on two systems, causing non-linear maintenance cost growth.

Counter-example: TSB Bank core system cutover accident. TSB attempted a one-time migration of 5.2 million user accounts to a brand-new core system with no parallel run or domain-by-domain peel-off. The result was 19 consecutive days of outages, nearly 1.9 million users unable to log in, funds visible but unusable, and cross-user data leaks. Total compensation and regulatory fines exceeded £370 million. In financial architecture reviews, this case is a red line: core transaction chains cannot tolerate long downtime, and big-bang cutover concentrates all risk into a single release window.

The Strangler Fig Pattern keeps the existing monolith as a stable base, adds a facade layer to intercept traffic, and peels off capabilities by business domain. Each peeled capability shrinks the monolith until it can be retired. The metaphor: a fig vine grows on a host tree, gradually taking over resources until the host withers — not chopping down the tree and replanting.

First-hand Case: Traditional Retail Mall Progressive Migration

I led the migration of a 12-year-old Java monolith with hundreds of thousands of lines, sparse documentation, and deeply coupled transaction chains. The team abandoned full rewrite and adopted the Strangler pattern.

Phase 1: Deployed a facade gateway, prioritized the product base domain — chosen for low change frequency and no core transaction dependencies. Never touch order/payment links first. Built an independent product database, used a local message table for dual-write, enabled traffic mirroring to compare old and new responses continuously, and ran reconciliation. Canary started at 1% traffic, slowly ramped up; after two stable months with zero data divergence, full cutover completed.

Phase 2: Peeled off marketing coupon module. Phase 3: Only then started order chain migration. Total cycle: 14 months. At project close, the old monolith retained only member basic queries and historical order read-only logic. After evaluating coupling and change frequency, we decided to keep that portion permanently in the monolith. Zero full downtime throughout; any module anomaly triggered instant gateway fallback to the old monolith, with no large-scale data inconsistency incidents.

Underlying Mechanism Breakdown

The architecture has three layers: Facade routing layer, legacy monolith, new microservice cluster.

Facade layer: unified traffic entry, routes migrated capabilities to new microservices per predefined rules; unmigrated traffic continues to legacy monolith.

Parallel run period: dual-write is the foundational capability and the biggest challenge.

Migration unit principle: peel by business capability domain, never split by database tables or code packages. Many teams fail here by cutting tables directly, tearing cross-domain logic and losing transaction consistency.

Client Request → Facade Routing Layer
  ├─ Migrated Capability → New Microservice → Dual-write Data
  └─ Pending Capability → Legacy Monolith

The facade is not just a simple canary router; it is the control plane of the migration, carrying traffic routing, anomaly circuit-breaking, request log comparison, and one-click rollback. When a new microservice misbehaves, the facade quickly cuts back to the monolith, guaranteeing business continuity — the safety baseline of the whole scheme.

Architecture diagram showing facade routing to microservices and legacy monolith with dual-write
Architecture diagram showing facade routing to microservices and legacy monolith with dual-write

Engineering Pitfalls

Pitfall 1: Shared Monolith Database — Fake Split Trap

The most common shortcut: new microservices directly reuse the monolith database. Short-term dev speed improves, but new service and old monolith become tightly bound to the same schema. Old system field/index changes immediately affect the new service. This is only deployment-level split; essentially a distributed monolith, rendering the migration ineffective.

Real case: Local e-commerce fake Strangler migration. An e-commerce team implemented Strangler migration but reused the monolith MySQL for the new product service, only splitting application code. After six months, services were independently deployed and migration appeared complete. Later the monolith added an inventory lock column to the product table; the new service query logic wasn't compatible, causing massive product detail page errors. Database coupling uncut — all migration was surface work.
Diagram showing shared database anti-pattern
Diagram showing shared database anti-pattern

✅ Correct approach: Each peeled business domain gets an independent database, relying on dual-write to sync old and new data.

Anti-pattern code:

// Wrong: sequential write to new DB then old monolith DB, no transaction protection, prone to single-side success
public void createOrder(OrderDTO dto){
    orderNewRepo.save(dto);
    orderOldRepo.save(dto);
}

This code repeats in many projects: new DB write succeeds, old DB write fails due to network timeout, leaving both sides inconsistent. Such issues are latent faults with extremely long investigation cycles; business sees data appear and disappear intermittently.

✅ Reliable dual-write simplified code (local message table):

// Local message table dual-write: same transaction guarantees new DB + message record, async compensation writes to old monolith
@Transactional
public void createOrder(OrderDTO dto) {
    // Same transaction: write new business DB + write local message table
    orderNewRepo.insert(dto);
    localMsgRepo.insert(
        LocalMsg.builder()
            .bizId(dto.getOrderNo())
            .bizData(JSON.toJSONString(dto))
            .targetSystem("legacy-monitor")
            .status(0)
            .build()
    );
}
// Independent scheduled task: consume local messages, retry write to old monolith DB, alert after multiple failures

Trade-off: dual-write adds latency and sync complexity, not a silver bullet . Business must accept brief eventual consistency; financial strong-consistency chains need layered distributed transaction solutions, with careful cost evaluation.

Pitfall 2: Migration Unit Granularity Out of Control

Too small: each interface migration requires facade change, frequent releases, ops burden rises. Too large: migration cycle drags, dual maintenance too long, business backlog piles up, team fatigue. Landing criterion: prioritize capabilities with clear boundaries, low change frequency, and independent autonomy. In mall scenarios, start with product base info; core transaction chains go late.

Pitfall 3: Relying Only on Unit Tests, Missing Traffic Regression Validation

Legacy system implicit rules cannot be fully covered by unit tests. Parallel phase must enable traffic mirroring: same request dispatched to both old monolith and new service, compare responses to detect logic gaps. Traffic mirroring is only a migration-phase verification tool; shut it down after stability to control resource overhead, not a permanent fixture.

Hidden Control Plane Risks

Most architecture docs discuss business peeling and dual-write but rarely mention the biggest Strangler hazard: migration control plane decay .

As multiple domains migrate, facade routing rules accumulate. Routing config scatters across business code and config centers without unified governance. Over time, which capabilities are on the new system vs. still in the monolith becomes a black box.

Real case: SaaS vendor stuck in parallel phase unable to finish. A SaaS vendor used Strangler to split system; multiple modules migrated sequentially, gateway routing hardcoded in business code. Two years later, the lead architect left; no one knew the full routing inventory. A business release caused both logics to run in parallel — some traffic to new service, some to monolith — reconciliation showed persistent discrepancies. Team dared not increase cutover ratio, could not retire old monolith, project permanently stuck in parallel swamp, incurring high annual maintenance cost.

Governance measures:

Unified migration metadata registry recording each domain's stage: pending / canary verification / full cutover / archived offline.

Routing rule changes must pass architecture review; ad-hoc hardcoded routing by developers forbidden.

Every domain migration must have minute-level one-click rollback to monolith — a hard red line.

Another often underestimated hidden cost: long-term ops pressure of dual-write and data reconciliation. Migration cycles often run six months to a year, requiring continuous monitoring of data deltas, developing reconciliation scripts, handling compensations. Many teams only count dev hours, ignoring long-term reconciliation headcount; late-stage projects run out of people, stuck in parallel phase unable to close.

Multi-layer Architecture Loop & ROI Assessment

Before launching Strangler migration, a cost-benefit assessment is mandatory. Strangler is not the standard answer for every monolith; many monoliths need no microservice migration.

Assessment checklist:

Business scaling pressure: does the monolith have clear scaling bottlenecks? If business is stable with no traffic growth, monolith architecture is sufficient — no forced transformation.

Organizational structure fit: are teams organized by business domain? Conway's Law — if organization isn't split, extracted services inevitably become distributed monoliths.

Cycle estimation: peel cycle per domain, plus parallel maintenance headcount cost.

Risk mitigation plan: rollback mechanism, data reconciliation scheme, canary operation process — are they complete?

Full landing loop:

Business domain partitioning, select first batch of peel modules.

Build facade routing layer with traffic mirroring, log auditing, rollback mechanism.

Design independent databases, implement local message table dual-write.

Mirror traffic comparison, verify new/old system logic consistency.

Small traffic canary, gradually increase traffic ratio.

After stable run period with zero reconciliation diff, disable old monolith module write capability.

Gradually trim and offline corresponding monolith module code.

Trade-off essence: Strangler pattern extends overall project timeline, increases short-term headcount investment, in exchange for business continuity. It sacrifices migration speed to control the massive risk of explosive rewrite. If the team expects fast transformation and lacks budget for long-term parallel maintenance, Strangler is not suitable.

Landing Review

In architecture review meetings, these 5 questions are mandatory as architecture gate checklist:

Is the current target peel domain boundary clear? Any cross-domain strong coupling logic that cannot be isolated?

What is the new/old data sync scheme? How to detect and auto-compensate data inconsistency?

Are canary cutover rollback thresholds, conditions, and operation plans documented?

What is the migration completion criterion? Not just functional pass, but continuous period of zero reconciliation diff and zero business incident reports.

What is the monolith module offline criterion? Which conditions must be met before shutting down old module?

Architecture Elevation Summary

Many architects invert priorities: treating microservice shape as the goal rather than a means to solve business bottlenecks. The Strangler Fig Pattern is essentially not a code-splitting technical solution, but a risk control system for legacy system evolution.

The migration endpoint is not monolith elimination. After assessment, if certain modules have extreme coupling and near-zero change, they can stay permanently in the monolith without forced migration. Architecture evolution never pursues formal perfection; it continuously seeks balance among business demands, headcount investment, and failure risk.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Dual WritemicroservicesrollbackTraffic MirroringMonolith MigrationArchitecture ReviewLegacy Modernizationstrangler fig pattern
Internet Backend Architecture Stack
Written by

Internet Backend Architecture Stack

Over ten years of hands-on Java backend architecture experience, focusing on sharing mainstream internet technologies such as Java backend, microservices, distributed systems, high concurrency, performance optimization, and source code analysis. Building a platform for Java technologists to exchange and grow, collaborating with peers to advance together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.