Claude Codeでデータベース移行・マイグレーション|安全なスキーマ変更の実践

kento_morota 17分で読めます

データベースのスキーマ変更は、アプリケーション開発で最もリスクの高い作業の一つです。テーブルの追加やカラムの変更は、一歩間違えるとデータの損失やサービスのダウンタイムにつながります。Claude Codeを活用すれば、マイグレーションファイルの自動生成、安全なスキーマ変更の設計、ロールバック戦略の策定まで、データベース移行の全工程をAIの支援のもとで安全に進めることができます。本記事では、PrismaとTypeORMの実例を交えながら、Claude Codeを使った安全なデータベースマイグレーションの実践手法を解説します。

データベースマイグレーションにおけるClaude Codeの活用

Claude Codeは、データベーススキーマの現在の状態を把握し、要件に応じた安全なマイグレーション手順を設計する能力を持っています。

Claude Codeがマイグレーションで担える役割

  • マイグレーションファイルの生成:スキーマ変更を反映したマイグレーションコードを自動作成
  • 安全性の検証:破壊的変更の検出と安全な実行手順の提案
  • ロールバック計画の策定:万が一の問題に備えた元に戻す手順の作成
  • データ移行スクリプトの作成:スキーマ変更に伴うデータ変換ロジックの生成
  • アプリケーションコードとの整合性確認:スキーマ変更に合わせたコード修正

CLAUDE.mdでデータベース規約を定義する

CLAUDE.mdにデータベースの規約を記載しておくことで、一貫したマイグレーション戦略を維持できます。

# CLAUDE.md のデータベース関連設定
## データベース
- ORM: Prisma
- DB: PostgreSQL 15
- マイグレーション命名: YYYYMMDDHHMMSS_description
- テーブル名: スネークケース・複数形(users, order_items)
- カラム名: スネークケース(created_at, updated_at)
- すべてのテーブルに id, created_at, updated_at を含める
- 外部キーにはインデックスを設定する
- 破壊的変更は段階的に実行する(後述のゼロダウンタイム戦略)

Prismaでのマイグレーション実践

Prismaは、TypeScript/Node.js環境で最も広く使われているORMの一つです。Claude Codeを使えば、Prismaスキーマの変更からマイグレーションの実行まで、スムーズに進められます。

スキーマ変更とマイグレーション生成

> 注文管理機能を追加するために、以下のテーブルを作成して:
> - orders(注文テーブル): ユーザーID, ステータス, 合計金額, 注文日
> - order_items(注文明細テーブル): 注文ID, 商品ID, 数量, 単価
> Prismaスキーマを更新して、マイグレーションファイルを生成して。

Claude Codeは以下のようなPrismaスキーマを生成します。

// prisma/schema.prisma(追加分)
model Order {
  id          String      @id @default(cuid())
  userId      String      @map("user_id")
  status      OrderStatus @default(PENDING)
  totalAmount Int         @map("total_amount")
  orderedAt   DateTime    @default(now()) @map("ordered_at")
  createdAt   DateTime    @default(now()) @map("created_at")
  updatedAt   DateTime    @updatedAt @map("updated_at")

  user        User        @relation(fields: [userId], references: [id])
  orderItems  OrderItem[]

  @@map("orders")
  @@index([userId])
  @@index([status])
}

model OrderItem {
  id        String   @id @default(cuid())
  orderId   String   @map("order_id")
  productId String   @map("product_id")
  quantity  Int
  unitPrice Int      @map("unit_price")
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @updatedAt @map("updated_at")

  order     Order    @relation(fields: [orderId], references: [id])
  product   Product  @relation(fields: [productId], references: [id])

  @@map("order_items")
  @@index([orderId])
  @@index([productId])
}

enum OrderStatus {
  PENDING
  CONFIRMED
  SHIPPED
  DELIVERED
  CANCELLED
}

スキーマが生成されたら、Claude Codeがマイグレーションコマンドを実行して確認します。

> npx prisma migrate dev --name add_orders_tables を実行して、
> 生成されたSQLを確認させて

既存テーブルへのカラム追加

既存テーブルへの変更は、新規テーブル作成よりもリスクが高くなります。Claude Codeは安全な変更手順を提案します。

> usersテーブルに phone_number カラム(任意項目)を追加して。
> 既存データへの影響がないことを確認して。

Claude Codeは以下のことを確認した上でマイグレーションを生成します。

  1. 新カラムがNULLABLEまたはデフォルト値を持つこと(既存行への影響回避)
  2. アプリケーションコードで新カラムの参照箇所を特定
  3. 関連するテストの更新が必要かどうかを確認

TypeORMでのマイグレーション実践

TypeORMを使用しているプロジェクトでも、Claude Codeは適切なマイグレーションコードを生成できます。

マイグレーションファイルの自動生成

