How much of a database migration can I safely delegate to an AI coding agent?

Mira, seen from behind, holds a heavy key ring while Noor removes one brass key beside an open wall cabinet filled with keys.

TL;DR

An AI coding agent can draft a migration, write the queries that check it and run it against a copy. It cannot know which data exists nowhere else, and it should not hold credentials that reach production or make the go decision. Restore a real backup into a database the agent's role can reach, run the migration there, compare counts, totals and per-row fingerprints with the original, and move on only when they match and a person approves.

What the seed script could not bring back

Hand-picked product images and their order, an admin account, store settings: those were the losses listed in a Claude Code bug report from April 2026, about a day of manual work. The agent had been asked to apply one new migration. It ran supabase db reset, which, as Supabase's CLI reference describes, recreates the local database, applies every migration and the seed file, and discards anything else. The incremental command, supabase migration up, applies only pending files. The seed script brought the catalogue back. The curation had lived only in that database.

Two questions from other places show the same gap from the delegating side. In Supabase's GitHub discussions, a developer using Codex asked whether it was risky that the Supabase MCP server let the agent apply a migration directly, without a migration file, leaving the local project out of step. On the OpenAI developer forum, someone running a Postgres application with more than 120 tables asked how others keep a coding agent oriented in a schema whose access rules live in the database catalog rather than the application code.

The split follows from those cases. An agent is good at the typing: the migration, the checks, the runbook. It cannot see which rows exist nowhere else, which systems still read a column, or whether the database in front of it is one you can afford to lose. Those answers have to exist before it starts, and the credentials it holds should make its worst mistake a cheap one.

Write down what has to survive

Before the agent drafts anything, list what the migration must preserve and how each item will be checked:

  • Data that exists only here: rows entered or curated by hand, history, anything a seed or import cannot regenerate. Name the tables and the counts or totals that prove they are intact.
  • Dependants the migration file does not show: foreign keys and their delete rules, triggers, views, scheduled jobs, reports, and other services or older app versions still reading a column.
  • Behaviour: uniqueness, defaults, access rules, and what the application does with the rows the change touches.

Some of the list is a decision rather than a fact. Merging duplicate accounts means choosing which record survives and what happens to the other's history. The agent can set out the options; a person who owns the data should pick one. With the inventory written, the division of work is clear:

Comparison

Hand to the agentKeep with a person
Drafting the migration and its reverseThe inventory of data and dependants that must survive
Writing reconciliation queries from that inventoryBusiness rules, such as which duplicate account wins
Running the migration on a restored copy and reporting the raw outputCredentials for production and for the backups
A runbook with timings taken from the rehearsalThe go decision and the production run itself

Give the agent a database it is allowed to lose

Isolation that depends on the agent choosing the right command is not isolation. The boundary that holds is the credential: a database role that can connect only to a rehearsal database restored from a recent backup, and no production connection string in any environment file, MCP configuration or shell history the agent can read. In the demonstration below, a role set up this way was refused a connection to the original database and refused permission to drop it.

Tool settings are a second layer, not a substitute. Claude Code's permission rules can deny a command such as Bash(supabase db reset *), but its documentation says a Bash deny rule "isn't a security boundary around the program": the same program called by another path or through sh -c is not matched. Prisma's CLI refuses prisma migrate reset --force from a detected agent until an environment variable records consent, which the agent is told to set only after asking you. Both are useful speed bumps. A role that cannot reach production is the part that does not rely on anyone's judgement in the moment.

If several agents work at once, each needs its own database as well as its own checkout. The article on running coding agents in parallel without them overwriting each other's changes covers what stays shared between worktrees.

Ivo pins the shoulder seam of a pale test jacket on a dress form beside a table holding folded coral fabric.
Cut the cheap cloth first: a rehearsal copy is where a mistake is allowed to happen.

A rehearsal that passed every check but one

