← all insights

#legacy-migration

A migration is a data migration with code along for the ride

2026-08-07

Teams plan a migration around the new framework, the fresh codebase, the shiny interfaces. That is the easy part. The part that actually goes wrong is the data. Every customer, order, and setting has to survive the move intact, and legacy data is rarely clean enough to make that trivial.

The code is the easy part

You can rewrite logic, reimplement features, and even change the stack. But data is the accumulated truth of years of decisions, some of them bad. If you cannot move it faithfully, you have not migrated anything, you have just built a new system next to an old one.

Dual-write and replay

  • Run the new system alongside the old one, writing to both, until you trust it.
  • Replay old records into the new system to backfill, then reconcile any drift.
  • Expect dirty data: nulls, duplicates, and orphaned rows. Write fixes for them up front.
  • Compare counts and checksums before cutover, and be ready to re-run until they match.
-- A reconciliation check that proves parity before cutover
SELECT count(*)             AS old_rows,
       count(new_table.id)  AS new_rows,
       count(*) - count(new_table.id) AS missing
FROM legacy.orders AS old_table
LEFT JOIN new.orders AS new_table
  ON new_table.external_id = old_table.id;

Treat the data as the source of truth for the whole project. If you can move it faithfully and prove it, the code will follow along easily. That is where a migration is actually won.