test data
PRANSHUL
Engineering

The Case for Boring Migrations

A schema is a promise you make to every future version of yourself. The most professional thing a migration can be is dull.

By PranshulA 7-minute read

There is a particular kind of pull request that never gets applause: fifteen lines of SQL, a new column with a default, an index behind it. Nobody screenshots it. Nobody demos it. And yet in five years, when everything else in the codebase has been rewritten twice, that migration will still be true.

Every schema is a promise§

A migration is the only part of a system you cannot take back. Code can be reverted; data outlives its authors. So the discipline I hold myself to is simple: each migration must read like a contract clause — one concern, stated plainly, enforceable by the database itself rather than by good intentions in the application layer.

Constraints are documentation that cannot go stale. A CHECK clause is a comment the database is willing to fight for.

What boring looks like§

Boring means defaults on every new column so old rows stay valid. Boring means enums for closed sets and CHECK constraints for open ones. Boring means the trigger owns updated_at, because clients lie and clocks drift.

 sql
alter table public.posts
  add column reading_time_minutes int not null default 1
  check (reading_time_minutes >= 0);

create index idx_posts_status_published_at
  on public.posts (status, published_at desc);

None of that is clever. All of it is kind — to the reviewer, to the on-call engineer, and to whoever inherits the database after you. Cleverness ages like fruit; dullness ages like oak.

Leave a mark

Pass it on

Further reading

Letters to the editor

Correspondence