exit code 0 を確認しただけでリリース完了としていないでしょうか。「機能が本来の目的を果たしているか」までを検証するには、時間軸別の3段階チェックが必要です。
具体的には、即時(〜30分)/ 24時間以内 / 24時間後の3段階で、それぞれ違う観点を grep で確認します。本記事は、PR #157/#159/#158 の本番リリース時に実際に運用したテンプレートをまとめます。
問題:exit 0 では検証が浅すぎる
ナイーブなリリース後検証は以下のようになりがちです。
1# 本番デプロイ2ssh prod "systemctl restart pipeline.service"3ssh prod "systemctl status pipeline.service" # → active (running)4echo "デプロイ完了"しかしこれは「プロセスが起動した」を確認しただけ。本来の目的(バグが直ったか、新機能が効いているか)を確認していません。
例えば、
- リリース内容: 「データ stale 検知ロジックを修正」
- 確認すべき: stale が起きたときに、新しいログが出るか / 誤検知が消えたか
- exit 0 で確認できる: プロセスが動いているか だけ
これでは、stale 検知ロジックが完全にバグっていても気づきません。
3段階チェックリストの構成
| 段階 | タイミング | 確認内容 |
|---|---|---|
| 即時 | デプロイ〜30分 | シンボル存在・import健全性 |
| 24h以内 | 翌朝〜翌晩 | 次回発火時のログ・誤検知有無 |
| 24h後 | リリースから1日後 | 本来目的の達成確認・指標改善 |
各段階で grep で具体的なシンボルを確認します。
段階1: 即時チェック(〜30分)
観点: シンボルが本番に存在するか
リリース内容に固有のシンボル(関数名・定数名・新規ログメッセージ)を、本番ファイルから grep します。
1# 例: PR#157 の新規ログメッセージ2ssh prod "grep -n 'OHLCV-STALE' /opt/app/pipeline.py"3# → ヒットすれば、新コードが本番に反映されている観点: import が壊れていないか
1ssh prod "PYTHONPATH=/opt/app python -c 'from pipeline.stale import check_ohlcv'"2# → ImportError が出れば即時ロールバック観点: 起動ログにエラーがないか
1ssh prod "journalctl -u pipeline.service --since '5 minutes ago' | grep -E 'ERROR|Traceback'"2# → エラーなしなら次の段階へこれらの即時チェックで「物理的に正しくデプロイされたか」を確認します。
段階2: 24h以内チェック(翌朝〜翌晩)
観点: 次回発火時のログ
リリース内容のロジックが最初に実行されるタイミングで、想定どおりのログが出るかを確認します。
1# 例: 新ロジックが朝6時の定期実行で初発火2ssh prod "journalctl -u pipeline.service --since 'today 05:00' | \3 grep -E 'OHLCV-STALE|notify_fallback|yfinance fallback'"4
5# 期待値:6# - OHLCV-STALE が必要な場合のみ出力(誤検知ゼロ)7# - notify_fallback が呼ばれているか8# - yfinance fallback が想定通りかここで重要なのは、期待値を事前に明文化しておくこと。
1期待値(事前定義):2- 通常日: [OHLCV-STALE] ログ出ない(誤検知ゼロ — Pre で 4/24 検証済)3- stale 発生日: [OHLCV-STALE] が出て、notify_fallback も呼ばれる4- 異常: ERROR ログが出る、もしくは fallback が呼ばれない観点: 誤検知の有無
新しい検知ロジックが、通常の正常データを誤って検知していないかを確認します。リリース直後の数時間で、誤検知の傾向が見えます。
段階3: 24h後チェック(リリースから1日後)
観点: 本来目的の達成
リリース内容が「何を解決するためのものだったか」に立ち返り、改善指標を確認します。
1# 例: PR#157 の目的「アラート連発を止める」2ssh prod "journalctl -u pipeline.service --since '24 hours ago' | \3 grep -cE 'EQIX|HWM|STZ'"4# → リリース前と比較して連発が減っているか観点: 副作用の有無
新ロジックの副作用として、別の問題が起きていないか。
1# 例: 副作用でトラフィック増えていないか2ssh prod "journalctl -u pipeline.service --since '24 hours ago' | \3 grep -c 'API request'"4# → リリース前と比較して急増していないか観点: 指標改善
最後に、リリースのROI を数値で確認します。
- リリース前: stale 誤検知 月10件
- リリース後 24h: 誤検知 0件 → 期待通り
- リリース後 7日: 誤検知 0件 → ROI 確定
チェックリストのテンプレート化
毎回同じ構造でリリースするため、以下のテンプレートを .docs/release_checklist_template.md として保存します。
1# リリースチェックリスト: PR#NNN2
3## 即時(〜30分)4- シンボル存在: `grep -n "{固有シンボル}" /opt/app/{file}`5- import 健全性: `python -c "from {module} import {symbol}"`6- 起動ログエラーなし: `journalctl --since '5 minutes ago' | grep ERROR`7
8## 24h以内9- 次回発火ログ確認: `journalctl --since 'today 05:00' | grep "{パターン}"`10- 誤検知の有無確認11- 期待値との照合12
13## 24h後14- 本来目的の達成確認: 改善指標 grep15- 副作用の有無確認1 collapsed line
16- ROI 数値確定PR ごとに {固有シンボル} {パターン} を埋めるだけで、3段階チェックが運用できます。
期待値の事前定義が肝
3段階チェックリストで最も重要なのは、期待値を事前に定義しておくことです。
1## 期待値(事前定義)2- 通常日: [OHLCV-STALE] ログ出ない(誤検知ゼロ — Pre で 4/24 検証済)3- stale 発生日: [OHLCV-STALE] が出て notify_fallback も呼ばれる4- 異常: ERROR ログ or fallback 未呼び出しこれがないと、**リリース後にログを見ても「これが正常か異常かわからない」**状態になります。
リリース前の段階で、
- 何が出れば成功か
- 何が出れば失敗か
- どちらでもない場合の判断基準
を明文化します。これは Pre 環境での Dry-Run 検証の段階で固めます。
教訓:「リリース完了」の定義を分割する
これまで「リリース完了 = デプロイ成功」と捉えていました。しかし実際は、
- 物理的完了: コードが本番に反映された(即時チェックで確認)
- 機能的完了: 想定どおり動いている(24h以内チェック)
- 目的的完了: 本来の問題が解決された(24h後チェック)
の3層に分けるべきです。それぞれで違う grep を打つことで、リリースの確からしさを段階的に確証できます。
まとめ
- exit 0 確認だけのリリースは検証が浅すぎる
- 即時 / 24h以内 / 24h後 の3段階で grep を打つ
- 期待値を事前に明文化する(成功/失敗/判定不能)
- 「物理的完了 → 機能的完了 → 目的的完了」の3層で検証
3段階チェックリストをテンプレート化することで、リリース後の不安が定量的に減っていきます。