database-migration について
database-migration は、AI エージェントによるバージョン管理されたデータベーススキーマ変更を支援するスキルです。データベースを進化させる開発チーム向けに、ロールバック、データバックフィル、ゼロダウンタイムの手法と複数の移行フレームワークを扱います。
主な機能
- 01スキーマ変更リスクの評価
- 02アップグレードとロールバック用スクリプトの作成
- 03データバックフィルへの対応
- 04エクスパンド・アンド・コントラクト移行の計画
- 05ステージング環境での検証
- 06デプロイ後の状態監視
活用シーン
- 01新しいカラムの移行スクリプト作成
- 02データベースカラムの安全な名前変更
- 03本番環境への段階的デプロイ計画
- 04CI でのアップグレードとロールバックのテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ seb1n/ awesome-ai-agent-skills --skill database-migrationインストール後、Claude Code で/database-migrationを入力して読み込まれたか確認します。
詳細
データベースのマイグレーション
このスキルを使用すると、AI エージェントはマイグレーションフレームワークを通じて、バージョン管理されたデータベーススキーマの変更を管理できます。エージェントは、適用およびロールバック用のマイグレーションスクリプトを作成し、スキーマ変更時のデータのバックフィルを処理し、安全なマイグレーションパターンでダウンタイムゼロのデプロイを実現して、マイグレーションのワークフローを CI/CD パイプラインに統合します。Alembic(Python/SQLAlchemy)、Prisma Migrate(TypeScript/Node)、Flyway(Java/SQL)、Knex(JavaScript)などの主要なツールに対応しています。
ワークフロー
-
スキーマ変更を評価する: 列の追加、テーブルの作成、制約の変更、フィールド名の変更、データの変換など、要求された変更を分析します。変更を後方互換性のあるもの(追加)か、破壊的なもの(削除など)かに分類し、デプロイ戦略を決定します。破壊的な変更には、複数フェーズのマイグレーションが必要です。
-
マイグレーションツールを選択する: プロジェクトの技術スタックに基づいて、適切なマイグレーションフレームワークを選択します。Python/SQLAlchemy プロジェクトには Alembic、TypeScript/Prisma プロジェクトには Prisma Migrate、Java または SQL ファーストのワークフローには Flyway、Node.js/Express プロジェクトには Knex を使用します。ツールが初期化され、対象データベースに接続されていることを確認します。
-
マイグレーションスクリプトを生成する: 対応している場合は、スキーマ差分からマイグレーションを自動生成します(Alembic の autogenerate、Prisma の migrate dev)。その後、生成されたスクリプトを確認して編集します。明示的なロールバック(ダウングレード)ロジックを追加します。データのバックフィルでは、スキーマとデータの変更をアトミックに保つため、データ変換をマイグレーションに含めます。
-
ステージング環境でテストする: 本番環境を再現したステージングデータベースにマイグレーションを適用します。問題なく適用されること、既存のクエリが引き続き動作すること、ロールバックによって以前の状態に復元されることを確認します。マイグレーション後のスキーマに対して、アプリケーションのテストスイートを実行します。
-
ダウンタイムゼロの戦略でデプロイする: 本番環境では、拡張と縮小によるマイグレーションを使用します。フェーズ 1:古い構造を削除せずに、新しい列やテーブルを追加します(拡張)。フェーズ 2:古い構造と新しい構造の両方に書き込むアプリケーションコードをデプロイします。フェーズ 3:データをバックフィルします。フェーズ 4:新しい構造のみを使用するコードをデプロイします。フェーズ 5:古い列やテーブルを削除します(縮小)。これにより、ダウンタイムを防ぎ、各フェーズで安全にロールバックできます。
-
検証して監視する: デプロイ後、フレームワークのステータスコマンドでマイグレーションの状態を確認します。アプリケーションログとデータベースのパフォーマンスを監視し、問題がないか確認します。すべてのマイグレーションのメタデータが、フレームワークのバージョンテーブルに記録されていることを確認します。
対応技術
- Alembic: Python、SQLAlchemy、PostgreSQL/MySQL/SQLite
- Prisma Migrate: TypeScript/JavaScript、Prisma ORM、PostgreSQL/MySQL/SQLite/SQL Server
- Flyway: Java、SQL ベースのマイグレーション、主要な RDBMS 全般
- Knex: JavaScript/TypeScript、Node.js、PostgreSQL/MySQL/SQLite
- Django Migrations: Python、Django ORM
- Sequelize: JavaScript、Node.js ORM
使用方法
必要なスキーマ変更(例:「users テーブルに phone_number 列を追加する」)を説明し、プロジェクトで使用しているマイグレーションフレームワークを指定してください。エージェントはアップグレードとダウングレードのロジックを含むマイグレーションファイルを生成し、適用手順を示して、本番環境での安全なデプロイ戦略について助言します。
例
例 1:Alembic のマイグレーション — データのバックフィルを伴う列の追加
依頼: users テーブルに display_name 列を追加し、first_name と last_name を連結してバックフィルしてください。
マイグレーションを生成する:
alembic revision --autogenerate -m "add_display_name_to_users"
マイグレーションファイル(versions/20250115_add_display_name_to_users.py):
"""add display_name to users
Revision ID: a1b2c3d4e5f6
Revises: 9z8y7x6w5v4u
Create Date: 2025-01-15 10:30:00.000000
"""
from alembic import op
import sqlalchemy as sa
revision = "a1b2c3d4e5f6"
down_revision = "9z8y7x6w5v4u"
branch_labels = None
depends_on = None
def upgrade():
# Phase 1: Add the column as nullable (safe, no locks on reads)
op.add_column("users", sa.Column("display_name", sa.String(300), nullable=True))
# Phase 2: Backfill existing rows
users = sa.table(
"users",
sa.column("id", sa.Integer),
sa.column("first_name", sa.String),
sa.column("last_name", sa.String),
sa.column("display_name", sa.String),
)
op.execute(
users.update().values(
display_name=sa.func.concat(
users.c.first_name, " ", users.c.last_name
)
)
)
# Phase 3: Set NOT NULL after backfill is complete
op.alter_column("users", "display_name", nullable=False)
def downgrade():
op.drop_column("users", "display_name")
適用して検証する:
alembic upgrade head
alembic current # Confirms: a1b2c3d4e5f6 (head)
例 2:Prisma Migrate — Review モデルの追加
依頼: Prisma プロジェクトで、User と Product に関連付けられた Review モデルを追加してください。
prisma/schema.prisma を更新する:
model Review {
id Int @id @default(autoincrement())
rating Int @db.SmallInt
comment String? @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userId Int
productId Int
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
@@unique([userId, productId])
@@index([productId])
@@index([rating])
}
マイグレーションを生成して適用する:
npx prisma migrate dev --name add_reviews_table
生成された SQL(prisma/migrations/20250115_add_reviews_table/migration.sql):
CREATE TABLE "Review" (
"id" SERIAL NOT NULL,
"rating" SMALLINT NOT NULL,
"comment" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"userId" INTEGER NOT NULL,
"productId" INTEGER NOT NULL,
CONSTRAINT "Review_pkey" PRIMARY KEY ("id")
);
CREATE INDEX "Review_productId_idx" ON "Review"("productId");
CREATE INDEX "Review_rating_idx" ON "Review"("rating");
CREATE UNIQUE INDEX "Review_userId_productId_key" ON "Review"("userId", "productId");
ALTER TABLE "Review" ADD CONSTRAINT "Review_userId_fkey"
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE;
ALTER TABLE "Review" ADD CONSTRAINT "Review_productId_fkey"
FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE CASCADE;
ベストプラクティス
- **すべてのマイグレーションに、明示的なダウングレード/ロールバック処理を必ず記述してください。**デプロイで問題が発生した場合に、安全に元に戻せるようにします。切迫した状況で手動でマイグレーションを取り消せると決めつけないでください。
- **expand-and-contract の手法を使い、マイグレーションに後方互換性を持たせてください。**まず新しい構造を追加し、次にデータを移行します。その後、新しいコードのデプロイが完全に完了してから、別のマイグレーションで古い構造を削除します。
- **自動生成されたマイグレーションを、確認せずに実行しないでください。**自動検出ツールは、名前が変更されたフィールドを見つけたときに、カラムの削除など破壊的な操作を生成することがあります。生成された SQL は必ず確認してください。
- サポートされている場合は、DDL にトランザクションを使用してください(PostgreSQL は DDL をトランザクションでラップしますが、MySQL はしません)。トランザクションに対応していない DDL データベースでは、部分的な失敗からの復旧を計画してください。
- **マイグレーションは小さく、目的を絞ったものにしてください。**マイグレーションファイルごとに、論理的な変更を 1 つだけ行います。これにより、ロールバックの範囲を細かくでき、問題の原因となったマイグレーションを特定しやすくなります。
- **CI で使い捨てのデータベースに対してマイグレーションを実行し、本番環境に到達する前にエラーを検出してください。**テストにはアップグレードとダウングレードの両方の経路を含めてください。
エッジケース
- **大きなテーブルのマイグレーション:**数百万行あるテーブルに、デフォルト値付きの NOT NULL カラムを追加すると、古いバージョンの PostgreSQL では数分間テーブルがロックされることがあります。
ADD COLUMN ... DEFAULT ... NOT NULL(PostgreSQL 11 以降ではロックフリー)を使用するか、まず NULL 許容として追加し、バッチでバックフィルしてから NOT NULL に設定してください。 - **カラム名の変更:**多くのマイグレーションツールは名前の変更を削除+追加として扱うため、データが失われます。Alembic の
op.alter_column()または生のALTER TABLE ... RENAME COLUMNを使って、実際の名前変更を行ってください。適用前に、生成されたマイグレーションを確認してください。 - **同時マイグレーション:**複数インスタンスでのデプロイでは、マイグレーションを実行するインスタンスが 1 つだけになるようにしてください。アドバイザリロック(Flyway と Alembic がサポートしています)を使用するか、アプリケーションインスタンスのローリングデプロイ前に専用のステップとしてマイグレーションを実行するデプロイパイプラインを使用してください。
- **Enum 型の変更:**PostgreSQL で値を Enum に追加する操作は、トランザクションに対応していません。新しい Enum 型を作成し、カラムを移行してから、古い型を削除してください。あるいは、変更しやすいように CHECK 制約付きの VARCHAR を使用してください。
- **データのみのマイグレーション:**スキーマを変更せずにデータを変換する必要がある場合(例:カラムの値を暗号化する場合)でも、環境間でバージョン管理と再現ができるよう、マイグレーションファイルを使用してください。