> TypeORMで商品テーブルに在庫管理用のカラムを追加するマイグレーションを作成して。
> stock_quantity(整数、デフォルト0)と
> low_stock_threshold(整数、デフォルト10)を追加して。
// src/migrations/1711100000000-AddStockColumnsToProducts.ts
import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm';

export class AddStockColumnsToProducts1711100000000
  implements MigrationInterface
{
  public async up(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.addColumn(
      'products',
      new TableColumn({
        name: 'stock_quantity',
        type: 'integer',
        default: 0,
        isNullable: false,
      })
    );

    await queryRunner.addColumn(
      'products',
      new TableColumn({
        name: 'low_stock_threshold',
        type: 'integer',
        default: 10,
        isNullable: false,
      })
    );

    // 在庫が少ない商品を素早く検索するためのインデックス
    await queryRunner.query(
      `CREATE INDEX idx_products_low_stock
       ON products (stock_quantity)
       WHERE stock_quantity <= low_stock_threshold`
    );
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query('DROP INDEX IF EXISTS idx_products_low_stock');
    await queryRunner.dropColumn('products', 'low_stock_threshold');
    await queryRunner.dropColumn('products', 'stock_quantity');
  }
}

注目すべきは、upメソッドでの変更に対応するdownメソッド(ロールバック処理)が正確に生成されている点です。カラムの削除順序やインデックスの削除も適切に処理されています。

安全なスキーマ変更の設計パターン

本番環境でのスキーマ変更には特別な注意が必要です。Claude Codeは、ゼロダウンタイムでスキーマを変更するためのパターンを提案できます。

破壊的変更を段階的に実行する

カラムの名前変更やデータ型の変更など、破壊的な変更は一度に行うべきではありません。Claude Codeは以下のような段階的な移行手順を提案します。

> products テーブルの price カラム(INT型)を
> price_in_yen(DECIMAL型)に変更したい。
> 安全な移行手順を設計して。

Claude Codeが提案する段階的な移行手順は以下の通りです。

ステップ1: 新カラムの追加(マイグレーション1)

-- マイグレーション1: 新カラムを追加
ALTER TABLE products ADD COLUMN price_in_yen DECIMAL(10,2);

-- 既存データの移行
UPDATE products SET price_in_yen = CAST(price AS DECIMAL(10,2));

ステップ2: アプリケーションコードの更新

アプリケーションコードを更新し、新旧両方のカラムに書き込むようにします(デュアルライト)。読み取りは新カラムから行います。

ステップ3: 旧カラムの削除(マイグレーション2)

-- マイグレーション2: 十分な検証期間の後に旧カラムを削除
ALTER TABLE products DROP COLUMN price;

この段階的アプローチにより、サービスを停止することなく安全にスキーマを変更できます。

大規模テーブルへの安全なインデックス追加

数百万行を超える大規模テーブルにインデックスを追加する場合、通常のCREATE INDEXはテーブルロックを引き起こします。Claude Codeは安全な方法を提案します。

> 1000万行ある orders テーブルに status カラムのインデックスを追加したい。
> 本番環境でロックなしで実行できるマイグレーションを書いて。
-- PostgreSQLの場合: CONCURRENTLYオプションでロックなしインデックス作成
CREATE INDEX CONCURRENTLY idx_orders_status ON orders (status);

-- 注意: CONCURRENTLYはトランザクション内では使用できないため、
-- マイグレーションツールのトランザクション設定を無効にする必要がある

ロールバック戦略の設計

マイグレーションが失敗した場合や、デプロイ後に問題が発見された場合のロールバック戦略は、事前に計画しておく必要があります。

ロールバック可能なマイグレーション設計

Claude Codeにロールバックを考慮したマイグレーション設計を依頼できます。

> 以下の変更に対して、ロールバック可能なマイグレーションを設計して:
> 1. users テーブルに email_verified カラム追加
> 2. user_settings テーブル新規作成
> 3. users テーブルの settings JSONカラムを user_settings に移行
> それぞれのロールバック手順も含めて。

Claude Codeは各ステップのロールバック手順を明確にしたマイグレーションを生成します。特にデータ移行を伴う変更では、ロールバック時のデータ復元手順も含めた設計を行います。

バックアップ戦略との連携

破壊的な変更を行う前には、必ずバックアップを取得すべきです。Claude Codeにバックアップスクリプトの生成も依頼できます。

> マイグレーション実行前のバックアップスクリプトと、
> 問題発生時のリストア手順を作成して。
> 対象はusersテーブルとordersテーブル。
#!/bin/bash
# backup-before-migration.sh
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="./backups/${TIMESTAMP}"
mkdir -p $BACKUP_DIR

echo "バックアップ開始: ${TIMESTAMP}"

# テーブル単位のバックアップ
pg_dump $DATABASE_URL \
  --table=users \
  --table=orders \
  --format=custom \
  --file="${BACKUP_DIR}/pre_migration.dump"

