Advanced·15 min·data · sqlalchemy · alembic · migrations
Database migrations with Alembic
Migrations: evolving your schema without downtime
Your first CREATE TABLE is easy. What about the third time you change users in production, without dropping the table?
That's Alembic. Every schema change gets a numbered, reviewable, revertible Python file.
The mental model
- Each revision is a file with
upgrade()(apply) anddowngrade()(undo). - Alembic keeps a
alembic_versiontable in your DB with the current head. alembic upgrade headfast-forwards from wherever you are to the latest.
Autogenerate — the killer feature
alembic revision --autogenerate -m "add plan column"
Alembic diffs your Python models against the live DB and writes the migration for you. Then you review it — autogen is 90% right; the other 10% is where prod outages come from.
The 5 rules of safe migrations
- Add columns nullable first, backfill, then set NOT NULL — otherwise deploys with old code crash.
- Never drop a column in the same release that removes it from code — deploy in two steps.
- Rename with expand-contract — add new column, copy data, dual-write, remove old. Never a straight rename in prod.
- Big table? Add index CONCURRENTLY on Postgres — regular
CREATE INDEXlocks writes. - Test the downgrade locally — running
alembic downgrade -1after every upgrade catches half of the bugs.
Try it (locally)
- Change a model — add
bio: Mapped[str | None]. alembic revision --autogenerate -m "add bio".alembic upgrade head, thenalembic downgrade -1.