メインコンテンツまでスキップ

第12章:“migration”設計:DBの変更は「履歴」で管理する📜

DBの変更って、つい「手でSQL打って終わり😌」になりがちだけど……それ、あとで必ず地獄になります🔥 この章は、DB変更を “履歴(=再現できる形)” にして、別PC・別環境でも同じ状態に戻せるようにする回だよ🧠✨


1) migrationって何?まず最短で理解しよう🚀

migration は一言でいうと、

  • DB版のGitコミット📌 「いつ・何を・どう変えたか」を“ファイルとして”残す → みんな同じ順番で適用する

ここが超重要ポイント👇

  • 初期化(/docker-entrypoint-initdb.d)は最初の1回だけ動くタイプ🎬 データディレクトリが空のときしか走らない、という注意が公式に明記されてるよ。だから「2回目以降の変更」には向かない。(Docker Hub)
  • migrationは 「すでにDBがある状態」 でも、足りない分だけ順番に適用できるのが強み📜✨

2) まず“運用ルール”を固定(これが成果物🧷)

ここ、5つだけガチで守ると事故が激減するよ😇🛡️

  1. 適用済みのmigrationファイルは二度と編集しない✋ (直したいなら“新しいmigration”を追加)
  2. 1ファイル=1つの意図🎯(「users追加」「emailにunique追加」みたいに)
  3. 名前で順番が決まる🧾(番号 or タイムスタンプ)
  4. 本番は “上げる(up)だけ” を基本⬆️(戻す(down)は“やり方”だけ用意して慎重に)
  5. 適用は必ずコマンド化🎛️(手作業は禁止🙅)

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-migratemigrate コマンド)を使うよ。 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分)🎯⏱️

  1. 0002_add_display_name.up.sql を作る✍️
  2. docker compose run --rm migrate で適用する⬆️
  3. もう一回同じコマンドを叩いて「何も起きない(差分なし)」を確認する✅
  4. down は“実行はしない”で、書けるかだけ挑戦(怖ければ空でOK)😆

10) この章のまとめ(超重要3つ)🧠✨

  • migrationは DB変更を“履歴ファイル”にする📜
  • 初期化スクリプトは 最初の1回だけ なので、変更履歴には向かない🎬(空データ時のみ実行)(Docker Hub)
  • ルールはこれだけ:「適用済みを編集しない」 が最強の安全装置🛡️🔥

次の章(データリセット手順💣➡️🧯)に行くと、開発がめちゃくちゃ気楽になるよ〜😄🎉