The client's booking system ran on a database that had been end-of-lifed by its vendor, on a schema that had accumulated a decade of undocumented triggers. It could not go offline for a migration weekend, because weekends were when it took the most bookings.
Why "just switch it over" was never an option
The obvious plan — build the new system, cut over one night, done — assumes you can freeze writes during the cutover. This system had customers actively booking around the clock across several time zones, and a frozen write window of even twenty minutes would have meant lost business the client could point to directly. The plan had to keep both databases genuinely live during the transition.
Dual writes, then dual reads, then one of each
- Phase one — every write goes to the old database as before, and is also written to the new one, with a background job reconciling anything that drifts.
- Phase two — reads move to the new database for an increasing share of traffic, with a fallback to the old one on any discrepancy, logged rather than silently swallowed.
- Phase three — writes stop going to the old database once reads have run cleanly off the new one for a full billing cycle, so any monthly-only process gets exercised before the old system is retired.
The undocumented triggers were the real risk
The legacy database had triggers that recalculated a loyalty balance on certain writes, discovered only because the new system's numbers quietly diverged from the old one during phase one. Nobody at the client remembered these existed. Dual writes surfaced the discrepancy within days rather than after cutover, which is the entire argument for running the two systems in parallel rather than trusting a migration script and a prayer.
A migration that cannot be observed running in parallel is a migration you are trusting rather than verifying.
How long this actually took
Four months from first dual write to retiring the old database, considerably longer than a cutover weekend would have taken on paper. The client's booking system had zero unplanned downtime attributable to the migration in that period, which was the number that mattered to them far more than the calendar time.
What we would tell anyone about to do this
Budget for the reconciliation job to find something you didn't know about. It always does, and finding it during a quiet dual-write phase is a non-event; finding it after the old system is gone is an incident.
