ADR-0010: Migrations Stay Additive; Rollback Is by Flag, Not by Schema Reversion
Status
Accepted
Context
A migration that drops a column on downgrade() destroys data. If a
deploy is rolled back to an earlier code version after that migration ran,
rolling back the schema too would permanently lose whatever was written to
that column in the meantime — data that reverting the code alone does
not bring back.
Decision
downgrade() deliberately raises NotImplementedError in every migration.
Schema changes are additive only. Behavior instead reverts through
environment-variable flags (FANOUT_V2, STREAM_AUTHZ_ENFORCE) — every
staged rollout is required to revert by flipping a flag, not by requiring a
redeploy or a schema reversion.
Alternatives Considered
Not recorded in source material.
Consequences
- A code rollback (deploying an older git tag) never needs a matching
schema rollback — the database is always compatible with an older
application version, because nothing destructive ever runs on
downgrade. - Any behavior change risky enough to need a rollback path must be designed as a flag-gated rollout from the start (see ADR-0007 and ADR-0008) — this ADR is the general rule those two are specific instances of.
- Schema growth is one-directional: old columns/tables that become unused
stay in place rather than being dropped as part of a reversible process:
removing them is a separate, deliberate forward migration, not something
downgradeever automates.