- 技術 💻
リリース手順書の書き方|切り戻し付きテンプレートと判定表
リリース手順書とは、本番環境を変更する作業を「誰が・いつ・何を・どう確認し・うまくいかなければどう戻すか」まで決めて書いた文書です。良い手順書の条件は、①作業者以外の人が読んでも同じ操作ができる、②各ステップに期待結果と判断基準があり「次に進んでよいか」を迷わない、③切り戻しの手順と「切り戻すと決める基準」が最初から書いてある、の3つです。この3つがそろうと、当日の判断は「手順書どおりか」の確認だけになります。
この記事では、構成の早見表、コピペで使える表形式のテンプレート(Markdown)、架空の会員制タスク管理サービスの記入例を丸ごと1本に加えて、上位の解説記事にはないGo/No-Go判定表と切り戻し判断の基準、PostgreSQL 16.13 で「バックアップ取得 → マイグレーション適用 → 動作確認 → 切り戻し」を実際に実行したログを載せます。リリース方式ごとに増える項目、連絡文のテンプレート、抜け漏れチェックリストまで、この1本で手順書が書けるようにまとめました。
リリース手順書の構成早見表
| 章 | 書くこと | 例 | 抜けると起きること |
|---|---|---|---|
| 1. 概要 | リリース名・バージョン、目的、変更内容の要約、対象システム、リリース方式 | v2.5.0:タスクの優先度とラベル機能。一斉切替(作業枠60分) | 想定外の事象が起きたとき「何のための作業か」から判断できない |
| 2. 体制と連絡先 | 責任者(判断者)・作業者・確認者・連絡担当、連絡手段、エスカレーション先 | 判断:佐藤 / 作業:鈴木 / 確認:高橋 / 連絡:田中 | 深夜に「誰に聞けばいいか」で止まる |
| 3. スケジュール | 判定・開始・各作業・確認・切り戻し判断の締切・終了の予定時刻 | 21:30 判定 → 22:00 開始 → 22:45 切り戻し締切 → 23:00 終了 | 時間切れに気づかず作業枠を超える |
| 4. 事前準備 | 前日までの準備(配布物、バックアップ先、権限、告知)と Go/No-Go 判定表 | タグ v2.5.0 確定、切り戻しをステージングで検証済み | 当日「権限がない」「ファイルがない」で始められない |
| 5. 作業手順 | 表形式。No・作業・コマンドまたは操作・期待結果・判断基準・所要時間・担当・実施チェック | 本記事のテンプレート | 手順が記憶頼みになり、順番や確認が抜ける |
| 6. 確認 | 動作確認の項目、確認用 SQL・URL、合格条件 | 件数一致、代表クエリが返る、画面でログインと保存 | 「たぶん大丈夫」で公開し翌朝に気づく |
| 7. 切り戻し | 切り戻す基準(時間・影響・データ整合)と切り戻し手順(表形式) | 22:45 までに確認が終わらなければ逆マイグレーション | 戻し方を当日に考えることになる |
| 8. 連絡テンプレート | 開始・完了・切り戻し(・延期)の定型文と宛先 | 本記事のテンプレート | 連絡の内容が人によって変わり、受け手が誤解する |
| 9. 記録 | 実績時刻、結果、発生した事象と対応、次回への改善 | 22:03 開始、22:48 完了、ロック待ち1回 | 次回も同じことでつまずく |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「リリース手順書の書き方|切り戻し付きテンプレートと判定表」
コピペ用テンプレート(Markdown)
まずはこのテンプレートを、Wiki・リポジトリの docs・チケットにそのまま貼って使ってください。作業手順は表形式にし、1行につき1つの操作、必ず「期待結果」と「判断基準」をセットで書くのがコツです。判断基準は「期待結果でなければ次に進まず、○○する」の形にすると、当日の迷いがなくなります。
# リリース手順書:{サービス名} v{X.Y.Z}
## 1. 概要
- リリース名 / バージョン:
- 目的(何ができるようになるか):
- 変更内容の要約(アプリ / DB / インフラ / 設定):
- 対象システム・環境:
- リリース方式:一斉切替 / メンテナンス画面 / Blue/Green / カナリア / フィーチャーフラグ
- 関連チケット・プルリクエスト:
## 2. 体制と連絡先
| 役割 | 氏名 | 連絡手段 | 備考 |
|---|---|---|---|
| リリース責任者(Go/No-Go と切り戻しの判断) | | | |
| 作業者 | | | |
| 確認者(作業者とは別の人) | | | |
| 連絡担当 | | | |
| エスカレーション先 | | | |
- 連絡チャンネル: - 通話ブリッジ:
## 3. スケジュール
| 時刻 | 内容 | 担当 |
|---|---|---|
| | Go/No-Go 判定 | |
| | 開始連絡・作業開始 | |
| | 動作確認 | |
| | 切り戻し判断の締切(= 枠終了 − 切り戻し所要時間) | |
| | 完了連絡・作業枠終了 | |
## 4. 事前準備(前日まで)
- [ ] 配布物(タグ / イメージ / パッケージ)が確定している
- [ ] マイグレーションと切り戻し手順をステージングで実行し、戻ることを確認した
- [ ] バックアップの取得先と容量、作業者・確認者の本番権限を確認した
- [ ] 利用者向け告知・関係部署への連絡、監視の一時停止と再開の方法を確認した
### Go/No-Go 判定表
| 判定項目 | Go の基準 | 確認方法 | 判定者 | 結果 |
|---|---|---|---|---|
| テスト | | | | Go / No-Go |
| 配布物の凍結 | | | | Go / No-Go |
| 切り戻し検証 | | | | Go / No-Go |
| バックアップ | | | | Go / No-Go |
| 体制 | | | | Go / No-Go |
| 環境の状態 | | | | Go / No-Go |
| 時間 | | | | Go / No-Go |
| 周知 | | | | Go / No-Go |
1つでも No-Go なら延期し、延期の連絡を行う。
## 5. 作業手順
| No | 作業 | コマンドまたは操作 | 期待結果 | 判断基準(期待どおりでないとき) | 所要時間 | 担当 | 実施 |
|---|---|---|---|---|---|---|---|
| 1 | | | | | | | [ ] |
| 2 | | | | | | | [ ] |
## 6. 確認
| No | 確認項目 | 方法(SQL / URL / 操作) | 合格条件 | 結果 | 確認者 |
|---|---|---|---|---|---|
| C1 | | | | | |
## 7. 切り戻し
### 切り戻す基準
- 時間:{切り戻し判断の締切時刻} までに「6. 確認」が完了しない
- 影響:{主要機能} が使えない、エラー率が {基準} を超える
- データ整合:件数・合計などが事前確認の値と一致しない
### 切り戻し手順
| No | 作業 | コマンドまたは操作 | 期待結果 | 判断基準 | 所要時間 | 担当 | 実施 |
|---|---|---|---|---|---|---|---|
| R1 | | | | | | | [ ] |
## 8. 連絡テンプレート
(開始 / 完了 / 切り戻し / 延期 の定型文と宛先)
## 9. 記録
| 項目 | 内容 |
|---|---|
| 開始・終了の実績時刻 | |
| 結果(完了 / 切り戻し / 一部完了) | |
| 発生した事象と対応 | |
| 次回への改善 | |
記入例(架空の会員制タスク管理サービス)
DB のマイグレーションを伴うリリースを、テンプレートに沿って丸ごと書いた例です。作業手順のコマンドと期待結果は、後半の「実際に実行したログ」と対応しています。
# リリース手順書:TaskBoard v2.5.0
## 1. 概要
- リリース名 / バージョン:TaskBoard v2.5.0(タスクの優先度・ラベル機能)
- 目的:会員がタスクに優先度(1〜5)とラベルを付けられるようにする
- 変更内容の要約:
- アプリ:API と画面の追加(v2.4.0 → v2.5.0)
- DB:tasks に priority 列を追加、labels / task_labels テーブルを追加、
期限3日以内の未完了タスクの priority を 1 に更新(既存データ移行)
- 設定:環境変数 FEATURE_LABELS=true を追加
- 対象システム・環境:本番 Web サーバー 2台、PostgreSQL 16.13(DB t67)
- リリース方式:一斉切替(メンテナンス画面あり、作業枠 60 分)
- 関連チケット:TB-412、PR #388(アプリ)、PR #391(マイグレーション)
## 2. 体制と連絡先
| 役割 | 氏名 | 連絡手段 | 備考 |
|---|---|---|---|
| リリース責任者 | 佐藤 | #release-taskboard / 携帯 | Go/No-Go と切り戻しを判断 |
| 作業者 | 鈴木 | 同上 | コマンド実行 |
| 確認者 | 高橋 | 同上 | 確認 SQL と画面確認。作業者と別の人 |
| 連絡担当 | 田中 | 同上 | 開始・完了・切り戻しの連絡 |
| エスカレーション先 | 開発部長 | 携帯 | 23:00 を超える見込みのとき |
- 連絡チャンネル:#release-taskboard(時刻つきで逐次投稿) - 通話ブリッジ:22:00〜常時接続
## 3. スケジュール(2026-10-08 木)
| 時刻 | 内容 | 担当 |
|---|---|---|
| 21:30 | Go/No-Go 判定 | 佐藤 |
| 21:50 | 開始連絡 | 田中 |
| 22:00 | メンテナンス画面へ切替、アプリ停止 | 鈴木 |
| 22:05 | 事前確認 SQL、バックアップ取得 | 鈴木 / 高橋 |
| 22:20 | マイグレーション適用、アプリ v2.5.0 起動 | 鈴木 |
| 22:30 | 動作確認(SQL・画面) | 高橋 |
| 22:45 | 切り戻し判断の締切(切り戻し所要 15 分) | 佐藤 |
| 22:50 | メンテナンス画面解除、完了連絡 | 鈴木 / 田中 |
| 23:00 | 作業枠終了 | 全員 |
## 4. 事前準備(前日まで)
- [x] タグ v2.5.0(コミット 8c1d2f0)を確定。10/6 以降の変更なし
- [x] ステージングでマイグレーション適用 → 逆マイグレーションを実行し、元に戻ることを確認(10/6、鈴木)
- [x] バックアップ先 /backup の空き容量 120GB(DB サイズ 8GB)。鈴木・高橋の DB 接続(ロール t67)を確認
- [x] 利用者向け告知(10/1 掲示)、サポート窓口へ連絡(10/7)、監視のメンテナンスモード(22:00〜23:00)を予約
### Go/No-Go 判定表(21:30 実施)
| 判定項目 | Go の基準 | 確認方法 | 判定者 | 結果 |
|---|---|---|---|---|
| テスト | ステージングの受け入れテスト 48 件が全件合格 | テスト報告書 | 高橋 | Go |
| 配布物の凍結 | タグ v2.5.0 のコミットが PR #388 のマージ結果と一致 | git tag -v | 鈴木 | Go |
| 切り戻し検証 | ステージングで逆マイグレーションを実行済み | 実施記録 10/6 | 鈴木 | Go |
| バックアップ | 取得先の空き容量が DB サイズの 3 倍以上 | df -h | 鈴木 | Go |
| 体制 | 4 名全員が 21:30 に集合、連絡先が最新 | 体制表 | 佐藤 | Go |
| 環境の状態 | 未対応のアラートがなく、1 分以上のトランザクションがない | 監視画面、事前確認 SQL | 高橋 | Go |
| 時間 | 作業+確認+切り戻し(30+15+15 分)が枠 60 分に収まる | スケジュール | 佐藤 | Go |
| 周知 | 告知が掲示され、サポート窓口が把握している | 掲示の記録 | 田中 | Go |
## 5. 作業手順
| No | 作業 | コマンドまたは操作 | 期待結果 | 判断基準(期待どおりでないとき) | 所要時間 | 担当 | 実施 |
|---|---|---|---|---|---|---|---|
| 1 | 開始連絡 | #release-taskboard に「開始」定型文を投稿 | 投稿される | — | 1分 | 田中 | [ ] |
| 2 | メンテナンス画面へ切替、アプリ停止 | LB でメンテナンス画面を有効化 → systemctl stop taskboard(2台) | トップがメンテナンス画面。systemctl status が inactive | 2回で止まらなければ責任者へ | 3分 | 鈴木 | [ ] |
| 3 | 事前確認 SQL | psql -f 05_precheck.sql | version=20260915_002_add_due_on、tasks_count=1000、long_transactions=0 | version が違えば中止。long_transactions が 0 でなければ終了を待つ | 2分 | 高橋 | [ ] |
| 4 | バックアップ取得 | pg_dump -Fc -f backup_before_v2.5.0_日時.dump → ls -lh → pg_restore --list | ファイルが作られ、--list に tasks の TABLE DATA がある | 取れなければ中止(切り戻し手段がないため) | 5分 | 鈴木 | [ ] |
| 5 | 配布物の配置 | v2.5.0 を 2 台に配置、.env に FEATURE_LABELS=true を追加 | 配置先のバージョン表示が 2.5.0 | 配置できない台があれば両方 v2.4.0 のまま中止 | 5分 | 鈴木 | [ ] |
| 6 | マイグレーション適用 | psql -v ON_ERROR_STOP=1 --single-transaction -f 10_migrate_v2.5.0.sql | 終了コード 0。UPDATE 216、INSERT 0 1 で終わる | 終了コード 3 なら何も適用されていない。原因が 5 分で分からなければ R1 へ | 3分 | 鈴木 | [ ] |
| 7 | 動作確認 SQL | psql -f 20_verify.sql | C1〜C6 がすべて期待値 | 1 つでも違えば R1 へ | 3分 | 高橋 | [ ] |
| 8 | アプリ v2.5.0 起動 | systemctl start taskboard(2台)→ ヘルスチェック | /healthz が 200、version=2.5.0 | 1 台でも起動しなければ R1 へ | 3分 | 鈴木 | [ ] |
| 9 | 画面での動作確認 | 確認用アカウントでログイン → タスク作成 → 優先度とラベルを設定して保存 | 一覧に優先度とラベルが表示される(C7) | 保存できなければ R1 へ | 5分 | 高橋 | [ ] |
| 10 | 切り戻し判断 | 22:45 時点で No.7〜9 が完了しているか | 完了している | 未完了なら R1 へ | — | 佐藤 | [ ] |
| 11 | メンテナンス画面解除、完了連絡 | LB のメンテナンス画面を無効化 → 定型文を投稿 | トップが通常画面 | 通常画面にならなければ LB 設定を再確認 | 3分 | 鈴木 / 田中 | [ ] |
| 12 | 監視再開、記録 | メンテナンスモード解除 → 実績時刻を「9. 記録」に記入 | 15 分間エラー率 1% 未満 | 基準を超えたら責任者へ | 15分 | 高橋 | [ ] |
## 6. 確認
| No | 確認項目 | 方法 | 合格条件 | 結果 | 確認者 |
|---|---|---|---|---|---|
| C1 | マイグレーションの記録 | SELECT version ... LIMIT 1 | 20261001_003_priority_labels | | 高橋 |
| C2 | priority 列の定義 | information_schema.columns | smallint / NOT NULL / default 3 | | 高橋 |
| C3 | tasks の件数 | SELECT count(*) FROM tasks | 事前確認と同じ 1000 | | 高橋 |
| C4 | priority の分布 | GROUP BY priority | 1 と 3 のみ。NULL なし | | 高橋 |
| C5 | 初期ラベル | SELECT count(*) FROM labels | 3 | | 高橋 |
| C6 | 代表クエリ | 未完了タスクを priority 順に 3 件 | 3 行返る | | 高橋 |
| C7 | 画面 | ログイン → 作成 → 優先度・ラベル設定 → 保存 | 一覧に反映 | | 高橋 |
## 7. 切り戻し
### 切り戻す基準
- 時間:22:45 までに C1〜C7 が完了しない
- 影響:ログイン・タスク作成・保存のいずれかができない
- データ整合:C3 が 1000 と一致しない、C4 に NULL や 1・3 以外の値がある
### 切り戻し手順(所要 15 分)
| No | 作業 | コマンドまたは操作 | 期待結果 | 判断基準 | 所要時間 | 担当 | 実施 |
|---|---|---|---|---|---|---|---|
| R1 | 切り戻し連絡 | 定型文を投稿 | 投稿される | — | 1分 | 田中 | [ ] |
| R2 | アプリ停止 | systemctl stop taskboard(2台) | inactive | — | 2分 | 鈴木 | [ ] |
| R3 | DB を戻す(切替後に会員の書き込みがない場合) | psql -v ON_ERROR_STOP=1 --single-transaction -f 30_rollback_reverse.sql | 終了コード 0、\dt に labels がない、version=20260915_002_add_due_on | エラーなら R3' へ | 2分 | 鈴木 | [ ] |
| R3' | DB を戻す(書き込みが進んだ場合) | 新しい DB に pg_restore → 確認 → ALTER DATABASE で名前を入れ替え | 入れ替え後の t67 で version=…002、tasks_count=1000 | 復元先の確認が通らなければ責任者へ | 8分 | 鈴木 | [ ] |
| R4 | アプリ v2.4.0 起動、FEATURE_LABELS を削除 | systemctl start taskboard(2台) | /healthz が 200、version=2.4.0 | — | 3分 | 鈴木 | [ ] |
| R5 | 動作確認、メンテナンス画面解除 | C3・C7 相当を v2.4.0 で確認 → LB 解除 | 通常画面でログインと保存ができる | — | 3分 | 高橋 | [ ] |
## 8. 連絡テンプレート
(本記事「連絡文のテンプレート」の 3 種を使用。宛先:#release-taskboard、サポート窓口)
## 9. 記録
| 項目 | 内容 |
|---|---|
| 開始・終了の実績時刻 | |
| 結果 | |
| 発生した事象と対応 | |
| 次回への改善 | |
Go/No-Go 判定表と、切り戻し判断の基準
Go/No-Go 判定は「始めてよいか」、切り戻し判断は「続けてよいか」を決めるものです。どちらも基準を事前に文章にしておくと、当日の判断が「気持ち」ではなく「事実」で決まります。
Go/No-Go 判定表(汎用)
| 判定項目 | Go の基準 | 確認方法 | 判定者 |
|---|---|---|---|
| テスト | ステージングで受け入れテストが全件合格。残った既知の問題は「リリース可」と判断済み | テスト報告書、チケット | QA・確認者 |
| 配布物の凍結 | リリース対象のタグ・イメージが確定し、それ以降の変更が入っていない | タグのコミット ID を照合 | 開発リード |
| 切り戻し検証 | 切り戻し手順をステージングで実際に実行し、元に戻ることを確認済み | 実施記録(日付・実施者) | 開発リード |
| バックアップ | 直前バックアップが取得できる(容量・権限・所要時間を確認済み) | df、前回の所要時間 | DB 担当 |
| 体制 | 責任者・作業者・確認者・連絡担当が全員そろい、連絡先が最新 | 体制表 | リリース責任者 |
| 環境の状態 | 未対応のアラートがない。長時間トランザクションや大きなバッチが動いていない | 監視画面、pg_stat_activity |
運用担当 |
| 時間 | 「作業+確認+切り戻し」の合計が作業枠に収まる | スケジュール | リリース責任者 |
| 周知 | 利用者向け告知と関係部署への連絡が済んでいる | 告知の記録 | 連絡担当 |
1つでも No-Go なら延期です。「たぶん間に合う」「後で確認する」は No-Go に含めます。延期の連絡文を用意しておけば(後述)、決めるのは1分で済みます。
切り戻し判断の基準(時間・影響・データ整合)
| 観点 | 切り戻す | 継続する(前方修正で対応する) |
|---|---|---|
| 時間 | 「切り戻し判断の締切(= 作業枠の終了 − 切り戻しの所要時間)」までに確認が終わらない | 締切前に確認が完了した |
| 影響 | 主要機能(ログイン・作成・保存など)が使えない。エラー率・レイテンシが基準を超えている | 軽微な表示の問題で、回避策があり翌営業日の修正で足りる |
| データ整合 | 件数・合計が事前確認の値と一致しない。NULL・重複・想定外の値が出た | 確認 SQL がすべて期待値どおり |
| 原因の見通し | 5〜10 分で原因が特定できない | 原因が分かり、修正が小さく、確認手順を再利用できる |
切り戻しの方式は「切替後に書き込みが進んだかどうか」で決めます。進んでいなければ逆マイグレーション(速くて確実)、進んでいれば「新しい書き込みをどう扱うか」を責任者が決めたうえでバックアップから復元します。この判断も、当日ではなく手順書を書く時点で決めておきます。
flowchart TD
A[Go/No-Go 判定] -->|1つでも No-Go| B[延期して連絡]
A -->|すべて Go| C[作業手順を実施]
C --> D{各ステップの期待結果どおり?}
D -->|はい| E[確認 C1〜Cn]
D -->|いいえ| F{判断基準: 時間・影響・データ整合}
E -->|締切までに全項目合格| G[完了連絡・監視再開・記録]
E -->|不合格 or 締切超過| F
F -->|書き込みが進んでいない| H[逆マイグレーション → 旧アプリ起動]
F -->|書き込みが進んだ| I[新DBへ復元 → 確認 → 入れ替え → 旧アプリ起動]
H --> J[切り戻し連絡・記録]
I --> J
DB マイグレーションを伴うリリースを実際に実行したログ
ここからは、記入例の No.3〜7 と切り戻し R3・R3' を、PostgreSQL 16.13 の検証用データベース(t67)で実際に実行した結果です。題材は記入例と同じ「tasks に priority 列とラベル機能を追加する v2.5.0」で、tasks には 1,000 行入っています。時刻はすべて JST です。
使うファイル
マイグレーションは 1 ファイル 1 トランザクションで書き、先頭で lock_timeout を設定します。
-- 10_migrate_v2.5.0.sql
-- 実行: psql -v ON_ERROR_STOP=1 --single-transaction -f 10_migrate_v2.5.0.sql
SET lock_timeout = '3s'; -- ロック待ちで固まらないよう、3秒で諦めてエラーにする
ALTER TABLE tasks
ADD COLUMN priority smallint NOT NULL DEFAULT 3
CHECK (priority BETWEEN 1 AND 5);
CREATE TABLE labels (
id serial PRIMARY KEY,
name text NOT NULL UNIQUE,
color text NOT NULL
);
CREATE TABLE task_labels (
task_id bigint NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
label_id int NOT NULL REFERENCES labels(id),
PRIMARY KEY (task_id, label_id)
);
INSERT INTO labels (name, color) VALUES
('急ぎ', '#d9534f'), ('レビュー待ち', '#f0ad4e'), ('あとで', '#5bc0de');
-- 既存データの移行: 期限が3日以内の未完了タスクは優先度を 1 にする
UPDATE tasks SET priority = 1
WHERE status = 'open' AND due_on <= date '2026-10-01' + 3;
INSERT INTO schema_migrations (version) VALUES ('20261001_003_priority_labels');
-- 20_verify.sql(動作確認。期待結果はコメントに書いておく)
-- C1 → 20261001_003_priority_labels
SELECT version FROM schema_migrations ORDER BY version DESC LIMIT 1;
-- C2 → priority / smallint / NO / 3
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = 'tasks' AND column_name = 'priority';
-- C3 → 1000(事前確認の値と一致)
SELECT count(*) AS tasks_count FROM tasks;
-- C4 → 1 と 3 のみ。NULL なし
SELECT priority, count(*) FROM tasks GROUP BY priority ORDER BY priority;
-- C5 → 3
SELECT count(*) AS labels_count FROM labels;
-- C6 → 3 行返る
SELECT t.id, t.title, t.priority FROM tasks t
WHERE t.status = 'open' ORDER BY t.priority, t.due_on LIMIT 3;
-- 30_rollback_reverse.sql(逆マイグレーション。適用と逆の順番で消す)
SET lock_timeout = '3s';
DROP TABLE task_labels;
DROP TABLE labels;
ALTER TABLE tasks DROP COLUMN priority;
DELETE FROM schema_migrations WHERE version = '20261001_003_priority_labels';
No.3 事前確認:今の版・件数・長いトランザクション
### STEP 3 事前確認(Go/No-Go の材料) [2026-10-01 07:07:54 JST]
version
-------------------------
20260915_002_add_due_on
tasks_count
-------------
1000
long_transactions
-------------------
0
db_size
---------
7967 kB
ここで取った「version」「件数」が、後の確認と切り戻しの比較対象になります。長いトランザクション(pg_stat_activity で xact_start が 1 分より前)が 0 件であることも、DDL がロック待ちにならない根拠として記録します。
No.4 バックアップ取得:ファイルと中身を確認する
pg_dump -h 127.0.0.1 -U t67 -d t67 -Fc -f backup_before_v2.5.0_$(date +%Y%m%d_%H%M).dump
ls -lh backup_before_v2.5.0_*.dump
pg_restore --list backup_before_v2.5.0_*.dump | grep 'TABLE DATA'
### STEP 4 バックアップ取得 [2026-10-01 07:07:54 JST]
real 0m0.104s
-rw-r--r-- 1 root root 14K Oct 1 07:07 backup_before_v2.5.0_20261001_0707.dump
3416; 0 90499 TABLE DATA public members t67
3418; 0 90506 TABLE DATA public schema_migrations t67
3419; 0 90512 TABLE DATA public tasks t67
「取った」だけでなく「中身がある」まで確認するのが手順書の書き方です。pg_restore --list で対象テーブルの TABLE DATA が並んでいれば、復元に使えるファイルだと分かります。ファイル名に日時を入れておくと、切り戻し時にどれを使うか迷いません。
No.6 マイグレーション適用:1 トランザクションで、エラーなら何も残さない
psql -h 127.0.0.1 -U t67 -d t67 -v ON_ERROR_STOP=1 --single-transaction -f 10_migrate_v2.5.0.sql
echo "exit code: $?"
### STEP 6 マイグレーション適用 [2026-10-01 07:07:54 JST]
SET
ALTER TABLE
CREATE TABLE
CREATE TABLE
INSERT 0 3
UPDATE 216
INSERT 0 1
real 0m0.077s
exit code: 0
--single-transaction は、psql の公式ドキュメントにあるとおり「最初のコマンドの前に BEGIN、最後の後に COMMIT を発行し、ON_ERROR_STOP が設定されていれば途中でエラーになったときに ROLLBACK を送る」オプションです。手順書の期待結果には「終了コード 0」「最後の行が INSERT 0 1」のように、機械的に照合できるものを書きます。psql は正常終了で 0、スクリプト内のエラーで ON_ERROR_STOP が有効なら 3 を返します。
No.7 動作確認 SQL:C1〜C6 を期待値と照合する
### STEP 7 動作確認 SQL [2026-10-01 07:07:54 JST]
version
------------------------------
20261001_003_priority_labels
column_name | data_type | is_nullable | column_default
-------------+-----------+-------------+----------------
priority | smallint | NO | 3
tasks_count
-------------
1000
priority | count
----------+-------
1 | 216
3 | 784
labels_count
--------------
3
id | title | priority
-----+-----------------------+----------
14 | 週次レポート作成 #14 | 1
994 | 週次レポート作成 #994 | 1
42 | 週次レポート作成 #42 | 1
件数 1,000 が事前確認と一致し、priority は 1(216 件)と 3(784 件)だけ、NULL なし。UPDATE の件数 216 と分布の 216 が一致していることも、データ移行が想定どおりだった根拠になります。
切り戻し R3:逆マイグレーション(書き込みが進んでいない場合)
### 切り戻し A: 逆マイグレーション [2026-10-01 07:07:54 JST]
SET
DROP TABLE
DROP TABLE
ALTER TABLE
DELETE 1
real 0m0.125s
exit code: 0
Schema | Name | Type | Owner
--------+-------------------+-------+-------
public | members | table | t67
public | schema_migrations | table | t67
public | tasks | table | t67
version
-------------------------
20260915_002_add_due_on
tasks_count
-------------
1000
逆マイグレーションは「適用と逆の順番で消す」だけなので数秒で終わり、テーブル一覧・version・件数が事前確認の値に戻りました。PostgreSQL は DDL もトランザクションで扱えるため、途中でエラーになっても中途半端な状態は残りません(公式ドキュメントのとおり DROP COLUMN は列を見えなくするだけで、領域は後から回収されます)。
切り戻し R3':バックアップからの復元(書き込みが進んだ場合)
切替後に新アプリが task_labels へ 50 行書き込み、tasks の 10 行を更新した状態を作ってから、復元を試しました。まず、同じ DB に --clean で上書き復元すると何が起きるかです。
pg_restore -h 127.0.0.1 -U t67 -d t67 --clean --if-exists backup_before_v2.5.0_20261001_0707.dump
pg_restore: error: could not execute query: ERROR: cannot drop table public.tasks because other objects depend on it
DETAIL: constraint task_labels_task_id_fkey on table public.task_labels depends on table public.tasks
HINT: Use DROP ... CASCADE to drop the dependent objects too.
Command was: DROP TABLE IF EXISTS public.tasks;
pg_restore: error: could not execute query: ERROR: relation "tasks" already exists
pg_restore: error: COPY failed for table "tasks": ERROR: duplicate key value violates unique constraint "tasks_pkey"
pg_restore: warning: errors ignored on restore: 5
exit code: 1
Schema | Name | Type | Owner
--------+-------------------+-------+-------
public | labels | table | t67
public | members | table | t67
public | schema_migrations | table | t67
public | task_labels | table | t67
public | tasks | table | t67
version
-------------------------
20260915_002_add_due_on
tasks_count | updated_rows
-------------+--------------
1000 | 10
バックアップは「リリース前」の状態なので、リリースで新しく作った task_labels を知りません。その task_labels が tasks を参照しているため DROP TABLE tasks が通らず、schema_migrations だけが戻って tasks は新しいまま、という「版の記録と実体がずれた状態」になりました。pg_restore はエラーがあっても既定では続行し、最後に件数を報告するだけです(--exit-on-error で止められます)。
手順書には「別の新しい DB に復元し、確認してから名前を入れ替える」と書いておきます。入れ替え前の DB は t67_old として残るので、新しい書き込みの調査にも使えます。
psql -h 127.0.0.1 -U t67 -d postgres -c "CREATE DATABASE t67_restore OWNER t67;"
pg_restore -h 127.0.0.1 -U t67 -d t67_restore --exit-on-error backup_before_v2.5.0_20261001_0707.dump
# 復元先を確認してから、アプリ停止中(接続なし)に名前を入れ替える
psql -h 127.0.0.1 -U t67 -d postgres -v ON_ERROR_STOP=1 \
-c "ALTER DATABASE t67 RENAME TO t67_old;" \
-c "ALTER DATABASE t67_restore RENAME TO t67;"
### B-2 別の新しいDBに復元し、確認してから名前を入れ替える [2026-10-01 07:09:48 JST]
CREATE DATABASE
real 0m0.060s
exit code: 0
--- 復元先の確認(切り替え前)
version
-------------------------
20260915_002_add_due_on
tasks_count | updated_rows
-------------+--------------
1000 | 0
--- 名前を入れ替えて切り替え(アプリ停止中・接続なしの状態で実施)
ALTER DATABASE
ALTER DATABASE
current_database | version | tasks_count
------------------+-------------------------+-------------
t67 | 20260915_002_add_due_on | 1000
ALTER DATABASE ... RENAME は、その DB に接続しているセッションがあると実行できません。切り戻し手順で R2「アプリ停止」が R3' より前にあるのは、このためです。
| 切り戻し方式 | 向いている状況 | 所要時間の目安 | 手順書に書くこと |
|---|---|---|---|
| 逆マイグレーション | 切替後に書き込みが進んでいない。スキーマ変更が可逆(列追加・テーブル追加) | 分単位 | 逆順の SQL、version の戻り先、確認 SQL |
| バックアップから復元 | 書き込みが進んだが、その書き込みを捨ててよい(責任者が判断) | DB サイズに比例 | 復元先 DB の作り方、入れ替え手順、旧 DB の保持期間 |
| 前方修正 | 原因が明確で修正が小さく、確認手順を再利用できる | 修正内容による | 「前方修正を選ぶ条件」と、修正版の確認手順 |
| データの部分復元 | 特定テーブルだけ戻したい | テーブルサイズに比例 | pg_restore -t テーブル名 の対象と、外部キーの順序 |
つまずく箇所①:DEFAULT なしの NOT NULL 列追加はエラーになり、全体が巻き戻る
DEFAULT 3 を付け忘れると、既存の 1,000 行に入れる値がないためエラーになります。
CREATE TABLE
psql:10_migrate_v2.5.0_ng.sql:9: ERROR: column "priority" of relation "tasks" contains null values
exit code: 3
Schema | Name | Type | Owner
--------+-------------------+-------+-------
public | members | table | t67
public | schema_migrations | table | t67
public | tasks | table | t67
先に成功した CREATE TABLE labels も一緒に巻き戻り、\dt に labels は残っていません。--single-transaction と ON_ERROR_STOP を組み合わせているからで、この 2 つがないと「labels だけできていて priority がない」状態で止まり、再実行すると今度は relation "labels" already exists で止まります。手順書の判断基準に「終了コード 3 なら何も適用されていないので、原因を直して再実行できる」と書けるのは、この組み合わせのおかげです。
なお PostgreSQL 16 の公式ドキュメントによると、揮発性でない DEFAULT を付けた列追加はテーブルの書き換えを伴わず、既存行にはメタデータ上の既定値が使われます。1,000 行でも 1 億行でも ADD COLUMN ... DEFAULT 3 は一瞬で終わるため、所要時間の見積もりにも効きます(型変更や揮発性の DEFAULT は書き換えが起きます)。
つまずく箇所②:ロック待ちで固まる → lock_timeout で「エラーにして気づく」
別のセッションが tasks を読んでいる途中(トランザクションが開いたまま)で、マイグレーションを実行した例です。ALTER TABLE は ACCESS EXCLUSIVE ロックを取るため、読み取りのロックが解放されるまで待たされます。
SET
psql:10_migrate_v2.5.0.sql:7: ERROR: canceling statement due to lock timeout
real 0m3.059s
exit code: 3
--- 塞いでいるセッションを探す
pid | state | xact_age | query
------+--------+-----------------+-----------------------------------------------
7754 | active | 00:00:04.080177 | BEGIN; SELECT count(*) FROM tasks; SELECT pg_
lock_timeout がないと、この ALTER は相手が終わるまで無言で待ち続け、しかも待っている間は後続のすべてのクエリ(SELECT も含む)がその後ろに並びます。3 秒で諦める設定にしておくと、「待ちが発生した」事実が終了コード 3 として判断基準に乗り、pg_stat_activity で相手を確認してから再実行できます。これが記入例の No.3 で長時間トランザクションを確認している理由です。
| 手順書に書く SQL の工夫 | 効果 |
|---|---|
SET lock_timeout = '3s' を先頭に |
ロック待ちで固まらず、エラーとして検知できる |
psql -v ON_ERROR_STOP=1 --single-transaction |
途中でエラーになったら全部巻き戻る。再実行が安全 |
schema_migrations に版を記録 |
事前確認・動作確認・切り戻しの比較対象になる |
| 確認 SQL のコメントに期待値 | 確認者が「期待どおりか」を読むだけで判定できる |
| バックアップ名に日時 | 切り戻しで使うファイルを迷わない |
CREATE INDEX CONCURRENTLY などは別ファイルに |
トランザクション内で実行できない文があると --single-transaction 全体がエラーになるため |
リリース方式の選択表と、方式ごとに手順書へ増える項目
方式が変わると、手順書に書く「切替」「確認」「切り戻し」の中身が変わります。小さなサービスで DB のスキーマ変更を伴うなら一斉切替(メンテナンス枠)が最も手順が短く、利用者を止めたくないなら Blue/Green やカナリアを選びます。
| 方式 | 向いている場面 | 停止時間 | 切り戻しの速さ | 手順書に増える項目 |
|---|---|---|---|---|
| 一斉切替(メンテナンス枠) | DB スキーマ変更、小〜中規模、深夜の作業枠が取れる | あり | 中(逆マイグレーション) | メンテナンス開始・終了の告知、アクセス遮断と解除、作業枠内の時間管理 |
| メンテナンス画面 | 一斉切替の派生。利用者に状況を案内したい | あり | 中 | メンテナンス画面の切替と戻し、ヘルスチェックの除外、CDN・ブラウザキャッシュの扱い |
| Blue/Green | 2 系統を用意し、確認後に切替。すぐ戻したい | ほぼなし | 速い(切替を戻す) | 両系統のデプロイ確認、切替方法(DNS / ロードバランサー)、旧系統の保持期間、DB の前方・後方互換の確認 |
| カナリア | 一部の利用者で確認してから広げる | なし | 速い(比率を 0 に) | 段階(例:1% → 10% → 50% → 100%)ごとの判断基準と観測時間、対象の選び方、比較する指標 |
| フィーチャーフラグ | コードは先に配布し、機能は後で有効化する | なし | 最速(フラグを戻す) | フラグ名・既定値・切替手順と権限、フラグを削除する予定日 |
Blue/Green・カナリア・フィーチャーフラグでは、新旧のアプリが同じ DB を同時に使う時間があります。そのため DB の変更は「追加してから、後で削除する」の 2 段階(expand / contract)に分け、旧アプリが新しい列を無視しても動くこと(後方互換)を確認項目に加えます。今回の例の priority 列は DEFAULT 3 があるので、旧アプリ(v2.4.0)が priority を知らずに INSERT しても動きます。
連絡文のテンプレート(開始・完了・切り戻し)
連絡文は「今どの状態か」「利用者への影響」「次の連絡はいつか」の 3 点を必ず入れます。定型文にしておくと、慌てているときほど効きます。
【リリース開始】TaskBoard v2.5.0
- 日時:2026-10-08 22:00 開始(予定終了 23:00)
- 内容:タスクの優先度・ラベル機能の追加
- 影響:22:00〜22:50 はメンテナンス画面を表示し、サービスを利用できません
- 状況:Go/No-Go 判定はすべて Go。作業を開始します
- 次の連絡:完了時(遅くとも 23:00)。遅れる場合は 22:45 に連絡します
- 連絡先:#release-taskboard(担当:田中)
【リリース完了】TaskBoard v2.5.0
- 完了時刻:2026-10-08 22:48
- 結果:すべての確認項目(C1〜C7)が合格。メンテナンス画面を解除しました
- 利用者への案内:優先度とラベルが設定できるようになりました。表示が更新されない場合はページを再読み込みしてください
- 監視:23:05 まで強化監視を継続します
【切り戻し】TaskBoard v2.5.0
- 判断時刻:2026-10-08 22:45(判断者:佐藤)
- 理由:確認項目 C7(画面での保存)が完了せず、切り戻し判断の締切に達したため
- 対応:v2.4.0 に戻し、DB は逆マイグレーションで 22:05 時点の状態に戻しました
- 影響:メンテナンス画面中のため、22:00〜23:00 の間に行われた操作はありません
- 今後:原因を確認のうえ、再リリース日を 10/9 中に連絡します
- 次の連絡:23:00 に状況を更新します
延期の連絡は、開始の連絡文の「状況」を「Go/No-Go 判定で{項目}が No-Go のため、{日付}に延期します。利用者への影響はありません」に置き換えるだけで作れます。
よくある抜け漏れチェックリスト
手順書レビューで実際によく見つかる抜けを、事前準備の項目として並べました。「該当なし」も明示的に書くと、レビューで「忘れたのか、ないのか」が分かります。
| 項目 | 確認すること | 抜けると起きること |
|---|---|---|
| キャッシュ | アプリ内キャッシュ、CDN、ブラウザキャッシュの削除・無効化と、その戻し | 新しい画面が一部の利用者に届かない、以前の JS が新 API を呼ぶ |
| cron・定期ジョブ | 作業中に動くジョブの一時停止と再開。停止中に飛ばしたジョブの扱い | マイグレーション中にバッチが走りロック待ちになる、再開を忘れて翌日のジョブが動かない |
| 環境変数・設定・シークレット | 追加・変更・削除する値と、置く場所(全台)、切り戻し時の値 | 1 台だけ設定が違い、断続的なエラーになる |
| DNS の TTL | 切替を DNS で行うなら、事前に TTL を短くする時期と戻す時期 | 切替・切り戻しが利用者に届くまで TTL の分だけかかる |
| 外部連携の切替 | Webhook の URL、コールバック先、API キー、相手側の設定変更のタイミング | 相手側が旧 URL に送り続ける、切り戻し時に相手側も戻す必要がある |
| 監視の一時停止と再開 | メンテナンスモードの予約と解除、解除後に確認するアラート | 作業中の通知で関係者が驚く、再開を忘れて障害に気づかない |
| 証明書・ドメイン | 新しいホストや系統の証明書、SAN の追加 | Blue/Green の新系統で証明書エラー |
| 権限 | 作業者の本番アクセス、DB ロールの権限(CREATE、ALTER)、配置先の書き込み権限 | 当日「権限がない」で止まる |
| 旧バージョンの残り | 旧プロセス・旧コンテナ・旧ジョブが残っていないか | 旧アプリが新スキーマに書き込み続ける |
| 通知メール・プッシュ | 作業中に送られる通知の抑止と、抑止解除後の再送の扱い | 利用者に大量の通知が届く |
| 時刻・タイムゾーン | スケジュールと記録の時刻にタイムゾーンを明記 | サーバー時刻(UTC)との取り違え |
| 作業ログの保存 | ターミナルのログ(script コマンドなど)を残す場所 |
記録に実績を書けない |
よくある質問
Q. 手順書はどのくらい細かく書けばよいですか? A. 「作業者以外の人が、その手順書だけで同じ操作ができる」が目安です。コマンドは省略せずに書き、GUI の操作は画面名とボタン名まで書きます。背景の説明や設計の理由は概要に 1〜2 行で十分です。
Q. 切り戻し手順は毎回検証する必要がありますか? A. DB のスキーマ変更やデータ移行を含むリリースは、ステージングで「適用 → 切り戻し → 再適用」まで実行してから Go にします。アプリの入れ替えだけで前回と同じ切り戻し手順なら、検証記録を流用できます。検証した日付と実施者を Go/No-Go 判定表に書きます。
Q. リリース手順書と、デプロイの自動化(CI/CD)は両立しますか? A. 両立します。自動化されている部分は「パイプライン名と起動方法、成功の判定方法、エラー時の止め方」を書き、人が判断する部分(Go/No-Go、切り戻し判断、連絡)を手順書に残します。自動化が進むほど、手順書は「判断と連絡の書類」になっていきます。
Q. 作業枠が短くて切り戻しの時間が取れません。 A. 「作業+確認+切り戻し」が枠に収まらなければ、その枠では No-Go です。対策は、枠を延ばす、作業を分割する(DB の列追加を前日に先行させる)、フィーチャーフラグで切替を後回しにする、の 3 つです。切り戻しの時間を削るのではなく、作業の側を小さくします。
まとめ
- リリース手順書の条件は、他の人が同じ操作をできる・各ステップに期待結果と判断基準がある・切り戻しの手順と基準が最初からあるの 3 つ
- 構成は 概要・体制と連絡先・スケジュール・事前準備・作業手順・確認・切り戻し・連絡テンプレート・記録 の 9 章
- 作業手順は No・作業・コマンド・期待結果・判断基準・所要時間・担当・実施チェック の表形式で、1 行 1 操作
- Go/No-Go は 1 つでも No-Go なら延期。切り戻し判断は 時間(締切)・影響・データ整合 の 3 観点で事前に決める
- DB マイグレーションは
ON_ERROR_STOP+--single-transaction+lock_timeoutで、エラーになっても何も残らず再実行できる形にする - バックアップからの復元は 同じ DB へ上書きせず、新しい DB に復元して確認してから入れ替える
- 連絡文は 状態・影響・次の連絡時刻 の 3 点。抜け漏れは キャッシュ・cron・環境変数・DNS TTL・外部連携・監視 から確認する
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「リリース手順書の書き方|切り戻し付きテンプレートと判定表」
株式会社RJCでは、リリース手順書のように「当日の判断を事前に文章にしておく」進め方を、プロジェクトの中で仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。