読み込み中...
この記事は、ひとりでつくるSaaS - 設計・実装・運用の記録 Advent Calendar 2025 の8日目の記事です。
昨日の記事では「データベースのID設計」について書きました。この記事では、DBマイグレーションの運用方法について解説します。
:::message
この記事で紹介する方法は、試行錯誤の末にたどり着いた独自のやり方です。もっと良い方法があれば、ぜひコメントで教えてください。
:::
DBマイグレーションには、いくつかの管理方法があります。
Drizzle ORM、Prisma、TypeORMなどのORMには、マイグレーション機能が組み込まれています。
# Drizzle ORMの場合
npx drizzle-kit generate # スキーマからマイグレーションを生成
npx drizzle-kit migrate # マイグレーションを適用たとえば、TypeScriptでテーブル定義を書くと、その変更を検出して ALTER TABLE などのSQLを自動生成してくれます。
メリット:
課題:
ORMのマイグレーション機能は単一環境の管理には便利ですが、開発環境と本番環境を分けて運用する場合、以下のような課題があります。
私が開発しているMemoreruでも同じ課題に直面し、試行錯誤の結果、現在のような運用ルールにたどり着きました。
マイグレーションファイルはClaude Codeに作成してもらい、連番で管理しています。
database/migrations/
├── sql/
│ ├── 001_create_users_table.sql
│ ├── 002_create_posts_table.sql
│ ├── 003_add_user_profile.sql
│ ├── 004_add_status_column.sql
│ └── ...
├── scripts/
│ └── migrate.sh
├── status.json
└── README.md
連番管理のメリット:
Drizzle ORMのマイグレーション生成機能(drizzle-kit generate)は使わず、SQLファイルを直接作成しています。ただし、スキーマ定義自体はDrizzle ORMで管理しているため、型安全性は保たれています。
SQLファイルを直接管理する理由:
Memoreruでは、開発環境と本番環境で同じスクリプトを使ってマイグレーションを適用しています。
# 開発環境
./database/migrations/scripts/migrate.sh dev 004_add_status_column.sql
# 本番環境
./database/migrations/scripts/migrate.sh pro 004_add_status_column.sql共通スクリプトのメリット:
| 項目 | 開発環境 | 本番環境 |
|---|---|---|
| 接続情報 | .env.localから自動読み込み | 毎回手動入力 |
| バックアップ推奨 | なし | 警告表示 |
本番環境の接続文字列を毎回入力するのは手間ですが、これが安全策として機能します。誤って開発環境のつもりで本番を操作する事故を防げます。
また、Claude Codeには本番環境のDB接続情報を教えていません。これにより、AIが誤って本番DBを操作するリスクを排除しています。
なお、どちらの環境でもマイグレーション適用前にpgAdminのバックアップ機能でバックアップを取得しています。万が一のロールバックに備えておくことが大切です。
スクリプトには以下のような安全策を組み込んでいます。
logs/migrations/ に保存し、後から確認できるようにするスクリプトの具体的な実装はClaude Codeに作成してもらいました。要件を伝えれば、環境に合わせたスクリプトを生成してくれます。
開発環境と本番環境の適用状況を1つのファイルで管理しています。
{
"lastUpdated": "2025-12-04",
"environments": {
"dev": {
"name": "開発環境",
"lastApplied": "004_add_status_column",
"appliedAt": "2025-12-04"
},
"pro": {
"name": "本番環境",
"lastApplied": "003_add_user_profile",
"appliedAt": "2025-11-30"
}
},
"pending": {
"pro": ["004_add_status_column"]
}
}# pendingの一覧を表示
jq '.pending.pro' database/migrations/status.json
# => ["004_add_status_column"]開発環境で適用したマイグレーションのうち、本番にまだ適用していないものが一目で分かります。
マイグレーション適用後、スクリプトが自動的に status.json を更新します。手動で更新する必要がないため、更新忘れを防げます。
カラム名の変更やテーブル構造の変更は、一度に行わず段階的に実行します。
-- ステップ1: 新しいカラムを追加
ALTER TABLE contents ADD COLUMN new_name TEXT;
-- ステップ2: データを移行
UPDATE contents SET new_name = old_name;
-- ステップ3: 古いカラムを削除(別のマイグレーションで)
ALTER TABLE contents DROP COLUMN old_name;ステップ2と3の間にアプリケーションの動作確認を挟むことで、問題があっても影響を最小限にできます。
重要なマイグレーションには、ロールバック用のSQLもコメントで残しておきます。
-- マイグレーション
ALTER TABLE contents ADD COLUMN status TEXT DEFAULT 'draft';
-- ロールバック(必要時のみ実行)
-- ALTER TABLE contents DROP COLUMN status;CLAUDE.mdにマイグレーション運用のルールを明記しています。
## マイグレーション運用
- 直接psqlでSQLを実行しない
- 必ずmigrate.shスクリプト経由で適用
- 本番適用前に開発環境でリハーサル
- 適用後はstatus.jsonをコミットAIエージェントが誤って直接SQLを実行することを防いでいます。
DBマイグレーション運用から得た学びをまとめます。
うまくいっていること:
注意が必要なこと:
個人開発でも、最初からルールを決めておくことで、後から困ることが少なくなります。
明日は「NextAuth.jsからBetter Authへ:認証ライブラリを移行した理由」について解説します。
シリーズの他の記事
コメント