Claude Codeでデータベースマイグレーションを安全に管理する——本番ダウンタイムなしのスキーマ変更

はじめに

データベース設計での初期スキーマ設計でも、CI/CDの全般的な話でもない。「本番に1000万行あるテーブルのカラムをリネームする」——ダウンタイムなし・データ損失なしでスキーマを変更する安全なマイグレーション手順を、Claude Codeで自動生成する話だ。

ALTER TABLE users RENAME COLUMN email TO email_address; は開発DBでは一瞬で完了するが、本番の1000万行テーブルに対して実行するとテーブルロックが発生し、数分間のサービス停止を引き起こす。「マイグレーションを書ける」と「本番で安全に適用できる」は別の問題だ。

CLAUDE.mdにマイグレーションポリシーを定義することで、Claude Codeが「安全なデフォルト」でマイグレーション手順を生成するようになる。Expand/Migrate/ContractパターンやCONCURRENTLYインデックス追加が当たり前の選択肢になる。

CLAUDE.mdでマイグレーションポリシーを定義する——「安全なデフォルト」の強制

マイグレーションの安全性ルールをCLAUDE.mdに定義しておくことで、Claude Codeが生成するマイグレーションスクリプトが常にこの制約に従う。

# CLAUDE.md(データベースマイグレーションポリシー)
## Database Migration Policy

### 安全性要件
- 全マイグレーションはゼロダウンタイムで実施(ロック取得禁止)
- 破壊的変更(カラム削除・型変更・リネーム)は必ずExpand-Migrate-Contract 3フェーズで実施
- NOT NULL カラム追加時はデフォルト値必須(既存行を考慮)
- 大テーブル(100万行超)へのインデックス追加は必ずCONCURRENTLY使用(PostgreSQL)

### マイグレーション手順
1. ステージング環境で先行テスト(本番適用前必須)
2. ロールバックスクリプトを必ずマイグレーションと同時に作成
3. 本番適用後はアプリケーションログで異常を5分間モニタリング

### ツール別ルール
- Prisma: 本番環境は必ずprisma migrate deploy(migrate devは禁止)
- Alembic: autogenerate後は必ず手動レビュー(自動生成を盲目的に適用しない)

### 禁止パターン
- 本番環境でのprisma migrate reset(データ消去)
- LOCK TABLE を取得するマイグレーション
- トランザクション内でのテーブル再作成

「ロールバックスクリプトを必ずマイグレーションと同時に作成」のルールが特に重要だ。マイグレーションが本番で失敗したとき、ロールバック手順が事前に存在することで復旧時間が大幅に短縮される。CLAUDE.mdにこれを必須として定義することで、Claude Codeがマイグレーションを生成するたびにロールバックスクリプトも一緒に出力する。

Claude Codeへの自然言語指示パターンで、この規約がどう機能するかを確認できる。

「usersテーブルにlast_login_atカラムをTIMESTAMP WITH TIME ZONEで追加して。
既存データへの影響を考慮してください」

CLAUDE.mdが読み込まれている状態では、Claude Codeはこの指示に対して「NOT NULL制約なし(または DEFAULT NULL)、ロールバックスクリプト付き、Staging先行テスト手順付き」の安全なマイグレーションを生成する。「安全に」と毎回指定しなくても安全な出力が得られる状態になる。

Expand/Migrate/Contractパターン——破壊的変更の安全な3フェーズ手順

カラムのリネームや型変更といった破壊的変更を直接実行すると、テーブルロックが発生して大テーブルでは書き込みが数分間ブロックされる。さらに、デプロイ済みアプリが古いカラム名を参照し続けるため、マイグレーションとデプロイのタイミング次第で障害が起きる。

Expand/Migrate/Contract(E-M-C)パターンはこの問題を3フェーズで安全に解決する。

Claude Codeへの指示パターンを先に見ておくと、こう書ける。

「usersテーブルのemailカラムをemail_addressにリネームしたい。
本番DBは約1000万行あるためダウンタイムは許容できない。
Expand/Migrate/Contractパターンで3フェーズのマイグレーションスクリプトを生成して。
使用ORM: Prisma、DB: PostgreSQL。
各フェーズで:
1. Expandマイグレーション(新カラム追加・データコピー)
2. アプリコードの移行指示(新旧両対応のデプロイポイント)
3. Contractマイグレーション(旧カラム削除)
各フェーズにロールバック用のdownマイグレーションも含めて」

