Story

The database that writes its own history

A production API needed a small schema change -- one column, low risk. The engineer who picked it up was new to the codebase and did what most of us would do: opened the migrations folder to see how the last change was made, so the new one would look like it belonged.

The human moment

What they found was not a folder full of one-off scripts named fix.sql, fix2.sql, and final_fix_REAL.sql -- the pattern almost every codebase drifts toward once enough people have touched the schema under deadline pressure. Instead, every file followed one dated, numbered naming rule, every change shipped with its own rollback file already written, and every single migration ended the exact same way: a short block that recorded, inside the database itself, that this migration had run. No spreadsheet. No wiki page someone forgot to update. No "ask Dave, he remembers." The database was keeping its own receipts.

The gap seen -- opportunity taken

The obvious shortcut here is the one most teams take without noticing they took it: trust a human, or a deploy log, or a project-management ticket to be the record of what schema state a database is actually in. That works fine until someone runs a migration by hand during an incident, or a rollback partially applies, or the wiki page is a version behind -- and now the "source of truth" for what the database looks like disagrees with the database. The team building this convention treated that drift as preventable, not inevitable, and closed it at the one place drift cannot lie: inside the transaction that made the change in the first place.

Teach the concept

This is a self-recording migration history: instead of a human or an external tool being responsible for tracking which schema changes have been applied, each migration script ends by writing its own record into a tracking table, in the same transaction that made the change. If the migration runs, the record exists. If it doesn't run, the record doesn't exist. There is no third state where the change happened but nobody wrote it down, because the writing-down is not a separate step -- it is the last line of the same script.

Why it matters -- the stakes without it

A schema history that can drift from reality is worse than no history at all, because it looks trustworthy right up until the moment someone acts on it. A rollback run against the wrong assumption about current state can take down a production database. A new engineer who inherits a migrations folder with no naming discipline and no rollback pairing either has to reverse- engineer the schema's actual history from the live database -- slow, risky, and error-prone -- or trusts a record that may already be stale.

How we approached it -- outcome level, and the tradeoff we accepted

The team accepted a small amount of upfront ceremony -- every migration must follow the naming rule, ship its own rollback, and end with the self-record block, with no shortcut for "this one's simple, skip the rollback" -- in exchange for a database that can always answer, truthfully and mechanically, "what has actually been applied to me." The easier path was to let a deploy pipeline or a project tracker own that answer instead; the harder path was rejected because a pipeline or a tracker can be out of sync with the database, and the database itself never can be.

What a reader can take to their own work

Ask where your own system's "source of truth" actually lives: in a document a human updates, or inside the system it describes. Any time those two can disagree, the document is a liability wearing the costume of documentation.

Evidence: an active, binding migration-naming and self-recording convention in a production API's schema-migrations folder -- verified by direct read of the current on-disk source file, L2-reviewed 2026-08-25 -- full record: docs/technology-portfolio/validation/run-4a-overnight-aifs/BATCH-CONTENT-PLAN-2026-08-25.md.

← All stories · Proof records →