All rules Rule BS002 · SO
PostgreSQL
critical
SO · Set ordering
hosted, paid, --remote

Does a later migration reference a column an earlier one dropped or renamed?

Refers to a column an earlier migration in the set dropped or renamed

Critical: this fails outright or takes production down.

What happens

Migrations apply in order. When file 0003 drops or renames a column and file 0005 still indexes, alters, or constrains the old name, 0005 fails at apply time, after 0003 and 0004 have already run, leaving production between two states no migration describes. Fires only for tables the set itself created, where the column list is known exactly; anything created outside the set is left alone.

Why it is dangerous on a populated table

On an empty table this finishes before anyone notices. On a populated one the lock is held for the whole operation, every query on the table queues behind it, and the connection pool fills from the back.

Fires on

-- 0003_cleanup.sql
ALTER TABLE orders DROP COLUMN legacy_flag;
-- 0005_index.sql
CREATE INDEX idx_orders_legacy ON orders (legacy_flag);

The safe pattern

Reference the column by its current name, or drop the dead statement. If the later migration was written first and the drop came in later, reorder them, the set is checked in apply order.

-- 0005_index.sql, its own migration run outside a transaction:
CREATE INDEX CONCURRENTLY idx_orders_status ON orders (status);

How to check locally

Catch this before it ships

This rule runs in the hosted service on Startup and above: add --remote with a team token, or use the GitHub Action. No install, nothing leaves your machine:

npx bolvrk check migration.sql --remote