結論:データベースのマイグレーションは「Claude Codeに任せて速く回す」より先に「壊さない仕組みを作る」が9割です。Claude Codeは現状スキーマの把握・前方/後方互換を意識したマイグレーションファイルの設計・ロールバックSQLの草案づくりを得意としますが、本番DBへの破壊的変更(DROP/ALTER)は必ず人がレビューし、実行前にバックアップを取る運用とセットでなければ事故ります。
- 要点1:まず「現状スキーマの把握」をClaude Codeにやらせる。`information_schema`や`\d`の出力、既存マイグレーション履歴を読ませて全体像を言語化させる。
- 要点2:マイグレーションは「expand → migrate → contract」の3段階で前方互換に設計し、ダウンタイムと巻き戻しを最小化する。
- 要点3:生成されたSQLは鵜呑みにせず、ステージング+本番相当のデータ量で検証し、ロールバック手順とバックアップを必ず先に用意する。
対象読者:Webアプリ・SaaSのバックエンドを触る開発者、SRE、テックリード。今日できること:手元のリポジトリでスキーマ把握プロンプトを1回流し、次のマイグレーションを expand/contract に分けて設計し直してみることです。
正直に言うと、自分が一番ヒヤッとしたのは数年前、ピーク帯に`ALTER TABLE`で大きなテーブルにカラムを追加して、テーブルロックでサービスが数分止まったときです。MySQLの古いバージョンで、`ALTER`が裏でテーブル全体をコピーする挙動を知らなかった。あれ以来「マイグレーションは設計が9割、実行は儀式」だと思ってやっています。
Claude Codeを使い始めてから、この「設計」のところがかなり楽になりました。既存スキーマを読み込ませて「このカラム追加を無停止でやるにはどう分割すべき?」と相談すると、expand/contractの分割案やロールバックの当て方まで一気に下書きしてくれる。ただし——ここが本題なんですが——Claude Codeが出したマイグレーションSQLをそのまま本番に流すのは絶対にやめたほうがいい。この記事では「速くする」より「壊さない」に振った、Claude CodeでのDBマイグレーション実践フローを、SQLとコード例つきで書きます。情報は2026年6月時点のものです。

