alembic revision --autogenerate -m "describe change"
Developer guide
¶ Database and Migrations
All schema changes go through Alembic from apps/api. Keep one migration head, and review risky migrations against production-shaped data.
Workflow
¶ Generate, apply, check heads
alembic upgrade head
alembic heads
cd apps/api
alembic revision --autogenerate -m "describe change"
alembic upgrade head
alembic heads
Split-head prevention
¶ alembic heads must print exactly one line before release
If feature branches create multiple heads, merge them on development before promoting to main.
Feature branches each added a revision on top of the same parent. Not releasable yet.
development
Merge the heads on development, before anything is promoted.
Exactly one line. Safe to promote to main.
Adding columns
¶ Safe backfills, named constraints, one head
New columns should reach existing rows without a table rewrite, and every rename or renumber has to leave alembic heads printing one line.
Prefer ALTER TABLE ... ADD COLUMN IF NOT EXISTS ... DEFAULT '<const>' so existing rows are filled without a table rewrite on modern Postgres.
Follow with an explicit ADD CONSTRAINT where one is required, and name constraints so downgrades can drop them reliably.
Run the repo's single-head alembic test and update it if you renumber migrations. A split head blocks pre-deploy migrations and must be resolved before production rollout.
A migration adds organizations.logo_object_key VARCHAR(255) and organizations.logo_updated_at TIMESTAMPTZ, both nullable and with no default. The change is a catalog-only ALTER on PG11+ (no table rewrite or heavyweight lock), and NULL is the meaningful default for existing rows. The downgrade drops the columns; it intentionally does not attempt to delete objects from storage.
Deployment gate
¶ A green gate does not prove a migration is safe on real data
Review risky migrations against production-shaped data.