Schema Migrations
Prerequisites: Creating tables, Transactions
Applications evolve, and so do their databases: new tables, new columns, renamed fields. A migration is a versioned script that changes the schema in a controlled, repeatable way.
Why migrations
- Every environment — your laptop, colleagues' machines, test, production — gets the same schema changes in the same order.
- Changes are stored in version control alongside the code that needs them.
- The database records which migrations have run, so each runs exactly once.
Migration tools
Most frameworks include one — Laravel migrations, Django migrations, Rails Active Record migrations, Entity Framework migrations, Prisma Migrate — and standalone tools such as Flyway, Liquibase, golang-migrate, and Alembic work with any stack. A plain-SQL migration might look like:
-- migrations/0007_add_phone_to_customers.up.sql
ALTER TABLE customers ADD COLUMN phone TEXT;
-- migrations/0007_add_phone_to_customers.down.sql
ALTER TABLE customers DROP COLUMN phone;
The "down" script reverses the change, for rolling back.
Changing live databases safely
On a busy production database, some changes lock tables or break the running application. Safe patterns:
Adding a required column — in steps:
- Add the column as nullable (fast, non-blocking).
- Deploy code that writes the new column.
- Backfill existing rows in batches.
- Add the
NOT NULLconstraint.
Renaming a column — "expand and contract":
- Add the new column; write to both old and new.
- Backfill; switch reads to the new column.
- Stop writing the old column, then drop it in a later release.
Renaming directly breaks every running instance of the old code the moment the migration runs.
Adding an index on a large table — use non-blocking options where
available (CREATE INDEX CONCURRENTLY in PostgreSQL; online DDL in
MySQL).
Good habits
- Keep each migration small and focused, and never edit a migration that has already run in production — write a new one.
- Test migrations against a copy of production-sized data before running them for real.
- Take a backup before significant changes, and know how you'd roll back.
- Run migrations in a transaction where the database supports transactional DDL (PostgreSQL and SQLite do; MySQL commits DDL immediately).