C.W.K.
Stream
Lesson 03 of 10 · published

Migrations: Schema Evolution

~14 min · apps, migrations

Level 0Schema Seedling
0 XP0/86 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

Migrations are your schema's source of truth

A migration is a versioned, reproducible change to the schema. 20260503_add_user_avatar.sql in your repo. Run it once on dev, once on staging, once on prod — same change, same order, every environment.

The mandatory rules

  1. Every schema change is a migration. No "I just ran an ALTER directly on prod." That breaks the reproducibility chain forever.
  2. Migrations are immutable once shipped. If you need to fix a migration that already ran in production, write a new migration that fixes it. Never edit an applied migration.
  3. Test migrations forward and back. Most tools support a down() direction. Use it on dev; trust forward-only on prod.
  4. Make destructive migrations multi-step. Renaming a column = (1) add new column, (2) backfill, (3) start writing to both, (4) cut over reads, (5) drop old column. Across deploys.

The tool zoo

Alembic (Python/SQLAlchemy), Flyway (JVM, language-agnostic), goose (Go), Prisma Migrate (Node/TypeScript), supabase migration / db push (Supabase), graphile-migrate (Postgres-native). Pick one per repo, never two.

Code

Alembic migration·python
# alembic/versions/20260503_add_user_avatar.py
def upgrade():
    op.add_column('users', sa.Column('avatar_url', sa.Text(), nullable=True))
    op.execute("UPDATE users SET avatar_url = 'https://...' WHERE id = 1")

def downgrade():
    op.drop_column('users', 'avatar_url')
Plain SQL migration (Flyway / supabase)·sql
-- migrations/20260503120000_add_user_avatar.sql
ALTER TABLE users ADD COLUMN avatar_url TEXT;
UPDATE users SET avatar_url = '/avatars/default.png' WHERE avatar_url IS NULL;
ALTER TABLE users ALTER COLUMN avatar_url SET NOT NULL;
Supabase CLI flow·bash
supabase migration new add_user_avatar
# edit the generated migration file
supabase db push --include-all

External links

Exercise

In a sandbox, generate a migration for a small schema change with whichever tool your project uses. Apply it. Generate a second migration that depends on the first. Apply it. Then practice 'oops, fix forward': write a third migration that corrects something the first one got wrong.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.