Every migration programme reaches the same conversation. Usually around the time the cutover plan is approved and someone realises there will be a window.
“If we’re going to take downtime anyway, let’s do the improvements as well.”
It sounds efficient. It is the statement I push back on hardest on any migration or modernisation project, and I’ve never regretted it. You can use the same downtime. You can spend the same error budget. You cannot mix the two changes.
Why the instinct is wrong
A migration and an improvement are validated by opposite things.
A migration is validated by sameness. The system runs somewhere new and behaves exactly as it did before. Every check in the cutover plan is a comparison against the old behaviour: same responses, same throughput, same data, same failure modes. When something differs, that difference is the bug.
An improvement is validated by difference. You changed the system on purpose and you are checking that the new behaviour is the one you wanted.
Put both in one change and you have destroyed your own validation. Something looks different after cutover. Is it the new platform, or the new code? You can’t tell, and the clock is running inside the window. Rollback is worse: rolling back the migration takes the improvement with it, and rolling back the improvement may not be possible on the new platform at all. Two suspects for every failure, two victims for every rollback.
What “isolated in process” means
The rule I hold is that the migration is isolated from the improvement in process, not necessarily in time.
They can sit on the same day. They can share the window and the error budget you negotiated for it. What they cannot share is the change itself: one plan, one validation, one rollback per change.
In practice that means:
- The migration goes first and is validated on its own terms, against the old behaviour, with the rollback path still open.
- Only when the migration is declared good does the improvement start, as its own change, with its own validation and its own rollback, inside whatever window remains.
- If the migration eats the window, the improvement waits. It does not get squeezed in because everyone is already awake.
This is the discipline behind zero unplanned downtime across every migration I’ve led. Not because nothing went wrong, but because when something did, there was exactly one thing that could have caused it and exactly one thing to roll back.
Where the line actually sits
The honest question is what counts as an improvement. My line: anything that changes what the system does, rather than where it runs.
Choosing the right instance size in the target, or a managed database where the source ran a self-hosted one, is part of the migration’s design. The user sees the same behaviour. At Koo we moved 40+ application services, 48 ML services and 21 databases from AWS to GCP inside a fixed four-month window with operational cost optimised alongside performance. That optimisation lived in the target design, not in the cutover. Nothing the application did changed on the day.
A new application version, a schema change, a refactor that was “waiting for a quiet moment,” a feature the product team has been holding: those change behaviour. They are improvements. They get their own change.
The migration that made this obvious
On an AWS migration programme for an urban and infrastructure consultancy in Singapore, the applications were legacy and their dependencies undocumented. We mapped the dependencies from live network traffic and migrated by staged traffic shifting, 10%, 25%, 75%, then 100%, validating at each step with rollback open.
Staged shifting only works if the thing you are shifting traffic onto is the same thing. Every stage is a comparison: does the 10% on the new platform behave like the 90% on the old? Introduce an improvement into that and the comparison is meaningless. You would be shifting traffic onto something different and calling the difference a migration issue, or a migration issue an improvement. The method itself forbids mixing.
Why customers keep asking
Because the window is expensive. Getting downtime approved is hard, and the instinct to amortise it across every pending change is a reasonable one.
The answer isn’t to refuse the improvements. It’s to give them the window without giving them the change. Sequence them, validate each on its own terms, and let the migration’s rollback stay clean. The customer still gets both done on the same night. They just get to know, at every point, which of the two they are looking at.
Share the window. Share the budget. Never share the change.