第12章:“migration”設計:DBの変更は「履歴」で管理する📜
DBの変更って、つい「手でSQL打って終わり😌」になりがちだけど……それ、あとで必ず地獄になります🔥 この章は、DB変更を “履歴(=再現できる形)” にして、別PC・別環境でも同じ状態に戻せるようにする回だよ🧠✨
1) migrationって何?まず最短で理解しよう🚀
migration は一言でいうと、
- DB版のGitコミット📌 「いつ・何を・どう変えたか」を“ファイルとして”残す → みんな同じ順番で適用する
ここが超重要ポイント👇
- 初期化(
/docker-entrypoint-initdb.d)は最初の1回だけ動くタイプ🎬 データディレクトリが空のときしか走らない、という注意が公式に明記されてるよ。だから「2回目以降の変更」には向かない。(Docker Hub) - migrationは 「すでにDBがある状態」 でも、足りない分だけ順番に適用できるのが強み📜✨
2) まず“運用ルール”を固定(これが成果物🧷)
ここ、5つだけガチで守ると事故が激減するよ😇🛡️
- 適用済みのmigrationファイルは二度と編集しない✋ (直したいなら“新しいmigration”を追加)
- 1ファイル=1つの意図🎯(「users追加」「emailにunique追加」みたいに)
- 名前で順番が決まる🧾(番号 or タイムスタンプ)
- 本番は “上げる(up)だけ” を基本⬆️(戻す(down)は“やり方”だけ用意して慎重に)
- 適用は必ずコマンド化🎛️(手作業は禁止🙅)
3) まずは“SQLファイル派”の最小構成(超おすすめ)🧱✨
「ORMに依存しない」「中身が見える」「トラブル時に強い」ので最初はこれが楽👏
プロジェクト構成イメージ👇
project/
compose.yml
migrations/
0001_init.up.sql
0001_init.down.sql
0002_add_display_name.up.sql
0002_add_display_name.down.sql
4) 適用方法は2択!おすすめはA🥇
A) “コンテナでmigration実行”(いちばん安定)🐳✅
ここでは golang-migrate(migrate コマンド)を使うよ。
migrationを「順番に適用」する目的に特化した定番ツールで、CLIとDockerイメージが用意されてる👌
- “順番に適用する”のがツールの役割として明記されてる(GitHub)
- Docker Hubでも公式イメージとして提供されてる(Docker Hub)
4-A-1) compose.yml 例(Postgres + migrate)🐘📦
※Postgresは2026時点だと 18系が現行(2025-09-25に18リリース)として公式に告知があるよ。(PostgreSQL) (ただし “latestタグ追従”は事故りやすい ので、メジャー固定がおすすめ🔒)
services:
db:
image: postgres:18
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: apppass
POSTGRES_DB: appdb
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
migrate:
image: migrate/migrate:v4.19.1
depends_on:
- db
volumes:
- ./migrations:/migrations
entrypoint: ["migrate"]
command:
[
"-database",
"postgres://app:apppass@db:5432/appdb?sslmode=disable",
"-path",
"/migrations",
"up"
]
volumes:
pgdata:
migrateの使い方(-databaseと-pathを渡してup)は公式リポジトリのUsageに載ってるよ。(GitHub)
4-A-2) 実行コマンド🎛️
docker compose up -d db
docker compose run --rm migrate
- 2回目以降も 未適用分だけ 上がる⬆️
- 失敗したら「どのSQLで落ちたか」がログで追える🔎
4-A-3) migrationファイル例🧪
migrations/0001_init.up.sql
CREATE TABLE IF NOT EXISTS users (
id BIGSERIAL PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
migrations/0001_init.down.sql(開発で戻したい時用)
DROP TABLE IF EXISTS users;
B) TypeScriptで“自作マイグレーター”(理解が深まる)🧠🛠️
「仕組みを腹落ちさせたい」ならアリ。 ただし最初の教材としては Aが簡単&事故りにくい ので、Bは“強化”枠でOK👍
(やるなら)
schema_migrationsみたいな履歴テーブルを作る📒migrations/*.sqlを読み、未適用だけ実行する🔁- 成功したら履歴テーブルに記録する✅ という流れにするのが王道だよ✨
5) 「初期化」と「migration」を混ぜないコツ🌱📜
混ぜると混乱するので、役割分担しよう👇
- 初期化:最初の1回だけ(ユーザー作成、拡張導入、最低限の初期スキーマなど) ※空のデータディレクトリのときだけ実行、という注意がある(Docker Hub)
- migration:スキーマ変更の履歴(あとから増えるのは全部こっち)
6) よくある事故と回避策🪤🧯
事故①:昔のmigrationを“こっそり修正”して爆発💥
✅ 回避:修正は禁止。新しいmigrationを足すだけ。
事故②:ブランチAとBで「0002」が被る😇
✅ 回避:
- 番号じゃなく タイムスタンプ方式にする(例:
20260211_101530_add_xxx.up.sql)⏱️ - もしくは merge 時に片方を
0003に繰り上げる📌
事故③:Postgresのデータが消えた気がする😱
✅ 回避:マウント先を間違えない。 (公式に「ここにマウントしないと永続化されない」注意があるよ)(Docker Hub)
7) ORMを使うなら:Drizzleのmigrationはこう動く🧩✨(参考)
もしDBアクセスに Drizzle を使ってるなら、drizzle-kit が
- migrationフォルダのSQLを読み
- DB側の「適用履歴テーブル」を見て
- 未適用だけ流す という仕組みを公式で説明してるよ📜(orm.drizzle.team)
8) AIの使い方(安全に爆速化🤖🛡️)
migrationはAIが得意だけど、そのまま実行は危険⚠️ おすすめの使い方👇
✅ AIに投げると強いもの
- 「この変更に必要な migration SQL を提案して」🧠
- 「壊れる可能性(データ消える/ロック長い等)を指摘して」🔎
- 「ロールバック案(down)を考えて」⬇️
✅ AIに投げる時のテンプレ(コピペ用)🧾✨
次の変更をPostgreSQL向けのmigration SQLにして。
- 変更内容:usersテーブルにdisplay_name(TEXT)を追加(NULL許可)
- 既存データあり
- できれば短いロックで
出力は「up」と「down」を分けて。
危険ポイントも箇条書きで教えて。
9) ミニ課題(10分)🎯⏱️
0002_add_display_name.up.sqlを作る✍️docker compose run --rm migrateで適用する⬆️- もう一回同じコマンドを叩いて「何も起きない(差分なし)」を確認する✅
downは“実行はしない”で、書けるかだけ挑戦(怖ければ空でOK)😆
10) この章のまとめ(超重要3つ)🧠✨
- migrationは DB変更を“履歴ファイル”にする📜
- 初期化スクリプトは 最初の1回だけ なので、変更履歴には向かない🎬(空データ時のみ実行)(Docker Hub)
- ルールはこれだけ:「適用済みを編集しない」 が最強の安全装置🛡️🔥
次の章(データリセット手順💣➡️🧯)に行くと、開発がめちゃくちゃ気楽になるよ〜😄🎉