Alembic Migrations
This project uses async SQLAlchemy 2.0 + Alembic on PostgreSQL. Migrations live in backend/alembic/versions/ and are numbered (0001_…, 0002_…).
Workflow
Change the model first in
backend/app/db/models/(Mapped[...]+mapped_column(),__repr__, relationships withondelete="CASCADE"). Make sure the model is imported inbackend/app/db/models/__init__.pyso autogenerate sees it.Autogenerate the migration:
cd backend && uv run alembic revision --autogenerate -m "add <thing>" # or: make db-migrateALWAYS review the generated file. Autogenerate is a draft, not the truth:
- Confirm
upgrade()matches your intent anddowngrade()actually reverses it. - Check the
down_revisionchains onto the current head (uv run alembic heads). - Watch for dropped columns/tables you didn't intend, server defaults, enum changes, and JSON/array types.
- Name the revision file with the next sequential prefix to match the existing convention.
- Confirm
Apply and verify:
cd backend && uv run alembic upgrade head # or: make db-upgrade uv run alembic current # confirm at headThen round-trip once to prove
downgrade()works:alembic downgrade -1 && alembic upgrade head.
Data migrations / backfills
For backfills, add explicit op.execute(...) or a small data-loop in upgrade() (see the existing *_backfill_*.py migrations for the pattern). Keep schema and data changes in separate, well-named revisions when practical.
Rules
- Never edit a migration that has already been applied in shared environments — add a new one.
make dev/make bootstraprunalembic upgrade headautomatically; you don't need a separate step in dev.- Keep models and migrations in sync — a model change without a migration will pass tests (sessions are mocked) but break on real Postgres.