Database migrations are one of the riskier things to hand off to Claude Code. They modify data, they're hard to reverse, and a mistake in production is a real problem. Here's the workflow I've settled on.
The core rule
In CLAUDE.md: "Never run database migrations automatically. Generate the migration file, then stop and wait for review. Do not run migrate or db:migrate or equivalent commands without explicit instruction."
This is the one place I always stay in review mode, regardless of what else I've delegated.
Generating migrations
For schema changes, I tell Claude: "Generate a migration that adds a last_login_at timestamp column to the users table, nullable, default null." Claude writes the migration file. I review it before running it.
For complex migrations involving data transformations: I ask Claude to generate the migration and explain what it does step by step before I review the code.
What Claude gets right
- Forward migration (the change)
- Rollback migration (the reverse) — Claude is good at writing reversible migrations
- Indexes on new columns
- Constraint naming following conventions
What to check before running
- Does the rollback actually reverse the forward migration?
- Are there any NOT NULL columns on existing tables without a default or backfill?
- Does this migration lock the table? For large tables, this matters.
- Is there a corresponding model/ORM change?
The deployment sequence
For any non-trivial migration: run it on a copy of production data first. Not staging, not dev — a real production data copy. This is the only way to catch issues with data volume, constraint violations, or lock durations before they affect users.
Claude can generate the migration. You still own the deployment.
Top comments (0)