Home / Insights / Migration and Improvement Can Share a Window, Not a Change
Migration Strategy 4 min read

Migration and Improvement Can Share a Window, Not a Change

Every migration customer eventually says it: if we're taking downtime anyway, let's fix a few things while we're in there. I say no every time. You can share the window and the error budget. You cannot share the change.

migrationmodernizationcutoverrollbackchange-managementzero-downtime

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.

Common questions

Short, direct answers to what people actually ask about this.

Should you modernise an application during its cloud migration?

Not in the same change. A migration is validated by sameness: the system behaves as before, in a new place. An improvement is validated by difference. Combine them and a failure has two suspects and a rollback has two victims. Run them as separate changes, even if they land on the same day.

Can a migration and an improvement use the same downtime window?

Yes. The window and the error budget are shared resources; the change is not. Sequence the migration first, validate it against the old behaviour, hold the rollback open, then run the improvement as its own change with its own validation and its own rollback inside the same window.

What counts as improvement versus migration?

Anything that changes what the system does, rather than where it runs, is an improvement: a new version, a schema change, a refactor, a feature. Choosing the right instance size or a managed equivalent in the target is part of the migration's design, because the behaviour the user sees stays the same.

Discussion