Let's look at a more realistic migration that reflects a common evolution.
The projects table is being renamed to initiatives to better reflect how teams plan and track long-term work.
We also want to record when each initiative officially launched.
Up migration:
ALTER TABLE projects RENAME TO initiatives;
ALTER TABLE initiatives
ADD COLUMN launched_at TIMESTAMP;
Down migration:
ALTER TABLE initiatives DROP COLUMN launched_at;
ALTER TABLE initiatives RENAME TO projects;
This pair of migrations is reversible and safe. If something breaks, we can undo it.
In real-world projects, we don't run raw SQL migrations. We use tools that help:
| Tool | Language | Notes |
|---|---|---|
| Goose | Go | Native Go tool |
| Flyway | Java, etc. | Simple file-based |
| Liquibase | Java | More config-heavy |
| Alembic | Python | For SQLAlchemy |
| Prisma Migrate | Node.js | Works with Prisma ORM |
this will vary according to the tool you use
001_add_columns_to_transactions.up.sql001_add_columns_to_transactions.down.sqlmigrate up
Migration files are committed like code. They travel with your project, so your teammates and CI systems always apply the same schema changes in the right order.