DBマイグレーションでClaude Codeを使うときの大前提
最初に、絶対に外せない安全原則を3つだけ。これを守らないと、どんなに賢いSQLを生成させても意味がないです。
- 本番DBへの変更は必ずバックアップを取ってから。論理ダンプ(`pg_dump` / `mysqldump`)かスナップショットを取り、復元手順まで確認しておく。「バックアップはあるが復元したことがない」は無いのと同じです。
- 破壊的変更(DROP TABLE / DROP COLUMN / ALTER COLUMN の型変更)は人が必ずレビューする。Claude Codeに`–dangerously-skip-permissions`を付けて本番DBに対して自走させるのは厳禁。生成・検証はAIに任せても、実行のトリガーは人間が引く。
- 生成されたSQLは検証してから実行する。Claude Codeはスキーマ名・カラム名・外部キーの向きを取り違えることがあります。`EXPLAIN`や`–dry-run`、ステージング実行で必ず確認してから本番へ。
Claude Codeの権限設計については、本サイトのClaude Codeでデバッグ・障害調査を効率化する実践ガイドでも触れていますが、DBが絡む作業は「読み取りは自動、書き込みは確認」をデフォルトにするのが鉄則です。具体的には、`settings.json`でマイグレーション実行コマンドを許可リストから外し、毎回確認が入るようにしておきます。
ステップ1:現状スキーマを把握させる
マイグレーションの事故の多くは「いまDBがどうなっているか」を正確に把握していないことから起きます。最初にやるべきは、Claude Codeに現状スキーマを読ませて全体像を言語化させること。これだけで設計の精度が変わります。
PostgreSQLなら、スキーマ情報をファイルに吐き出してからClaude Codeに読ませるのが確実です。
# PostgreSQL: スキーマ定義をダンプ(データは含めない)
pg_dump --schema-only --no-owner --no-privileges mydb > schema_snapshot.sql
# テーブルごとの行数・サイズも把握(マイグレーション戦略の判断材料)
psql mydb -c "
SELECT relname AS table_name,
n_live_tup AS row_estimate,
pg_size_pretty(pg_total_relation_size(relid)) AS total_size
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC
LIMIT 20;
"
そのうえで、Claude Codeにこう指示します。プロンプトの粒度がポイントで、「読んで」だけだと表面をなぞるので、出力フォーマットまで指定します。
schema_snapshot.sql と上の行数サマリを読んで、次を整理して:
1. 主要テーブルと、それぞれの責務(1行ずつ)
2. 外部キーの依存グラフ(どのテーブルがどれを参照しているか)
3. NOT NULL・UNIQUE・CHECK 制約のうち、マイグレーションで
引っかかりやすいもの
4. 行数が多くオンライン変更で注意すべきテーブル(100万行超)
推測が入る箇所は「推測」と明記すること。
「推測は明記」を入れておくと、Claude Codeが履歴ファイルにない情報を断定で書くのを抑えられます。ここで出てきた依存グラフが、次の設計ステップの土台になります。
ステップ2:前方/後方互換を意識して設計する(expand → migrate → contract)
無停止デプロイで最重要なのが、この3段階分割です。アプリケーションの旧バージョンと新バージョンが「同時に動いても壊れない」状態を保ちながらスキーマを変える、という考え方です。
- expand(拡張):新しいカラム・テーブルを「追加するだけ」。既存コードはこれを無視できるので無停止。NULL許容かデフォルト値つきで追加する。
- migrate(移行):アプリを新バージョンにデプロイし、新旧両方のカラムに書き込む(dual-write)か、バックフィルでデータを埋める。
- contract(収縮):旧バージョンが完全に居なくなったことを確認してから、古いカラムを削除する。ここが唯一の破壊的変更で、最も慎重に。
たとえば「`users.name`を`first_name`/`last_name`に分割する」という、一見シンプルだけど一発でやると危険な変更を考えます。Claude Codeに設計を相談すると、こういう分割案を出してくれます(出力は必ずレビューする前提)。
-- フェーズ1: expand(無停止・追加のみ)
ALTER TABLE users ADD COLUMN first_name VARCHAR(100);
ALTER TABLE users ADD COLUMN last_name VARCHAR(100);
-- フェーズ2: migrate(バックフィル。本番は分割バッチで)
-- 一気にUPDATEするとロック/レプリ遅延が出るので
-- 主キー範囲で区切って少しずつ流す
UPDATE users
SET first_name = split_part(name, ' ', 1),
last_name = split_part(name, ' ', 2)
WHERE id BETWEEN 1 AND 10000
AND first_name IS NULL;
-- フェーズ3: contract(旧バージョン消滅を確認後にのみ実行)
-- ALTER TABLE users DROP COLUMN name; -- ★人間が最終確認
contractの`DROP COLUMN`はコメントアウトしたまま渡し、「旧アプリのトラフィックがゼロになったログを確認してから外す」運用にしておくのが安全です。Claude Codeは「論理的には正しいけど運用順序が危ない」SQLを平気で出すので、この順序の番人は人間がやります。
ステップ3:マイグレーションファイルを生成して確認する
多くのプロジェクトはマイグレーションツール(Flyway、Alembic、Prisma Migrate、Rails、golang-migrate など)を使っています。Claude Codeにはツールの作法に合わせたファイルを生成させますが、ここで効くのが「既存のマイグレーションファイルを数本読ませてから書かせる」ことです。命名規則やトランザクションの囲み方を真似してくれます。
# 既存のマイグレーション規則をClaude Codeに学習させてから生成
$ claude
> migrations/ ディレクトリの直近5ファイルを読んで、命名規則・
> up/down の書き方・トランザクションの囲み方を把握して。
> その作法に厳密に合わせて、users テーブルに first_name と
> last_name を追加する expand フェーズのマイグレーションを
> 1ファイル作成して。down では追加カラムを確実に戻すこと。
生成されたら、必ず`up`と`down`の両方をレビューします。とくに`down`(巻き戻し)が雑だと、いざというときに復旧できません。Alembicの例だと、こういう形になります。`down_revision`の連結が正しいか、`downgrade()`が`upgrade()`を完全に打ち消すかをチェックします。
# Alembic: migrations/versions/a1b2_add_name_columns.py
from alembic import op
import sqlalchemy as sa
revision = "a1b2c3d4e5f6"
down_revision = "0f9e8d7c6b5a"
def upgrade() -> None:
op.add_column("users", sa.Column("first_name", sa.String(100), nullable=True))
op.add_column("users", sa.Column("last_name", sa.String(100), nullable=True))
def downgrade() -> None:
# upgrade を完全に打ち消す。順序にも注意
op.drop_column("users", "last_name")
op.drop_column("users", "first_name")
ORM(Prisma / TypeORM など)を使っている場合は、スキーマファイルを更新させたうえで、ツールが生成したSQLを`–create-only`系のオプションで「生成だけして適用しない」状態にし、人がSQLを目視してから適用するのがおすすめです。ORMが自動生成するSQLは、想定外の`DROP`を含むことが本当にあります。マイグレーションファイルのテストについては、Claude CodeでQA・テスト自動化を加速する実践ガイドの考え方をマイグレーションテストにも応用できます。
ステップ4:ロールバック方針を先に決める
「マイグレーションを書く前にロールバックを書く」くらいの順序が安全です。ロールバックには大きく2系統あり、変更内容によって使い分けます。
- 可逆マイグレーション(down で戻せる):カラム追加、インデックス追加など。`down`を実行すれば元に戻る。前方互換に設計していればこちらが基本。
- 不可逆+バックアップ復元(down で戻せない):`DROP COLUMN`でデータが消える、型変換で精度が落ちる、など。この場合は「巻き戻し=バックアップからの復元」になるので、変更直前のバックアップが生命線になる。
Claude Codeに、マイグレーションごとのロールバック判定をやらせると抜け漏れが減ります。
このマイグレーションは down で完全に元に戻せる「可逆」か、
データが失われる「不可逆」かを判定して。
不可逆なら、巻き戻しに必要なバックアップ対象テーブルと
復元手順を箇条書きで出して。
不可逆と判定されたら、実行前に対象テーブルだけでも論理ダンプを取っておきます。全DBダンプが重い場合は、対象テーブルのコピーを残すだけでも保険になります。
-- 破壊的変更の直前に、影響テーブルを別名で退避(保険)
CREATE TABLE users_backup_20260606 AS TABLE users;
-- 問題なければ後日 DROP。問題があれば INSERT ... SELECT で復元
ステップ5:本番前の検証(ステージング・バックアップ・データ量)
ここを飛ばすと、開発DBでは一瞬だったマイグレーションが本番で数十分テーブルロックする、という事故になります。検証の観点は3つです。
- 本番相当のデータ量で時間を測る:本番のスナップショットを復元したステージングで実行し、所要時間とロック範囲を確認する。1万行と1000万行では挙動が別物。
- ロック影響を事前に読む:PostgreSQLなら`ACCESS EXCLUSIVE`を取る操作(一部の`ALTER`)に注意。`CREATE INDEX CONCURRENTLY`のように無停止オプションがある操作はそちらを使う。
- バックアップからの復元を一度通す:「バックアップが復元できる」ことをステージングで確認しておく。本番障害のときに初めて復元コマンドを叩くのは怖すぎます。
Claude Codeには、本番適用前のチェックリストを生成させると便利です。
このマイグレーションを本番に適用する前のチェックリストを作って。
観点:
- バックアップ取得と復元確認
- ステージングでの所要時間計測(本番相当データ量)
- テーブルロック・レプリケーション遅延の有無
- アプリの新旧バージョン互換(expand/contract のどの段階か)
- ロールバック手順(可逆 or バックアップ復元)
- 適用する時間帯(低トラフィック帯か)
チェックボックス形式で。
PostgreSQLで無停止インデックスを張る場合は、マイグレーションツールのトランザクション外で実行する必要がある点も注意です(`CONCURRENTLY`はトランザクション内で使えない)。Claude Codeはこの制約を見落とすことがあるので、生成SQLを必ず確認します。
-- 無停止でインデックス作成(テーブルロックを避ける)
-- 注意: CREATE INDEX CONCURRENTLY はトランザクション内で実行不可
CREATE INDEX CONCURRENTLY idx_users_last_name ON users (last_name);
落とし穴とアンチパターン(❌/⭕)
自分や周りが実際にやらかした、DBマイグレーション×AIの典型的な失敗を挙げます。
❌ 生成SQLを本番に直流し
Claude Codeが出した`ALTER`をコピペで本番に実行。スキーマ名の取り違えや、ピーク帯のロックで巻き込み事故。
⭕ ステージング→時間計測→低トラフィック帯で適用。生成は任せても実行は儀式として人が回す。
❌ down(ロールバック)を書かない/テストしない
`up`だけ書いて満足し、障害時に戻せない。
⭕ up と同時に down を書き、ステージングで up→down→up を一度通す。不可逆な変更はバックアップ復元を巻き戻し手段として明文化する。
❌ 一発で破壊的変更(リネーム・型変更・列削除)
`users.name`をいきなりリネーム。旧アプリがエラー連発。
⭕ expand→migrate→contract で分割し、旧バージョンが居なくなってから削除。
❌ 巨大テーブルを一括UPDATEでバックフィル
1000万行を1本の`UPDATE`で更新し、ロックとレプリ遅延で全体が詰まる。
⭕ 主キー範囲でバッチ分割し、少しずつ流す。進捗とエラーを見ながら止められるようにする。
このあたりの「危険な変更を見抜く」目の養い方は、Claude Codeでコードレビューを効率化する実践ガイドのレビュー観点とも通じます。マイグレーションは「コードレビューと同じ厳しさで人が見る対象」だと考えると、ちょうどいい温度感です。
まとめ:Claude Codeは「設計と検証」、実行トリガーは人間が引く
DBマイグレーションでClaude Codeが効くのは、現状スキーマの把握・前方互換な分割設計・ロールバック判定・検証チェックリスト作成といった「考える」部分です。一方で、本番DBへの破壊的変更の実行だけは、バックアップと人間のレビューを必ず挟む。この線引きさえ守れば、マイグレーションの設計時間は明確に短くなります。Claude Codeの基本操作や安全設定をまだ固めていない場合は、Claude Code 実践テクニック完全ガイドから土台を作るのがおすすめです。
今日の一歩としては、手元のリポジトリで「ステップ1のスキーマ把握プロンプト」を1回流してみてください。自分のDBの依存グラフを言語化してもらうだけで、次のマイグレーションの設計がぐっと安全になります。