2025-01-09 6 min read

The Strangler Fig Pattern: Safely Migrating Monoliths to Services

Learn how the strangler fig pattern lets you migrate monolithic systems to microservices without the risk of a big-bang rewrite. A practical guide with code examples.

The Strangler Fig Pattern: Safely Migrating Monoliths to Services

Your monolith works. It's been working for years. But it's also become a bottleneck—hard to test, harder to scale, and nearly impossible to deploy without holding your breath. You know microservices could help, but a complete rewrite is off the table. You need a migration strategy that doesn't blow up production.

Enter the strangler fig pattern. Named after a parasitic plant that gradually replaces a host tree, this approach lets you incrementally extract functionality from your monolith and move it to new services, one piece at a time. No big bang. No weeks of integration hell. Just steady, measurable progress.

How the Strangler Fig Works

The core idea is simple: introduce a facade (usually an API gateway or router) between your clients and the monolith. Route requests to the new service if it exists; otherwise, pass them through to the old system. Over time, more functionality lives in services, until the monolith becomes unnecessary.

typescript
// Express.js example: routing requests to old or new systems
const express = require('express');
const httpProxy = require('express-http-proxy');

const app = express();

// Route to new user service
app.use('/api/users', httpProxy('http://user-service:3001'));

// Route to new order service
app.use('/api/orders', httpProxy('http://order-service:3002'));

// Everything else goes to the monolith
app.use('/', httpProxy('http://legacy-monolith:8080'));

app.listen(3000);

The beauty here is control. You choose which endpoints move first. You can test each service independently. You can roll back a problematic extraction without affecting the entire system.

Planning Your Migration

Identify Extraction Candidates

Start with services that are loosely coupled to the rest of the monolith. Authentication, user management, or billing often make good first candidates. Avoid ripping out core business logic that touches everything else.

Map your monolith's dependencies:

python
# Simple dependency tracker for planning
services = {
    'users': ['auth', 'email'],
    'orders': ['users', 'inventory', 'payments'],
    'inventory': ['notifications'],
    'payments': ['users', 'orders']
}

def extract_order(target):
    """Check what must move with target service"""
    dependencies = services.get(target, [])
    print(f"Moving {target} requires: {', '.join(dependencies)}")

extract_order('orders')
# Output: Moving orders requires: users, inventory, payments

Set Up Your Facade

Your routing layer is critical. It needs to be simple, fast, and easy to modify. At LavaPi, we typically recommend starting with a lightweight gateway like Kong or a custom Node.js router. Avoid overcomplicating this layer—it's a transition tool, not permanent infrastructure.

Handle Shared Data

The hardest part of strangler migration isn't code—it's data. If your new service needs the same data the monolith uses, you have options:

  • Dual writes: Write to both systems during transition. Risky but fast.
  • Event sourcing: Use events to keep systems in sync.
  • Database replica: New service reads from a copy of monolith data.
bash
# Basic example: replicate order data to new service
psql -h legacy-db.internal -d monolith_db \
  -c "COPY orders TO STDOUT" | \
psql -h new-service-db.internal -d orders_db \
  -c "COPY orders FROM STDIN"

Execution Best Practices

Start small. Extract one service, run it in production for two weeks, handle edge cases, then move on.

Monitor heavily. Both the new service and monolith need observability. Errors in either path should be visible immediately.

Keep rollback easy. Your routing layer should allow instant fallback to the monolith if a service fails.

Test the seams. Integration tests between your facade and both old and new systems are non-negotiable.

The Payoff

Strangler migration takes longer than a rewrite would—maybe 6–18 months for a substantial system. But you're deploying features to production every sprint. You're learning from your services as you build them. And if you discover a mistake, you haven't bet the entire company on it.

The monolith doesn't disappear overnight. It shrinks, slowly and deliberately, until one day you realize it's handling nothing but legacy queries nobody's touched in years. At that point, you shut it down without ceremony.

That's the strangler fig pattern: patient, practical, and far less risky than the alternative.

Share
LP

LavaPi Team

Digital Engineering Company

All articles