On this page
- The five phases
- Phase one — Lock the source
- Phase two — Round-trip every record class
- Phase three — Dual-run for a defined window
- Phase four — Go-live with rollback rehearsed
- Phase five — Read-only retention
- The class-by-class checklist
- Money
- Audit
- Attachments
- Custom code
- What we don’t do
- The takeaway
Playbook
Dylan Karaitiana 26 Mar 2026 4 min read Migrations break in predictable ways. We use the same playbook for every transition — site relaunch, CRM swap, ERP go-live, hosting move. The pattern doesn’t change much because the failure modes don’t.
The five phases
Phase one — Lock the source
Before anything moves, the source system is locked. Schema-level lock: no migrations, no new columns, no new integrations during the migration window. Data-level lock: writes either continue under freeze rules (well-documented and time-bounded) or pause entirely. Integration lock: no new third-party connections to the source.
If the source is moving while we’re migrating, every test we run against it is invalidated by the next change. We’ve seen migrations fail because someone enabled a new Salesforce field two days before go-live and the field wasn’t mapped in the destination. The lock prevents this.
The lock includes a documented exception process. Sometimes a critical fix really does need to ship during migration. The exception process means the change is reviewed against the migration plan and either accommodated explicitly or postponed.
Phase two — Round-trip every record class
Not a sample. Every class. Customers, orders, invoices, tax records, audit logs, attachments, notes, custom objects, every relationship between them. We define a parity test per class — the test passes when the record loads correctly into the destination, all relationships resolve, and any computed fields match expected values.
The round-trip is the part that catches the silent failures. Most migrations pass spot-checks because nobody runs every record class. Then production goes live and the orders class has 0.3% records with a missing field, and the operator’s accounts team notices a week later when they reconcile to the bank.
Phase three — Dual-run for a defined window
Both systems live, writes mirrored, reads from the old system, dashboards comparing record counts hourly. The window is two to fourteen days depending on the asset shape. CRM migrations get fourteen because the relationship graph is dense; site relaunches get two because the data shape is shallower.
The dual-run catches the silent migration failures — the ones where data made it across but in the wrong shape. A field that was a string in the source and an enum in the destination might import “successfully” but with values that don’t validate. The dual-run dashboard surfaces this within hours.
Phase four — Go-live with rollback rehearsed
See the go-live playbook for the rules. Migration go-lives add one specific consideration: rollback for a migration usually means reverting writes, not reverting code. Both directions need to be rehearsed.
For most migrations, go-live means switching reads to the destination while keeping writes mirrored for a final tail-window. After 24-48 hours of clean operation, writes get redirected to the destination only and the source goes read-only.
Phase five — Read-only retention
Keep the old system in read-only for ninety days minimum. Operators always need to look up something old; cutting too aggressively burns goodwill and sometimes burns audit obligations.
Ninety days is our default, longer for regulated environments. The retention period gets agreed before go-live and documented in the closeout.
The class-by-class checklist
Some classes need extra attention.
Money
Anything financial — invoices, payments, refunds, tax records — gets reconciled to the bank for the migration window. We run a daily total comparison between source and destination during dual-run and post-go-live, and we don’t close the project until 30 days of clean reconciliation has passed.
Audit
Audit log migration is its own sub-project. Audit logs are usually append-only and chronological; the migration must preserve both properties. We’ve seen audit logs migrated with timezone shifts that broke regulatory reporting two months later. We always test timezone handling explicitly.
Attachments
Files attached to records are easy to forget. We always inventory the attachment storage explicitly, migrate it as a separate stream with checksums, and verify checksums post-migration. Lost or corrupted attachments are usually noticed by customers, not by the operator’s team, which is the wrong way to find out.
Custom code
Triggers, scripts, plugins, integrations. These don’t migrate as data — they migrate as code, and they need to be re-implemented (not transferred) on the destination. We inventory every custom code path during phase one and rebuild during phase two. This is where most “migration project” overruns originate, so we scope it explicitly.
What we don’t do
A few practices we deliberately reject.
We don’t promise zero-downtime for migrations involving complex relationship graphs. Some downtime windows are honest — fighting them with elaborate dual-write architectures often introduces more risk than the downtime itself. We’re transparent with the operator about which transitions need a window and why.
We don’t migrate without freezing the source. If the operator can’t lock the source for the migration window, we postpone. The migration won’t succeed under writes-during-migrate conditions for any non-trivial system.
We don’t accept “the data is fine” without parity tests. Operators sometimes want to skip the round-trip step because their team has been using the source system for years and “the data is fine.” It usually isn’t fine. The round-trip surfaces issues that have been latent for years.
The takeaway
Five phases, every migration, no exceptions. Lock, round-trip, dual-run, go live, retain. The discipline is in not skipping phase three under pressure to deliver. We’ve never regretted holding the dual-run window; we have regretted shortening it.