Every migration we run follows one rule: the cut-over is the least interesting moment of the project.
The rehearsal loop
- Snapshot production into a staging estate nightly.
- Replay the migration script against it — timed, measured, diffed.
- Reconcile: row counts, valuation totals, VAT indexes, sequence gaps.
- Only when three consecutive rehearsals pass clean do we schedule the window.
A migration that has not survived a dry run is not a migration. It is a hope.
Dual-run, not big-bang
Writes go to both estates for a bounded period; reads switch per-module behind a flag. Any divergence pages an engineer — the same engineer who wrote the mapper.
with env.cr.savepoint():
env["stock.quant"]._rebuild_valuation()
The result: cut-overs that take minutes, and a rollback path that is a flag flip, not a restore.