AI can write a migration that runs cleanly and still damages a production system. That's the sentence worth sitting with before handing schema work to any model, because it points at the actual failure mode: syntactically correct SQL is not the same thing as SQL that's safe for the table it's about to run against.
Why a migration isn't just another code change
Application code has a built-in undo button. A bad release goes back to the previous version and the incident is mostly resolved. A database migration doesn't get that. It edits persistent state that already contains production data, under live traffic, with locks, permissions, and rollback constraints that a normal deploy doesn't have to think about. A migration that fails partway through can block traffic, corrupt records, or leave the schema and the application out of sync and some of that damage isn't reversible no matter how confident the rollback script sounds. AI-generated migration risk generally clusters around a handful of patterns: destructive operations, failed constraints, long-running locks, incorrect assumptions about existing data, and incompatibility between old and new application versions running at the same time.
The trust gap this workflow is trying to close
This isn't a hypothetical concern about AI output in general, it's measurable, and the numbers are worth citing directly. Sonar's 2026 State of Code Developer Survey found that 96% of developers don't fully trust AI-generated code to be functionally correct, yet only 48% say they always review AI-assisted code before committing it. That's a verification gap on ordinary code. Stack Overflow's most recent developer survey found a similar divergence at a larger scale: AI tool adoption climbing toward 84%, while trust in the accuracy of that output fell to 29%, down from 40% a year earlier. Extending that same casual level of verification to a change running directly against a production database is where the gap stops being tolerable. Migrations are the category of AI output where the cost of skipping review is highest, which is exactly why they need a distinct, stricter workflow rather than the default level of scrutiny applied to a typical pull request.
Feed the model database context, not just the schema change
A migration prompt needs to carry more than "add a column." Before generating SQL, it's worth including the database engine and version, the current table definition, an approximate row count, existing indexes and foreign keys, read/write traffic patterns and peak windows, which application versions will be live during rollout, known data-quality issues, downtime tolerance, and whether the requested change is additive, transformational, or destructive. Concretely, that looks like:
We use PostgreSQL 16. The
orderstable contains 80 million rows and receives writes continuously. The application must remain available during deployment. Add a nullablerefunded_attimestamp, backfill existing refunded orders, and avoid a table rewrite or long blocking lock. Provide the migration plan, SQL, validation queries, rollback limitations, and rollout sequence before writing the migration.
A model given that context reaches for CREATE INDEX CONCURRENTLY and a batched backfill instead of an operation that locks the table for the full duration of the run. That single difference in prompt quality is often the gap between a migration that's safe at scale and one that only ever got tested against a handful of rows.
Classify before you review
Not every schema change carries the same risk, and treating them identically either slows down safe changes or under-scrutinizes dangerous ones. Lower-risk changes include adding an unused nullable column, adding a new table, adding a non-blocking or concurrent index, and adding a new enum value (behavior here depends on the engine and how the application reads it). Higher-risk changes include adding a NOT NULL constraint to a populated table, adding a unique constraint where duplicates might already exist, changing a column type, renaming a column or table, adding foreign keys against data that hasn't been validated, and large data backfills. Highest-risk changes include anything destructive, full-table rewrites, changes scheduled during peak traffic, anything touching billing, identity, permissions, or personal data, and changes that require multiple services to deploy together. The more destructive and harder to reverse the operation, the more cautiously an AI-generated version of it should be treated, dry runs, staged rollout, and explicit human sign-off scale up with the risk tier rather than being applied uniformly.
Expand, backfill, switch, contract
This is the technical core of a safe migration workflow, and it's the part most worth internalizing as a default rather than a special case. Changing the database schema and the application code in a single step removes the ability to back out cleanly, so the change gets split into four stages instead. Expand adds the new column, table, index, or structure without touching the old one. Backfill populates the new structure gradually, in batches, with checkpoints, not inside one long transaction. Switch moves the application to read from and write to the new structure. Contract removes the old structure only once nothing in the codebase still depends on it.
Renaming a column is the clearest way to see why this matters. The unsafe, single-step version:
ALTER TABLE users RENAME COLUMN full_name TO display_name;
The safer sequence: add display_name, write to both full_name and display_name simultaneously, backfill existing rows in batches, move reads over to display_name, monitor for missing or inconsistent values, and only then drop full_name in a later release. The extra steps exist specifically so an old and a new version of the application can run side by side during a rolling deployment without either one breaking against the schema underneath it.
Nothing AI-generated should reach production without a human step in between
The control that matters most here is procedural rather than technical. Let the AI generate and explain the migration, then run it against a clean database snapshot, then against sanitized production-like data, then execute a dry run in CI, review the generated SQL and its query plan, and require explicit approval before it reaches staging or production. The agent producing the migration shouldn't hold unrestricted production credentials, and the migration itself, its reviewer, its approval, and its execution result are all worth logging as a matter of course. In practice: the AI may propose a migration, CI may test it, but a human approves the production change, every time, regardless of how routine the change looks.
This pattern of separating proposal from execution isn't unique to migrations, and it's worth noting how much of the broader AI development tooling landscape has converged on it independently. Architecture-first platforms like 8080.ai generate a system requirements document and architecture plan before any code gets written; spec-driven workflows built around files like AGENTS.md do something similar by giving reviewers a concrete artifact to check before changes get applied; builders like Replit and Lovable increasingly gate generated changes behind an explicit approval step rather than applying them automatically. None of these fully close the trust gap described above on their own, the review still has to actually happen, but they reflect the same underlying principle this workflow is built on: AI output is a draft until a human, or a tested process, says otherwise.
Test against data that looks like production, not a demo table
A migration that succeeds against ten clean rows can fail in ways that only show up at real scale. Testing against a sanitized production snapshot, one that retains nulls, duplicates, malformed values, old records, and inconsistent formatting rather than clean synthetic data, surfaces problems that a demo dataset hides. Worth measuring specifically: execution time and lock behavior at representative table size, memory and CPU usage during the run, query plans before and after the change, application behavior during a partial rollout, and whether new constraints genuinely hold against the existing data before they're switched on.
Make backfills survivable, not just fast
Large backfills are where migrations that looked simple on paper turn into operational incidents. Processing rows in batches with stable ordering and checkpoints, making the operation idempotent, avoiding a single massive transaction against a large table, slowing or pausing the backfill under rising load, and recording progress so a retry doesn't duplicate or partially publish data, these are the details that determine whether an interrupted backfill is a minor inconvenience or a multi-hour incident. An idempotent migration, specifically, is one that can be safely run again without corrupting or duplicating anything already written which is what stable identifiers, checkpoints, and transactional writes are actually for.
Rollback needs to be tested, not asserted
It's worth staying skeptical of any AI-generated migration that casually claims it's reversible. Additive schema changes are often easier to leave in place than to remove cleanly. Destructive changes may require a restore rather than a reversal. Data transformations frequently aren't perfectly invertible, and a "down migration" existing in the file doesn't guarantee that running it actually restores the original state. The more reliable approach is deciding, before execution, whether the response to a failed migration is a revert, a forward fix, a restore from backup, or a stop-and-investigate and then actually testing that recovery path in staging, including verifying backups and point-in-time recovery, before the change goes anywhere near production.
The migration isn't finished when the command exits
A successful exit code is the start of the observation window, not the end of it. Migration duration, lock waits, active connections, query latency, error rates, replication lag, connection-pool saturation, backfill progress, constraint violations, and application errors tied to the changed fields are all worth monitoring during and after execution. For PostgreSQL specifically, standard guidance holds up well here: batch large updates, use transactions where they make sense, and create indexes concurrently wherever the engine supports it, rather than accepting a blocking operation by default.
What actually matters
The real question for an AI-generated migration was never whether the SQL is syntactically correct, it's whether the change preserves data, availability, application compatibility, and a real path to recovery once it's running under production conditions. That holds regardless of where the migration came from: a chat-based assistant, an autocomplete suggestion inside an IDE, or a more structured, architecture-first platform such as 8080.ai that produces a plan before any SQL exists. The workflow above is the same either way, the tool changes; the review doesn't.
Top comments (0)