Migration writer
Skill imtiazrayhan/agentscamp-library/skills/migration-writer
Write a safe, reversible, zero-downtime database migration using expand-contract — add the new shape, backfill in batches, switch reads/writes, then drop the old — so every deploy stays compatible with the running app version. Use when adding or changing schema on a live system, renaming/dropping a column, adding NOT NULL or a foreign key on a large table, or when a migration risks locks, table rewrites, or an unrevertable step.From its SKILL.md
npx -y skills add imtiazrayhan/agentscamp-library --skill migration-writerAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.6 KB, ~1.2k tokens by cl100k_base, as published. Nobody here has run it
Most schema migrations break production not because the SQL is wrong but because they assume the database and the app flip over in one atomic instant. During a rolling deploy, old and new code run at the same time against one schema — so a migration that the new code needs will crash the old code, and a rollback that the old code needs is gone the moment you DROP. This skill writes migrations the expand-contract way: each step is independently deployable against the version before and after it, every change has a real down, and no step takes a lock that blocks writes on a hot table.
When to use this skill
- Adding, renaming, dropping, or retyping a column on a table that a live app reads/writes.
- Adding
NOT NULL, aCHECK, a foreign key, or a unique constraint to a table with existing rows. - Creating an index on a large/busy table, or backfilling a new column across millions of rows.
- Splitting/merging tables, moving a column, or any change where old and new app code must coexist during the deploy.
Instructions
- Decide the expand-contract phases first, before writing SQL. A column rename
a → bis not one migration; it is: (1) addbnullable, (2) dual-writeaandbin app code, (3) backfillbfroma, (4) switch reads tob, (5) stop writinga, (6) dropa. Each phase ships and is safe to roll back to the phase before it. Name the phases explicitly in the output, mapped to app deploys. - Make additive changes nullable / without a default rewrite.
ADD COLUMN ... NULLis instant. Adding a column with a non-constant default (or, on old engines, any default) rewrites the table under a lock — split it into add-nullable, then backfill, then set default for future rows. - Add
NOT NULLandCHECKwithout a blocking scan. On Postgres:ADD CONSTRAINT ... CHECK (...) NOT VALID, thenVALIDATE CONSTRAINT(takes only aSHARE UPDATE EXCLUSIVElock, doesn't block writes). ForNOT NULL, add the validatedCHECK (col IS NOT NULL)first, then promote — neverSET NOT NULLcold on a big table, which full-scans under anACCESS EXCLUSIVElock. - Build indexes and FKs concurrently / unvalidated.
CREATE INDEX CONCURRENTLY(andDROP INDEX CONCURRENTLY) so writes keep flowing; add foreign keys asNOT VALIDthenVALIDATE CONSTRAINTin a second step. Concurrent index builds run outside a transaction — keep them in their own migration with no other statements. - Backfill in bounded batches, never one transaction. Update in chunks (e.g.
WHERE id BETWEEN ...orLIMIT nloops) committing each batch, with a short sleep between batches to spare replication and locks. Keep the backfill in a separate migration/job from the schema DDL so a slow backfill can't hold a DDL lock and a failed batch doesn't roll back the whole table. - Write a real
downfor everyup. The down must actually reverse the change (drop the added column/index/constraint), or, where reversal loses data (a dropped column, a narrowed type), say so loudly and add an export/backup step to the up rather than pretending it's reversible. - State the deploy ordering contract. For each migration, note which app version it requires and which it must remain compatible with: backward-compatible (expand) migrations run before the code that needs them; destructive (contract) migrations run after all code that used the old shape is fully rolled out and confirmed.
[!WARNING] A single-transaction backfill (
UPDATE big_table SET ...with no batching) holds row locks on every touched row until commit, bloats WAL, can deadlock with live traffic, and on failure rolls back hours of work. Always batch and commit; treat any unboundedUPDATE/DELETEon a large table as a production incident waiting to happen.
[!WARNING] Type changes that rewrite the table (
ALTER COLUMN ... TYPEbetween incompatible types, e.g.int → biginton older Postgres) take anACCESS EXCLUSIVElock and block all reads and writes for the duration. Prefer expand-contract: add a new column of the target type, backfill, switch over, drop the old — never an in-place rewrite on a hot table.
[!NOTE] Don't take
ACCESS EXCLUSIVEDDL withlock_timeout = 0. Set a shortlock_timeout(e.g.5s) so a migration that can't grab its lock fails fast and retries, instead of queueing behind a long query and stalling every write that piles up behind it.
Output
For the requested change, produce:
- The
upanddownmigration — split into separate files per expand-contract phase, withCONCURRENTLY/NOT VALID/VALIDATEused where they avoid blocking locks. - The backfill + rollout sequence — the ordered phases (add → dual-write → backfill → switch reads → stop old writes → drop), each tagged with the app deploy it pairs with, and the batched backfill loop as a separate step.
- Locking & risk notes — for each statement: the lock it takes, whether it blocks reads/writes, whether it rewrites the table, and whether the
downis lossless — with destructive/irreversible steps called out explicitly.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.