CLAUDE.mdでポリシーを定義していれば「emailをemail_addressにリネームして。Expand-Migrate-Contractで」だけでも同等の出力が得られる。

実際に生成される3フェーズの内容はこうなる。

フェーズ1(Expand)では新カラムを追加して既存データをコピーする。この時点でアプリは古いカラムのみを参照している状態だ。

-- マイグレーション 001: Expand
ALTER TABLE users ADD COLUMN email_address VARCHAR(255);
UPDATE users SET email_address = email WHERE email_address IS NULL;

フェーズ2(Migrate)では新旧両方のカラムに書き込む形でアプリをデプロイする。読み取りは新カラム優先、なければ旧カラムという設計にする。この段階でロールバックが最もやりやすい。

フェーズ3(Contract)ではアプリが新カラムのみを参照するようになった後で旧カラムを削除する。

-- マイグレーション 002: Contract(アプリが新カラムのみ参照するようになった後)
ALTER TABLE users DROP COLUMN email;

このパターンはMartin Fowler等の技術ブログでも広く紹介されている業界標準のアプローチだ。ロールバックが容易で各フェーズを独立してデプロイできることが、本番での安心感につながる。

Prisma MCP Server × PostgreSQL大テーブル操作——実装ツールの活用

PrismaはClaude Code・Claude Desktop向けに公式MCPサーバーを提供している(prisma.io/mcp)。PostgreSQLデータベースのスキーマ変更・マイグレーション生成・SQLクエリをClaude Codeから自然言語で操作できる。

「usersテーブルにprofile_imageカラムをText型でnullableで追加して」
「前回のスキーマ変更のマイグレーションスクリプトを生成して」
「本番DBのusersテーブルのカウントを確認して」

PrismaがClaude Code向けMCPを公式サポートしている点は重要だ。「ツールベンダーがAI支援を公式に統合した」という信頼性の裏付けになる。

開発環境と本番環境でコマンドを分けることをCLAUDE.mdで強制することが必須だ。

# 開発環境(マイグレーション生成・スキーマ変更あり)
prisma migrate dev --name "add_email_address_column"

# 本番環境(マイグレーション適用のみ。生成・スキーマ変更なし)
prisma migrate deploy

本番環境でprisma migrate devを実行すると、開発用の動作(スキーマのリセットを提案するなど)が本番データに影響する可能性がある。CLAUDE.mdに「本番環境はprisma migrate deploy必須」と書いておくことで、Claude Codeが本番用の指示を受けた際に正しいコマンドを選択する。

大テーブルへのインデックス追加はPostgreSQLのCONCURRENTLYオプションが重要だ。通常のCREATE INDEXはテーブルロックを取得するが、CONCURRENTLYを使うとロックなしで追加できる。

-- 通常(危険: テーブルロック発生)
CREATE INDEX idx_users_email ON users(email);

-- CONCURRENTLY(安全: ロックなし。時間はかかるが本番で安全)
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);

CONCURRENTLYはPostgreSQL固有の機能だ。MySQLではALGORITHM=INPLACEが近い役割を担うが挙動が異なる。記事内のコード例はPostgreSQLを前提としている。

Alembicを使うPython環境でも同様のパターンが使える。

# AlembicでのCONCURRENTLY対応
def upgrade():
    op.execute("""
        CREATE INDEX CONCURRENTLY IF NOT EXISTS
        idx_users_email ON users(email)
    """)

def downgrade():
    op.execute("DROP INDEX CONCURRENTLY IF EXISTS idx_users_email")

まとめ

データベースマイグレーションの安全性をClaude Codeで自動化する起点は、CLAUDE.mdへのマイグレーションポリシー定義だ。「Expand-Migrate-Contract必須」「CONCURRENTLYデフォルト」「ロールバックスクリプト同時作成」「Staging先行テスト必須」をCLAUDE.mdに書けば、以降はClaude Codeがそのポリシーに従ったマイグレーションを生成する。

ジュニアエンジニアがマイグレーションを書いた際にも「本番で安全な手順」がデフォルトになる状態を作れることがこのアプローチの本質だ。「シニアに確認してから本番適用」という依存を減らし、マイグレーション設計を民主化する。

まず試すなら、CLAUDE.mdに「100万行超テーブルへのインデックス追加はCONCURRENTLY必須」「破壊的変更は3フェーズで実施」の2行を追加してからマイグレーション生成を依頼してみてほしい。その2行が、本番障害を防ぐ設計の出発点になる。

コメント

タイトルとURLをコピーしました