In this article
Almost every business that has run software for ten years has one system that everyone is nervous about. It works, mostly. Nobody remembers why it does certain things. Adding a feature takes three times longer than it should, and the person who understood it left in 2019. The obvious thought is "let's rebuild it properly". That thought is where many projects begin to go wrong.
This article explains the alternative that engineers have favoured for two decades: replace the system gradually, while it keeps running.
Why big-bang rewrites disappoint
Martin Fowler's description of the problem is worth quoting at length, because it matches what many teams experience. He writes that it is easy to treat modernisation as a simple replacement: "We know what the old system does, so just build a new system that does exactly the same". But "we've seen this simple-sounding plan go down in flames most of the time". His reasons are that replacing a serious system takes a long time and users cannot wait for new features, that it is hard to figure out the details of existing behaviour, and that much of that behaviour "isn't really wanted, so building it is a waste".
That is an experienced practitioner's view rather than a measured failure rate, and you should read it that way. It is still a useful warning: the risk in a rewrite is concentrated at the end, when everything must switch over at once.
How the strangler fig works
Microsoft's description breaks the process into four phases.
- 01Introduce a façadeA proxy sits between users, the old system and the new one. At first it sends most requests to the old system.
- 02Shift functionality graduallyWith each iteration more pieces are built in the new system and the façade routes those requests to it.
- 03Decommission the legacy systemOnce nothing depends on the old system, it is switched off and all requests go to the new one.
- 04Remove the façadeClients then talk to the new system directly, which marks the end of the migration.
Fowler adds a framing that is useful for management conversations. He cites four high-level activities, which he says are not a sequence: understand the outcomes you want, decide how to break the problem into smaller parts, successfully deliver the parts, and change the organisation so this can happen on an ongoing basis. Muddled objectives, he notes, are a common problem, with different groups wanting different things.
Step 1: Be clear about the outcome
"Modernise" is not an outcome. Better outcomes sound like: cut the time to add a pricing rule from weeks to days, remove the dependence on an unsupported hosting platform, or let customers self-serve a task that currently needs a phone call. Write two or three of these down and agree them with everyone who will argue later. They will decide what you migrate first.
Step 2: Find the seams
Fowler's term is "seams": places where you can insert a boundary so a piece can be split off. He is candid that well-designed systems would already have them and that such systems are "unicorns". In a web application, practical seams are often:
- a self-contained section of the site, such as a customer portal or an admin reporting area;
- a single business capability, such as quoting, invoicing or booking;
- an integration, such as payment, email or a CRM sync;
- a read-only feature, which is lower risk than a feature that changes data.
Choose the first slice for learning and value rather than size. A small, visible, low-risk piece that goes live in weeks teaches the team how the old system really behaves.
Step 3: Route traffic
For a web application, the façade can be a reverse proxy, a gateway or the front-end framework itself. The Next.js documentation describes the last option. Rewrites can proxy requests to an existing website, and a fallback rewrite sends anything the new application does not handle to the old one. The documentation says this is "especially useful for incrementally adopting Next.js", and that you do not need to change the rewrite configuration as you migrate more pages.
An illustrative configuration, adapted from the documented pattern, follows. The legacy address is a placeholder, so check the current documentation for your version before using anything like it.
// next.config.ts (illustrative sketch, not a drop-in configuration)
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
async rewrites() {
return {
// Anything the new app has no route for is proxied to the legacy site.
fallback: [
{
source: "/:path*",
destination: "https://legacy.example.com/:path*",
},
],
};
},
};
export default nextConfig;
Whatever routing you use, Microsoft lists points to watch: the façade must keep up with the migration and must not become a single point of failure or a performance bottleneck. From our own experience of this kind of work we would add two checks, which are our own judgement and not from those sources. Make sure sign-in works across old and new pages, so people are not logged out when they cross the boundary. And keep existing web addresses working with proper redirects, so bookmarks and search visibility are not lost.
Step 4: Handle the data
Data is usually harder than screens. Microsoft's guidance flags that services and data stores used by both systems must be accessible to both at the same time. Its worked example describes extracting one domain at a time from a shared database:
- A new service starts handling requests for its domain but still reads and writes the legacy database.
- A separate domain database is created. Historical data is loaded, and change data capture keeps the two in sync while consistency is validated before cutover.
- After validation, the new database becomes the system of record for that domain, and the corresponding legacy objects are removed.
The guidance also points out that rollback is possible until the legacy objects are removed, and that removing them is a deliberate final step to take only after validation. In plain terms: do not delete the old data path the same day you switch over.
The guidance recommends an anti-corruption layer where old and new components must call each other. It is an adapter that translates between them, so the new design is not forced to inherit legacy conventions.
Step 5: Prove each slice before moving on
Before each cutover, run the new slice alongside the old and compare. Check the same inputs produce the same outputs, including the awkward cases nobody wrote down. Keep a fast route back to the old behaviour. Document what the old system did that nobody asked for, and decide deliberately whether to keep it. This step is where automated tests pay for themselves, which is why modernisation and test coverage tend to arrive together.
When this approach does not fit
| Condition | What it means in practice |
|---|---|
| Requests to the back end cannot be intercepted | If you cannot place a routing layer in front, you cannot shift traffic gradually. |
| You cannot access the legacy source code | Microsoft notes you need to modify the legacy system to disable migrated features and redirect internal calls. |
| The system is small and simple to replace | A rewrite may be cheaper than building transitional routing. |
| The original must be switched off quickly | The pattern keeps the old system running for an extended period by design. |
Fowler is also honest about the cost. Building transitional architecture that will be thrown away "may appear to be a waste", but in his view the reduced risk and earlier value outweigh it. He adds that the approach "doesn't make the exercise easy". Microsoft similarly suggests treating a retained façade as transitional and balancing its risk benefits against its temporary infrastructure cost.
What to ask a supplier
- What is the first slice you would migrate, and what would we see live within weeks?
- How will the routing layer work, and what happens if it fails?
- How will data be shared or synchronised between old and new during the migration?
- How will we compare old and new behaviour before each cutover?
- What does it cost to run both systems in parallel, and for how long?
- Who maintains the old system while it shrinks?
A supplier who answers with a single big delivery date and no interim milestones is describing a rewrite, whatever they call it.
After the migration
A modernised system still needs looking after. Plan for dependency updates, security patches and monitoring from day one, which we cover in our web application maintenance checklist. If the new front end uses Next.js, see the Next.js 16 upgrade checklist and where to host a Next.js app for the operational choices that follow.
Conclusion
You do not have to choose between living with a fragile system and betting the business on a rewrite. Replacing it in slices lets you learn early, show value sooner and stop or change course if priorities shift. It takes discipline about outcomes, seams, routing, data and testing, and it is not right for every system.
If you are weighing a modernisation, our SaaS modernisation and cloud deployment page explains how we approach it, and you can contact us to talk through a specific system.
Frequently asked questions
- What is the strangler fig pattern?
- It is an incremental way to migrate a legacy system. You put a façade in front of the old system, replace specific pieces of functionality with new services one at a time, and route requests to the new pieces as they are ready. When nothing depends on the old system, you decommission it. Microsoft describes it this way in its Azure Architecture Center, and Martin Fowler popularised the metaphor.
- Why not just rewrite the whole thing?
- Martin Fowler writes that replacements seem easy to specify but it is often hard to work out the details of existing behaviour, and that much of that behaviour is not really wanted. He says the simple-sounding plan has gone down in flames most of the time. That is his experience, not a measured statistic.
- When is a full rewrite reasonable?
- Microsoft's guidance says the strangler fig pattern may not suit a small system that is simple to replace, a system whose requests cannot be intercepted, one where you cannot access the legacy source code, or a case where the original must be decommissioned quickly. In those cases a rewrite or a different approach may be sensible.
- How long does it take?
- It depends on the size of the system and how cleanly it can be split. We will not quote a typical duration because none of the sources we checked give one, and it varies widely. A useful early outcome is a first migrated piece in production, which tells you far more about timing than an upfront estimate.
- Does the new system need to be built with Next.js?
- No. The pattern is technology-neutral. Next.js is one option, and its documentation describes using rewrites to fall back to an existing site while pages are migrated, which suits the front end of a web application.
Sources and further reading
Facts, prices and dates in this article were checked against these pages on the dates shown. Vendor prices and regulations change, so check the source before you rely on a figure.
- Strangler Fig pattern · Microsoft Learn, Azure Architecture Center · accessed 10 October 2026
- Strangler Fig · Martin Fowler · accessed 10 October 2026
- next.config.js: rewrites · Next.js documentation · accessed 10 October 2026