DL
Duong Labs

7 September 2026 · Databases

What breaks when an AI agent edits your database schema

The migration runs clean locally, the diff looks reasonable, and code review passes. None of that tells you whether it's safe to run against a table with real rows in it.

Letting an agent write application code and letting it write schema changes are different risk classes. Application code is easy to roll back — redeploy the previous build. A migration that already ran against production is a fact, not a diff. If it dropped a column, truncated a type, or took a lock for four minutes on your busiest table, "revert the commit" doesn't undo any of that.

Four patterns keep showing up in agent-generated migrations that pass a first read and still cause an incident.

1. Silent data loss disguised as a rename

Ask an agent to "rename user.name to user.full_name" and a plausible answer is: drop the old column, add the new one. Functionally that's not a rename — it's a delete followed by an insert of nulls. A real rename needs three deploys (add the new column and dual-write, backfill, then drop the old one) or a database-native rename statement. The agent's one-migration version looks identical in a diff to the safe version; the difference only shows up when you check whether existing rows kept their data.

2. No down-migration, or a down-migration that doesn't actually reverse anything

Most agents will generate an up block without being asked twice, and a down block that technically exists but is wrong — commonly it drops a column that other code still depends on, or it's simply pass/a no-op with a comment. Nobody notices until the migration needs to be rolled back mid-incident, at which point discovering the down-migration doesn't work is the second incident.

3. Locking behavior that's invisible until table size is real

Adding a NOT NULL column with a default, adding an index, or changing a column type can each take a full-table lock depending on the database and version. On a local dev database with 40 rows this finishes instantly. On a production table with 40 million rows the same statement can hold a lock long enough to queue up every write and effectively take the app down. An agent has no way to know your table sizes unless you tell it — and most prompts don't.

-- looks harmless, can lock the whole table on a large dataset
ALTER TABLE orders ADD COLUMN status TEXT NOT NULL DEFAULT 'pending';

-- safer pattern most agents won't produce unprompted:
-- 1) add column nullable, 2) backfill in batches, 3) add NOT NULL constraint separately

4. Type coercion that quietly truncates rather than errors

Narrowing a column — varchar(255) to varchar(50), bigint to int, a timestamp to a date — sometimes fails loudly and sometimes silently truncates or coerces existing data depending on the database's strictness settings. An agent asked to "tighten up the schema" has no visibility into which rows are about to lose data on the cast; it only sees that the statement is valid SQL.

What doesn't need a human

Drafting the migration syntax itself, adding indexes for a query you've already profiled, straightforward additive changes (new nullable column, new table).

What does

Anything that renames, narrows, drops, or adds a NOT NULL/unique constraint to a column that already has data — read the generated SQL, not just the description of what it does.

The one habit that catches most of this

Run every agent-generated migration through EXPLAIN (or your database's equivalent) against a copy of production-sized data before it goes anywhere near the real database, and read the actual SQL — not the agent's plain-English summary of the SQL, which is where the "just a rename" framing usually comes from. A migration review gate that only reads the commit message will approve all four of the patterns above.