To see what a rehearsal catches, a small one was run for this article on PostgreSQL 18.6 in a throwaway container. A synthetic database stood in for production: 2,040 customers, 40 of them second accounts whose email differs only in letter case, and 10,000 orders whose foreign key uses ON DELETE CASCADE. The goal was a unique index on lower-cased email. Two migrations were written by hand, not by an agent. Migration A keeps the oldest account for each email and deletes the rest. Migration B first moves each duplicate's orders to the surviving account and records every merged row in a log table, then deletes.

A pg_dump backup of the original was restored into a fresh rehearsal database for each run, and each migration ran as the restricted role. Before and after, the same queries recorded order count, order total, distinct emails and a fingerprint of every order with its owner's email:

SELECT md5(string_agg(o.id || ':' || o.total_cents || ':' || lower(c.email), ',' ORDER BY o.id))
FROM orders AS o JOIN customers AS c ON c.id = o.customer_id;

Migration A did exactly what it was asked, and every check an agent would usually report passed. The cascade removed 198 orders along with the duplicate accounts, and only the comparison with numbers taken before the run showed it. Before either migration, the restored copy's fingerprint matched the original's. That is the restore check: a backup counts once it has been restored and compared, not when the dump command exits cleanly. The original database was unchanged at the end.

Ivo jogs toward Noor with one hand raised while Noor marks the final box on a clipboard.
A drill is only finished when the count after matches the count before.

Decide what lets it reach production

Agree the evidence before the rehearsal runs, so the result is judged against something fixed:

  • Restore: a recent backup restored and fingerprinted, with the restore timed at production size.
  • Reconciliation: every item on the inventory matches, or differs exactly as planned. In migration B, 40 fewer customers and 40 log rows were the expected difference.
  • Run time and locks: measured on production-sized data, with a window that fits.
  • A way back: the reverse migration run on the rehearsal copy, or the restore accepted as recovery, knowing that writes made after the backup would need separate handling.
  • Approvals: the data owner signs the inventory and business rules; someone other than the agent reviews the raw rehearsal output and makes the go decision.

Then a person runs production with the rehearsed commands, and the same queries run before and after. The article on whether an AI coding agent should be allowed to merge its own pull request explains why those grants belong to different people.

Paqad is not a migration tool, a backup or a permission boundary. When a request is a migration, Paqad's routing always puts it in its full lane, which on Claude Code and Codex CLI means a recorded plan and frozen specification before the first code edit; while a decision waits for your answer, further code edits are blocked. Its hooks watch file edits only: a db reset or DROP TABLE run through the shell is not checked, and in paqad-ai 1.91.1 no gate requires a database review before a migration. Paqad's documentation sets out what its guardrails stop and what they leave to your tool's permissions.

Frequently Asked Questions

Scale it to how reversible each step is. Adding a table, a nullable column or an index can go further with less ceremony. Steps that drop, rewrite or merge data are better split, so the new structure ships and is used first and the destructive step runs later, as its own rehearsed change, once nothing depends on the old shape.

Build the dependency list from the database catalog rather than from application models: foreign keys with their delete rules, triggers and views that reference the tables. In the demonstration the damage came from a cascade defined in the schema, which the migration file never mentioned. Then add readers outside the database, such as reports and other services.

A role that can reach only a restored copy, and backups the agent's credentials cannot delete or overwrite. Keep production secrets out of every file and configuration the agent loads. Deny rules in the agent's tool reduce accidents, but test the boundary by trying the forbidden connection yourself with the agent's credentials.

Write the reconciliation queries before the migration and run them on the untouched copy first, so both the baseline and the queries are proven. Record the expected differences in advance, then rehearse the recovery path as well as the forward one, with timings, so the rollback is a measured option rather than a hope.

Matching or explained reconciliation, a restore checked against the original, measured run time and locking, and a stop condition written in advance, such as a count difference that aborts the run. The approver should read the rehearsal output itself, not the agent's summary of it, and the approval should name the backup it relied on.