← Back to all products

Database Migration Toolkit

$39

Schema migration frameworks, data migration scripts, zero-downtime migration patterns, and rollback procedures.

📁 30 files
MarkdownPythonSQLPostgreSQL

📄 Product Preview

Try the interactive reader and demo tools below, or get the full product with all content unlocked.

📖 Interactive Reader (Free Preview) ⚙ Try Demo Tools 📦 Download Free Sample

📁 File Structure 30 files

database-migration-toolkit/ ├── LICENSE ├── README.md ├── checklists/ │ └── migration-review-checklist.md ├── docs/ │ ├── cross-engine-checklist.md │ ├── data-backfill.md │ ├── online-schema-change.md │ ├── rollback-playbook.md │ ├── versioned-migrations.md │ └── zero-downtime-expand-contract.md ├── examples/ │ ├── backfill_batched.sql │ └── postgres_expand_contract/ │ ├── 00_setup.sql │ ├── 01_expand.sql │ ├── 02_backfill.sql │ ├── 03_contract.sql │ └── README.md ├── free-sample.zip ├── guide/ │ ├── 01_why-this-exists.md │ ├── 02_requirements.md │ ├── 03_cli-reference.md │ └── 04_license.md ├── index.html ├── migrations/ │ ├── V001__create_core_schema.down.sql │ ├── V001__create_core_schema.up.sql │ ├── V002__add_indexes.down.sql │ ├── V002__add_indexes.up.sql │ ├── V003__expand_split_customer_name.down.sql │ ├── V003__expand_split_customer_name.up.sql │ ├── V004__contract_drop_full_name.down.sql │ └── V004__contract_drop_full_name.up.sql └── runner/ └── migrate.py

📖 Documentation Preview README excerpt

Database Migration Toolkit

A dependency-free, runnable toolkit for changing database schemas the safe way:

versioned up/down migrations, a tiny SQL runner you can actually read, and the

expand/contract patterns that let you ship schema changes to a busy database **without

downtime**. Everything here runs on the Python standard library and SQLite out of the box —

no pip install, no services to start — and the same patterns scale up to production

PostgreSQL and MySQL.

This is not another opaque migration framework. It's a small runner plus a set of worked,

proven examples and field-tested documentation, so you can learn the patterns and lift

the code.

Why this exists

The SQL in a schema migration is rarely the hard part. The hard parts are:

  • Two code versions hit the database at once during a rolling deploy — the schema has

to be valid for both.

  • A single ALTER or UPDATE can lock or rewrite a huge table and stall production.
  • Rolling back is often impossible (data's gone) or unsafe (DDL auto-commits on MySQL).
  • Teams collide on version numbers and silently edit already-applied migrations.

This toolkit gives you a concrete, working model for all four, with the trade-offs written

down.

Features

  • runner/migrate.py — a ~480-line, stdlib-only migration runner: ordered

up/down, a schema_migrations tracking table, SHA-256 checksums that detect a

changed-after-apply migration, status, validate, and a new scaffolder. Each

migration runs in one transaction.

  • Drives 3 engines through one thin seam: SQLite with zero setup (default), plus

PostgreSQL and MySQL by passing a --url (install that driver).

  • A proven expand/contract example — four bundled migrations that split a full_name

column into first_name/last_name with zero downtime, fully reversible end to end.

  • Production PostgreSQL DDL — the same change with CREATE INDEX CONCURRENTLY-class

concerns, a transition trigger, a batched backfill procedure, and the lock-light

NOT NULL technique.

  • A reusable batched-backfill template (PostgreSQL + MySQL) using keyset pagination.
  • Six field guides covering versioning, zero-downtime expand/contract, online schema

change per engine, the rollback playbook, large-table backfills, and a cross-engine

cheat sheet — plus a reviewer's checklist for migration PRs.

What's inside


database-migration-toolkit/
├── README.md
├── LICENSE
├── runner/
│   └── migrate.py                       # the stdlib migration runner (CLI)
├── migrations/                          # runnable on SQLite with zero setup
│   ├── V001__create_core_schema.up.sql / .down.sql
│   ├── V002__add_indexes.up.sql / .down.sql
│   ├── V003__expand_split_customer_name.up.sql / .down.sql   # EXPAND
│   └── V004__contract_drop_full_name.up.sql / .down.sql      # CONTRACT
├── examples/
│   ├── backfill_batched.sql             # reusable keyset-batched backfill (PG + MySQL)
│   └── postgres_expand_contract/        # the production-grade PostgreSQL version

*... continues with setup instructions, usage examples, and more.*

📄 Code Sample .sql preview

examples/backfill_batched.sql-- backfill_batched.sql -- a reusable, copy-paste template for backfilling a large -- column in bounded batches instead of one table-locking UPDATE. -- -- WHY: `UPDATE big_table SET new_col = ...` with no bound rewrites every row in a -- single transaction. On a busy table that means a long lock, gigabytes of WAL / -- redo, table + index bloat, and replication lag spikes. Paging through the table -- in small committed chunks keeps each transaction short and cancellable. -- -- The key technique is KEYSET PAGINATION: walk forward by primary key -- (WHERE id > :last_id ORDER BY id LIMIT :n) instead of OFFSET, which gets slower -- on every page. Below are correct variants for PostgreSQL and MySQL. Adapt the -- table/column names and the expression that computes the new value. -- =========================================================================== -- --------------------------------------------------------------------------- -- PostgreSQL (11+) -- a procedure so we can COMMIT between batches. -- --------------------------------------------------------------------------- CREATE OR REPLACE PROCEDURE backfill_in_batches(batch_size int DEFAULT 5000) LANGUAGE plpgsql AS $$ DECLARE last_id bigint := 0; max_id bigint; updated int; BEGIN SELECT max(id) INTO max_id FROM big_table; WHILE last_id < max_id LOOP UPDATE big_table SET new_col = /* <-- your expression, e.g. */ upper(old_col) WHERE id > last_id AND id <= last_id + batch_size AND new_col IS NULL; -- only untouched rows -> safe to re-run GET DIAGNOSTICS updated = ROW_COUNT; last_id := last_id + batch_size; -- advance the keyset window RAISE NOTICE 'window <= %, updated % rows', last_id, updated; COMMIT; -- release locks; let autovacuum catch up PERFORM pg_sleep(0.05); -- throttle; raise if replicas fall behind
Buy Now — $39 Back to Products