Platform
Migrations on systems that cannot stop
Expand, migrate, contract. Three deploys instead of one, and every intermediate state is a state you could stay in indefinitely.

A schema change that requires downtime is a schema change that requires a meeting, a maintenance window, an out of hours deploy and a rollback plan nobody has rehearsed. It is cheaper to do it in three steps that each require none of those things.
Expand
Add the new column, table or index. Do not remove anything and do not make it required. The old code keeps working because nothing it depends on has changed. Deploy this on a Tuesday afternoon like any other change.
Migrate
Write to both shapes and backfill the old rows in batches small enough that no single batch holds a lock long enough to matter. Read from the old shape still, but compare against the new one and log the differences. That comparison is the whole point: it tells you the backfill is correct before you depend on it.
Stay here as long as you need. Days is normal. This is a stable state, not a transition.
Contract
Once reads have moved and the difference log has been quiet for long enough to believe it, stop writing the old shape. Then, in a later change, drop it.
Why the separation matters
- Every deploy is independently reversible, because no deploy removes something the previous version needs.
- The risky part, the backfill, runs on its own schedule rather than inside a deploy window.
- You find out the migration is wrong while both shapes still exist.
- Nobody has to be awake at four in the morning.
The cost is three changes instead of one, and a period where the schema is untidy. That is a very cheap price for never having to write the sentence "the system will be unavailable".