ai · tutorial · agents

Migrations AI Can't Nuke: Additive Only, Never Drop

One agent prompt can wipe prod with a DROP. The additive-only law plus the 3-deploy pattern for destructive changes. The rules inside.

The original Pennsylvania Station in New York, early 1910s
The original Pennsylvania Station in New York, early 1910s · Photo: Detroit Publishing Co., public domain, via Wikimedia Commons

"clean up the old column" deletes prod

The prompt was innocent: "remove the deprecated nickname field." The agent wrote a migration with DROP COLUMN, tested against an empty dev database, and pushed. Production held two years of nicknames users paid to customize.

No backup from today. No reviewer caught it — the diff looked tidy. The agent did exactly what it was told, and what it was told was catastrophic.

Additive migrations ai safe law exists because agents execute destructive commands with the same cheer as safe ones. The repo must make the dangerous ones unrepresentable.

Destructive vs additive under agents

Statement Reversible? Agent-safe?
ADD COLUMN ... DEFAULT Yes (ignore it) Yes
CREATE TABLE IF NOT EXISTS Yes (empty) Yes
CREATE INDEX CONCURRENTLY Yes (drop it) Yes, off-peak
DROP COLUMN No (data gone) Never
RENAME COLUMN Barely (downtime + drift) Never in one deploy
DELETE without WHERE No Never, ever

Three details make destructive migrations worse under agents than humans:

  1. Agents test on empty databases. Dev seeds hold five rows; DROP succeeds instantly and the gate stays green. Destruction only manifests against prod-shaped data — which the agent never sees before pushing.
  2. "Cleanup" prompts sound safe. Rename, remove, tidy, consolidate — human words for irreversible operations. Agents lack the flinch a DBA feels at DROP; the law must supply it in writing.
  3. Three engines triple the blast radius. A drop written for SQLite replays against Mongo and Supabase through parity delegation. One prompt, three databases, synchronized data loss — unless the law blocks the statement class everywhere at once.

Backups mitigate; only the law prevents. Both, always — but prevention first.

The law, the pattern, the backstop

BoringPush states this as the first never-violate rule in its agent rules, and backs it with a check that refuses destructive statements.

Step 1: Post the additive law where agents read it. The rule is short. Allowed: adding a column with a default, creating a table if it does not exist, creating an index off-peak. Forbidden: dropping a table or column, renaming, truncating, deleting or updating without a condition. Violations fail review automatically, with no discussion and no exceptions.

"Fail review automatically" removes negotiation. Sessions do not argue edge cases with the law; they route around it with safe patterns. Ban the statement class, not the incident, because agents generalize to classes better than to instances.

Step 2: Ship destructive intent as three safe deploys. A rename becomes add, migrate, remove across days. First, add the replacement column so old and new coexist. Days later, backfill it, dual-write and verify the two match. Weeks later, stop reading the old column, and only then drop it, a step that is human-approved and human-run.

Each step is independently safe and reviewable, and old code keeps working against the new column. Slow?

Deliberately. Irreversible deserves ceremony.

Step 3: Keep the backstop that assumes the law fails. BoringPush's database check scans a migration file before it runs and refuses drops, truncations and unbounded deletes or updates, whether a human or an agent wrote them. Use it before every migration, with bp db check on the file. The kit also takes a daily database snapshot, so there is something to restore from.

Production migrations are never run by an agent. A human runs them deliberately, against staging first.

Defense in depth: the law in the rules file, the process of three deploys, and automation in the check plus snapshots. Any single layer can fail and your data still stands.

That's the whole trick: the data outlives every session's enthusiasm.

What it costs

The cons, listed: the three-deploy pattern turns a one-line rename into a multi-week project, and the forbidden list occasionally blocks a genuinely safe cleanup for days.

BoringPush prices ceremony against catastrophe. Renames are rare, and data loss is forever.

Impatience is the only cost, and impatience is billable to nobody after an incident. BoringPush publishes no incident counts from this practice, and this post does not invent any.

FAQ

Are additive migrations ai safe on all three engines equally?

Yes — ADD COLUMN with defaults, CREATE TABLE IF NOT EXISTS, and index creation are safe operations on SQLite, Postgres (Supabase), and Mongo (schemaless, additive by nature). The law is engine-agnostic by design: one rule memorized once, protection everywhere. Engine-specific sharp edges, such as Postgres locking on big-table changes and SQLite's limited ALTER, belong in per-engine notes, not in the law.

What about DELETE with WHERE — allowed or not?

Allowed but caged: single-row deletes by primary key (unsubscribe, remove-my-account) are normal operations. Bulk deletes get the ceremony treatment — explicit row count first (SELECT COUNT), limit-batched execution, snapshot verified before running. WHERE-present is necessary but not sufficient; volume decides the ritual.

How do seed and test data interact with the law?

Freely — dev and test databases accept any statement, including drops, because they hold nothing irreplaceable. The law binds prod paths only: production migrations, prod consoles, anything touching live rows.

Sessions iterate destructively on throwaway data and additively on real data. Same agent, two arenas, bright line between them.

Where should the destructive-statement check run?

Both where you can, cheapest first. Run it on your machine before a migration is applied, and again in whatever automated check runs on push. Either alone suffices, and together they cover the unusual path.

Redundant guards on irreversible operations are not waste. They are the point.

When is a human allowed to run a DROP on prod?

After three conditions hold: the column has been unread by code for two full releases (verified by grep, not memory), a fresh snapshot exists and restores cleanly on staging, and a second human knows it is happening. Then the human — never the agent — types it in a staffed hour with rollback rehearsed. Ceremony scales with irreversibility; DROP sits at the top.

Do ORMs like Prisma make this safer or riskier?

Riskier by default: prisma migrate dev happily generates drops from schema edits, and agents accept generated SQL without reading it. If you keep an ORM, gate its output through the same destructive-statement check before apply. Raw SQL with reviewed migrations avoids this class entirely — generated destruction is still destruction, just faster.

Keep reading

+ all the boring stuff (analytics, auth, SQLite ↔ Supabase, full marketplace, webhooks)

The Agent-First Shipping Kit

Command from your phone.

Built so autonomous AI agents build what you want without breaking what's already paying you.

$199

Get BoringPush

One-time payment · Lifetime updates · Unlimited projects.

Keep reading