echo "バックアップ完了: ${BACKUP_DIR}/pre_migration.dump"
echo "リストアコマンド:"
echo "pg_restore -d \$DATABASE_URL ${BACKUP_DIR}/pre_migration.dump"

データ移行とETL処理

スキーマ変更に伴うデータの変換・移行(ETL処理)は、マイグレーションの中でも特に注意が必要な工程です。

大量データの段階的移行

数百万行のデータを一度に移行すると、長時間のロックやメモリ不足の問題が発生します。Claude Codeはバッチ処理による段階的な移行スクリプトを生成します。

> users テーブルの full_name カラムを first_name と last_name に分割する
> データ移行スクリプトを書いて。500万行あるので、バッチ処理で安全に実行して。
// src/scripts/migrate-user-names.ts
import { prisma } from '../lib/prisma';

const BATCH_SIZE = 1000;

async function migrateUserNames() {
  let processedCount = 0;
  let lastId = '';

  while (true) {
    const users = await prisma.user.findMany({
      where: {
        firstName: null,
        ...(lastId ? { id: { gt: lastId } } : {}),
      },
      orderBy: { id: 'asc' },
      take: BATCH_SIZE,
      select: { id: true, fullName: true },
    });

    if (users.length === 0) break;

    const updates = users.map((user) => {
      const [firstName, ...lastNameParts] = (user.fullName || '').split(' ');
      return prisma.user.update({
        where: { id: user.id },
        data: {
          firstName: firstName || '',
          lastName: lastNameParts.join(' ') || '',
        },
      });
    });

    await prisma.$transaction(updates);

    processedCount += users.length;
    lastId = users[users.length - 1].id;

    console.log(`処理済み: ${processedCount}件`);

    // DBへの負荷を軽減するための待機
    await new Promise((resolve) => setTimeout(resolve, 100));
  }

  console.log(`移行完了: 合計 ${processedCount}件`);
}

migrateUserNames().catch(console.error);

カーソルベースのページネーション、バッチ処理、エラーハンドリング、進捗ログなど、本番運用に必要な要素が網羅されています。

アプリケーションコードとの連携

データベーススキーマの変更は、必ずアプリケーションコードの変更を伴います。Claude Codeは両方の変更を一貫して管理できます。

スキーマ変更に伴うコード修正の自動化

> ordersテーブルに discount_amount カラムを追加した。
> 関連するすべてのアプリケーションコードを更新して。
> API層、サービス層、テストコードを含めて。

Claude Codeは以下のファイルを自動で特定し、更新します。

  • Prismaスキーマ:モデル定義の更新
  • 型定義:TypeScript型の更新
  • APIエンドポイントREST APIのリクエスト/レスポンス処理
  • サービス層:ビジネスロジックの更新
  • バリデーション:入力検証スキーマの更新
  • テストコード:関連テストの更新

複数ファイルの同時編集機能により、これらの変更を一貫して行うことができます。

モノレポでのスキーマ変更の伝播

モノレポ環境では、スキーマ変更が複数のパッケージに影響することがあります。Claude Codeは依存関係を追跡し、すべての影響範囲を特定した上で変更を伝播させます。

> Prismaスキーマの変更を packages/api と packages/admin の両方に反映して。
> 型の再生成とコードの更新を合わせて行って。

まとめ

Claude Codeを活用したデータベースマイグレーションは、リスクの高いスキーマ変更を安全かつ効率的に進めるための強力な手法です。本記事で解説した主要なポイントを振り返ります。

  • CLAUDE.mdにDB規約を定義して、一貫したマイグレーション戦略を維持する
  • Prisma/TypeORMのマイグレーションファイルを自動生成し、ロールバック処理も含める
  • 破壊的変更は段階的に実行し、ゼロダウンタイムでスキーマを移行する
  • ロールバック計画を事前に策定し、問題発生時に迅速に対応できるようにする
  • 大量データの移行はバッチ処理で安全に実行する
  • アプリケーションコードとの連携で、スキーマ変更を一貫して管理する

データベースマイグレーションは慎重さが求められる作業ですが、Claude Codeの支援によりリスクを最小限に抑えつつ、開発速度を向上させることができます。Gitワークフローによるマイグレーションファイルの適切なバージョン管理や、テスト自動生成によるマイグレーション後の動作検証と組み合わせて、安全なデータベース運用を実現してください。

#Claude Code#データベース#マイグレーション
共有:
無料メルマガ

週1回、最新の技術記事をお届け

AI・クラウド・開発の最新記事を毎週月曜にメールでお届けします。登録は無料、いつでも解除できます。

プライバシーポリシーに基づき管理します

AI活用のヒントをお探しですか?お気軽にご相談ください。

まずは話だけ聞いてもらう