Verified against Claude Code · 2026-08-08
Write a MIGRATION.md ledger Claude Code re-reads every session of a multi-week migration
A persistent rules-and-ledger file for long-running code migrations — the kind that outlives any single Claude Code session — tracking what has been moved, what is frozen, and how to verify each step before touching the next file.
The prompt
Ready to copy — highlighted parts are example details you can swap.
Write this as MIGRATION.md at the repository root. This file is not a one-off report — it is a living ledger a fresh Claude Code session will read at the start of every future sitting on this migration, possibly weeks apart, with no memory of earlier sessions beyond what is written here. Its job is to make a half-finished migration resumable by a session that has never seen it before. MIGRATION Express 4 to Fastify 4 on the internal billing API — moving from 12 Express route files under src/api/billing/, using Express-style middleware chains to Same 12 routes reimplemented as Fastify plugins with schema-based request validation. STRATEGY State the chosen approach explicitly — strangler-fig (old and new run side by side behind a seam until the old path is deleted) or big-bang (one atomic cutover) — and why: Strangler-fig — both servers run behind a feature-flagged router until every route is migrated and verified, since billing cannot tolerate a single atomic cutover risk. Never let a session default to whichever approach is easier for the file it happens to be looking at; the strategy is fixed for the whole migration. FREEZE LINE Name the exact boundary of what is in scope. Anything outside 12 Express route files under src/api/billing/, using Express-style middleware chains's stated boundary is frozen — do not refactor it, rename it, or 'improve it while I'm in there,' even if it looks related. A migration that scope-creeps into adjacent code is a migration that never finishes and is hard to review as one coherent change. LEDGER Maintain a table of every unit being migrated (file, module, or route) with three columns: status (pending / in progress / migrated / verified), the commit or PR that moved it, and any compatibility shim it still depends on. Update this table as the last step of every session, not as an afterthought — a session that migrates three files and forgets to update the ledger has made the next session's job harder than doing nothing at all. VERIFICATION PER UNIT A unit only moves to 'verified' after running npm run test:contract -- --grep billing (runs recorded-request contract tests against both old and new route for identical input) against it specifically, not just the full suite passing in aggregate. State what 'passing' means for this migration precisely — same output, same behavior under the old code path's known edge cases, or a named acceptance test — since 'the tests are green' can be true while the migration is still behaviorally wrong. ROLLBACK For each migrated-but-not-yet-verified unit, state how to revert it alone without reverting the whole migration. If no per-unit rollback is possible for this strategy, say so plainly rather than leaving it implied. HARD RULES - Never delete the old code path for a unit until its ledger row says verified. - Never migrate a unit not yet reached in Strangler-fig — both servers run behind a feature-flagged router until every route is migrated and verified, since billing cannot tolerate a single atomic cutover risk's intended order just because it looks quick. - Never mark a unit verified based on the migration compiling — compiling is not passing. WHEN A SESSION ENDS MID-MIGRATION Leave the ledger in a state where the very next row needing attention is unambiguous — the next session should be able to read this file top to bottom and know exactly where to resume without re-deriving it from a git log.
Customize
Optional — swap in your own details for the highlighted parts above.
Why this works
A migration that spans many Claude Code sessions has a failure mode that a single-session task does not: each new session starts with a fresh context window and no memory of what a previous session decided, so unless the state of the migration is written down somewhere Claude Code will read automatically, every session either re-derives the plan from scratch or, worse, silently assumes a different plan than the one actually in progress. The ledger table exists specifically to make 'where are we' a lookup instead of an investigation — a session that reads a table of pending/migrated/verified rows can pick up exactly where the last one stopped, whereas a session that has to infer progress from a git log or a half-remembered conversation is likely to either redo finished work or, more dangerously, assume something is done when it is only compiling. The distinction between migrated and verified is the single most load-bearing rule in the file, because a migrated-but-unverified unit that a later session assumes is safe to build on top of is exactly how a migration produces a regression that surfaces weeks after the fact, once nobody is looking at that code anymore. Fixing the strategy — strangler-fig versus big-bang — as a stated fact rather than a per-session judgment call matters because the two strategies imply opposite answers to questions like 'is it safe to delete the old path yet,' and an agent that re-decides the strategy file-by-file will produce an inconsistent migration that is neither fully incremental nor a clean cutover. The freeze-line rule targets a well-documented agentic-coding failure mode directly: an agent given latitude inside a file it is already touching tends to also fix nearby things it notices, which turns a reviewable, single-purpose migration diff into a sprawling change nobody can review with confidence, and is why the instruction states explicitly that adjacent, related-looking code stays untouched regardless of how easy the fix would be.
What you get back
## Ledger | Unit | Status | PR | Shim depended on | |---|---|---|---| | POST /invoices | verified | #412 | none | | GET /invoices/:id | migrated | #418 | legacy auth middleware wrapper | | POST /refunds | pending | — | — | Next session: GET /invoices/:id needs contract verification before its shim can be removed; POST /refunds has not been started.
Verified against
Claude Code Sonnet 4.6 · 2026-08-08
Changelog
- 2026-08-08 — Initial publish, verified against Claude Code Sonnet 4.6.
Building this for real?
This is a free starting point. If you'd rather have custom software built and running for your business, that's Scult's day job.
EXPLORE CUSTOM SOFTWARE
