Why the big rewrite keeps failing
A full rewrite asks the business to freeze while engineering rebuilds years of behavior that was never written down. The old system keeps changing underneath you, the new one ships late, and for months you run two systems that both partly work.
The strangler pattern avoids that. You put a seam in front of the legacy system and move one capability at a time behind it. The business keeps running on the old system until each piece is proven on the new path.
Step 1: put a facade in front of everything
Before moving any logic, route traffic through a layer you own: an API gateway, a thin service, or a set of serverless functions. At first it does nothing but pass requests through and log them.
That log is the most valuable artifact of the whole project. It tells you which endpoints and workflows are actually used, which are dead, and where the volume is.
Step 2: choose the first slice by risk, not by size
A good first slice has clear inputs and outputs, a measurable result and limited blast radius if it goes wrong. Common good first candidates:
- Read-only reporting that today requires an export and a spreadsheet
- Notifications and messaging triggered by state changes
- Intake from a new channel (a form, a partner webhook) that the old system was never built for
- Payment and billing sync, where an external processor already holds the truth
Step 3: one adapter per legacy system
Every call into the old system goes through a single module. It handles authentication, retries, pagination, field mapping and every odd behavior you discover. When the vendor returns success with no ID, or the same page twice, that is fixed in one place.
This also gives you a clean place to write contract tests, so a vendor update that changes behavior fails a test instead of a customer workflow.
Step 4: decide who owns each piece of data
For each entity (customer, order, job, invoice), write down which system is the source of truth. Two systems that both claim to own a record will drift. Everything else should read from, or be derived from, the owner.
Step 5: make failure visible
Integrations fail quietly. Put every failed write in a dead-letter store with its payload and the reason, show it to the people who own the workflow, and let them retry it. Silent failure is the most expensive bug in any integration project.
How you know you are done
You are done with a slice when traffic for it no longer reaches the legacy code path, the monitoring proves it, and the old path can be switched off. You may never retire the legacy system entirely, and that is fine. The goal is that it stops being the bottleneck.