The donor journey needed to get noticeably better on a website that was not going to be replaced. A full rebuild had already been proposed and turned down. For a live organisation taking real gifts, that refusal was not timidity. A rebuild puts all of the organisation's giving on one cutover, and if the cutover goes wrong the cost is not a delayed project. It is a donate page that fails in the middle of an appeal, and gifts that simply never happen.
A strangler migration grows the new system around the old one until the old one has nothing left to do.
The name comes from the strangler fig, a plant that grows around an existing tree and gradually takes over its place until the tree inside is no longer needed. In software it describes replacing a system piece by piece while it keeps running. Something sits in front of the old system and sees every request. For most requests it does nothing and passes them through. For requests that belong to a piece that has been rebuilt, it sends them to the new code. Over time more requests go to the new code, and eventually the old system receives none.
The routing layer is what makes this safe. Moving a path from old to new is a change to a routing rule, not the launch of a whole new site. Moving it back is the same change in reverse. So each step can be small, can be tried with real traffic, and can be undone in the time it takes to change a rule. A full rebuild works the other way round. The new site goes live all at once on a cutover date, and the only way back from a serious problem is to switch the whole site back and lose everything that went live with it.
The price is a period in which two systems run at once. A donor might begin on an old page and finish on a new one. Whatever they carry across that boundary, the session they are in, the campaign that brought them, the amount they picked, has to survive the crossing. Reporting has to understand both sides. A change to something shared, such as the site header or a policy link, has to be made in two places. None of this is hard, but all of it has to be planned, because the seam is where incremental migrations go wrong.
A donation journey adds one more rule. The new paths must not bring their own payment handling. They sit on the same gift core as the old ones, so whether a gift came through an old page or a new one, it is confirmed, deduplicated and reconciled in exactly the same way. The migration changes what the donor sees. It leaves the guarantees about the money alone.
So the journey is replaced incrementally, behind the existing site, one path at a time, with the reliable gift core underneath every new path. The limit on the work was never technical skill. It was blast radius. At no point could the organisation be more than one path away from its last working state, and that rules out any approach whose value only arrives at the end.
Incremental migrations fail at the seam, or by never finishing.
The common failure is the boundary between old and new. A donor starts on an old page, follows a link to a new one, and arrives without the campaign they came from, or the amount they picked, or with a session the new code does not recognise. Each side works on its own and the crossing loses something. Tests usually exercise one side at a time, so the crossing is the least tested part of the system and one of the most travelled.
The other failure is a migration that stalls. The easy paths move early, the awkward ones are left, and the organisation ends up running two systems indefinitely, with the routing layer as permanent infrastructure nobody planned to own. A strangler migration needs a plain record of which paths have moved and which remain, or the halfway point quietly becomes the end state.
The reflex, and the fix.
Propose a full rebuild
It is the clean answer, and the existing site genuinely limits what the donor journey can become.
It was turned down, correctly, as too risky for a live organisation taking gifts. An approach that only pays off at cutover is also an approach that cannot be stopped halfway, and if it is stopped it leaves nothing of value behind.
Intercept and rebuild path by path
Two code paths living side by side during the migration, and a routing seam between them to design and look after.
A better donor journey arriving in pieces, the rest of the site untouched, and every piece individually reversible.
The organisation has to keep taking gifts on every day of the migration. A rebuild cannot promise that and path-by-path replacement can, so the journey was replaced one path at a time.
Interception at the level of a path
Requests for a donor path being replaced are routed to the rebuilt journey. Everything else reaches the existing site exactly as before, so the surface area of each change is the one path being replaced and nothing more.
The reliable gift core underneath
The rebuilt journey sits on the same webhook-confirmed, idempotent, reconciled gift core as everything else. Payment handling is not written a second time for the new path.
One path at a time, each reversible
Each path is cut over on its own and can be routed back. The organisation is never more than one step from a state it knows works.
A seam that is designed
Two code paths coexist while the migration runs. That is stated openly, and what happens at the boundary between them is designed in advance so that it is never discovered by a donor.
What each choice cost, and what it bought.
ARouting at the level of a path
The rebuilt journey is reached by intercepting specific donor paths in front of the existing site. That put a routing layer into every request, which is one more component to run and understand. It bought changes that are exactly one path wide, and a way back that is a rule change. Replacing pages in place on the existing site would have avoided the router, and it would have turned every rollback into an emergency redeploy.
BNo new payment handling on the new paths
The rebuilt journey sits on the existing gift core and does not touch payments itself. That meant the new journey had to live within the core's contract, and payments could not be redesigned in the middle of the migration. It also meant gift reliability was identical on both sides of the seam for the whole migration. Giving the new paths their own payment handling would have looked cleaner and created two definitions of a confirmed gift, one per side.
CReversibility as the unit of progress
A path only moved once it could be moved back. Each step therefore needed a working route back, and the old version of the path had to stay alive and correct until the new one had proven itself. That is slower than moving several paths at once. It is also the property that kept the organisation within one step of a known-good state throughout, which was the whole reason the rebuild had been refused.
DNaming the seam as a cost
The coexistence of two code paths was stated plainly from the start, and the behaviour at the boundary was designed and checked. That meant admitting the approach has a price, and spending effort on a boundary that will eventually disappear. The alternative, presenting incremental replacement as free, ends with donors discovering the seam one lost campaign tag at a time.
- Choose one donor pathnothing else on the site is in scope
- Rebuild it on the coreno new payment handling
- Route the path acrossa rule change, not a redeploy
- Check the boundary behaviourwhat crosses from old to new survives
- Route back if anything failsthe old path is still standing
A single headline number hides where a system fails. This work was scored on the dimensions that actually decide whether it holds in production, measured on real, held-out cases rather than the demo path.
No measured figures are published for this engagement. Outcomes are described qualitatively.
A rebuild proposal that is turned down, after which nothing changes at all. Or a rebuild that is accepted, and puts a live organisation's giving on a single cutover date.
A donor journey replaced path by path behind the existing site, each step reversible, all of it on one shared reliable gift core, with the migration seam designed and acknowledged.
What it owns, and what it hands to a person.
Incremental replacement limits the risk, and it means two code paths coexist for as long as the migration lasts. That seam has to be managed openly for its whole life. It is a genuine cost of not doing the rebuild, and it is presented here as one.
To improve a live system you are not allowed to replace, put a routing layer in front of it, rebuild one path at a time over a shared reliable core, and keep every step individually reversible. The old system stays standing as the fallback until the last path has moved.
If a live system cannot be replaced, it can usually be surrounded. Put a routing layer in front of it and move one path at a time.
Make each step of a migration reversible by a configuration change and not a redeploy. The ability to go back is what makes it safe to go forward.
The seam between old and new is where incremental migrations break. Decide in advance what a user carries across it, and test the crossing on purpose.
Do not let new paths reimplement something critical that the old ones share. Put them on the same core, so the migration changes the experience and leaves the guarantees alone.
Keep a written list of what has moved and what remains. Without one, the halfway point quietly becomes the permanent architecture.