All rules Rule BV027 · DS
PostgreSQL
critical
DS · Destructive changes
hosted, paid, --remote

Should DROP SCHEMA or DROP DATABASE ever appear in a migration?

DROP SCHEMA or DROP DATABASE in a migration

Critical: this fails outright or takes production down.

What happens

Dropping a schema removes every object in it in one statement, with CASCADE, without even listing them. Dropping a database is the same at a larger radius. Neither belongs in schema history; both are irreversible.

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

DROP SCHEMA app CASCADE;

The safe pattern

Quarantine before deleting: rename the schema, let it soak while anything still depending on it surfaces, then drop its objects explicitly in small reviewed migrations. DROP DATABASE never belongs in schema history at all, that is an operator action with backups verified first.

-- Quarantine first, delete later:
SET lock_timeout = '5s';
ALTER SCHEMA app_legacy RENAME TO app_legacy_deprecated;
-- after a soak period, drop its objects explicitly, a few per
-- reviewed migration, never the whole schema with CASCADE

Fixtures

The rule ships with these files and the test suite runs them on every change: the first set must fire, the second must stay silent.

Fires (1)

drop schema
DROP SCHEMA app CASCADE;

Stays silent (1)

drop table
DROP TABLE app_legacy;

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