~/iflux/thinking/migration-checklist.md
data-migration2026-06-019 min read

The 22-question checklist
we run before touching a single byte.

Before we move a single byte, we answer 22 questions. Here's why the boring checklist keeps migrations off the incident channel.

flux/dk
lead technical consultant, iflux

Every migration I've run that went smoothly had one thing in common: we answered a specific, unglamorous set of questions before moving a single byte. Every one that went sideways, mine or a client's system I got called in to fix afterward, had skipped some of them. What's actually on that list, and why does something so boring make the difference between a quiet weekend and a 3am rollback?

01  ·  Define done and wrong before you're under pressure to guess

The single most common root cause I see isn't a bad migration script. It's a team that reached the cutover without a precise, pre-agreed definition of what "it worked" means and what triggers a rollback. Deciding those thresholds live, mid-migration, with a stakeholder asking for a status update, is how a borderline call becomes a permanent mistake.

“If you can't answer 'how will we know this is wrong' before you start, you'll answer it in production instead, and that answer costs a lot more.”

02  ·  The questions, grouped by what they're actually protecting

The full checklist runs to 22 items because migrations fail in specific, recurring ways, and each question exists to close one of them. Grouped by category, here is the core of it, the dozen or so that catch the most:

03  ·  Data integrity

  1. 01What's the reconciliation query, and who runs it? Row counts, checksums, and a spot-check of high-value records, agreed and written down before cutover, not improvised after.
  2. 02What does "acceptable drift" mean, numerically? Some divergence during a live cutover is often fine. "Fine" needs a number, not a feeling, decided in advance.
  3. 03Can we replay the migration from any point without duplicating data? Idempotency isn't optional for anything that might need a partial re-run at 2am.

04  ·  Reversibility

  1. 01Is there a tested rollback path, or just a theoretical one? "We could roll back" and "we have rolled back, in staging, and timed it" are different sentences. Only one of them is a plan.
  2. 02Can we dual-write during the transition? Writing to both old and new systems during cutover turns an all-or-nothing leap into a reversible window.
  3. 03Is the backfill re-runnable without side effects? A backfill that can safely run twice removes an entire category of "we can't try again" panic.

05  ·  Timing and blast radius

  1. 01What actually depends on this data, beyond the obvious consumer? The dependency nobody remembered is the one that breaks. This question alone has saved more migrations than any script.
  2. 02What's the maintenance window, and what happens if we overrun it? A window with no defined overrun plan turns a delay into an improvised decision made by whoever's still awake.
migrate: reconcile.sh
$ reconcile --source=legacy --target=new --sample=full
row count legacy: 4,812,004 new: 4,812,004 ✓
checksum legacy: a3f9e2.. new: a3f9e2.. ✓
high-value spot check (n=500) ..... 500/500 match ✓
drift outside tolerance ........... 0 records
# this is what green actually looks like. not a vibe.

06  ·  Ownership

The last category isn't a question about the data at all, it's about who's around when the data misbehaves. Who signs off that the migration is complete? Who's on call during the cutover window, specifically, by name, not "the team"? And critically: does that person have everything they need to fix or roll back without waking up whoever wrote the migration script? If the answer is no, the plan has a single point of failure that happens to be a person's sleep schedule.

// the 3am test: if this breaks at 3am, can the on-call engineer fix it or roll it back using only the runbook, without calling anyone? if the honest answer is no, the migration isn't ready, regardless of how confident the script looks in a dry run.

07  ·  Why the boring version wins

None of these 22 questions are interesting to answer. That's precisely the point. A migration that generates a war story is a migration where someone skipped a boring question earlier in the week. The checklist trades a small amount of upfront tedium for the near-certainty of an uneventful cutover, and an uneventful cutover is the actual goal, even though it makes for a worse story afterward.

$ end of file · written by flux/dk · 2026-06-01

// about the author

flux/dk: lead technical consultant

The engineer clients call once a system is already in trouble. Fifteen-plus years untangling data pipelines, test suites, and migrations that went sideways somewhere upstream of where anyone was looking.

_next-file

Every legacy modernization plan fails the same way. Here's the plan that doesn't.

flux/dk · 2026-05-25 · 11 min read