デプロイ後に何が起きるか:新しいバージョンが実際に動作していることを確認する

デプロイボタンが押された。パイプラインはグリーン。チームは一息つく。しかし、ここで仕事が終わったと思ったら、痛い目を見ることになる。

このパターンは数え切れないほどのチームで見てきた。新バージョンが本番に上がり、皆が祝杯を挙げた30分後、最初のサポートチケットが届く。ステージングでは動いていた機能が、本番ではメモリを食いつぶしている。テスト呼び出しには正常に応答していたAPIが、実際のユーザートラフィックで誰も気づかなかった競合状態を露呈する。

リリースはゴールではない。本当のテストが始まる瞬間だ。

ステージングだけでは不十分な理由

チームはリリース前にテストを実行したはずだ。単体テストはパスした。結合テストもグリーンだった。ステージング環境は健全に見えた。それらはすべて良いことだが、十分ではない。

ステージングには実際のユーザーがいない。実際のデータ量もない。本番が日常的に処理するような予測不能なトラフィックパターンも存在しない。ステージングでは50ミリ秒で完了するデータベースクエリが、本番では数百万行のテーブルに対して5秒かかることがある。テストアカウントでは問題なく動く機能が、十数種類のIDプロバイダからの認証トークンで壊れることもある。

このステージングと本番のギャップこそが、リリース後の確認を必要とする理由だ。本番で動作しているバージョンが、実際の条件下で期待通りに動いていることを確認しなければならない。

スモークテストから始める

リリース後に行うべき最初の作業はスモークテストだ。この名前は電子工学に由来する。回路基板に初めて電源を入れたとき、煙が出るかどうかを確認する。煙が出なければ基板は燃えていないので、より深いテストに進める。

ソフトウェアにおけるスモークテストは、新バージョンが生きているかを素早く確認するものだ。メインページにアクセスしてロードされることを確認する。重要なAPIエンドポイントを呼び出してレスポンスが返ってくることを確認する。データベース接続が確立されていることを確認する。ログインフローが即座にクラッシュしないことを確認する。

スモークテストは意図的に浅い。すべての機能やエッジケースをテストするわけではない。一つの問いに答えるだけだ。「デプロイは実際に機能したのか、それともアプリケーションは到着早々に壊れているのか?」

以下のフローチャートは、この記事で説明するリリース後の検証プロセスと、ロールバックの判断ポイントを示しています。

デプロイ直後にスモークテストを実行するためのシンプルなbashコマンドです:

curl -f https://prod.example.com/health && echo "Smoke test passed" || echo "Smoke test failed"
flowchart TD A[Release] --> B[Smoke Test] B -->|Pass| C[Verification] B -->|Fail| D[Rollback] C -->|Pass| E[Health Monitoring] C -->|Fail| D E -->|Anomaly Detected| F{Severity?} F -->|Minor| G[Hotfix] F -->|Major| D E -->|Normal| H[Continue Monitoring] G --> E

優れたスモークテストは2分もかからない。自動化されていれば数秒で終わる。手動で行う場合もチェックリストは短く保つ。最大で5〜10項目。それ以上になると検証の領域に踏み込んでいる。

検証に移行する

スモークテストが通ったら、新バージョンが正しく動作することを検証する必要がある。検証はより体系的だ。システムの実際の動作を期待値と比較する。

ここで既存のテストスイートが再び役立つ。重要な自動テストを本番に対して実行する。完全なリグレッションスイートではなく、変更した機能をカバーするサブセットだ。新しいチェックアウトフローを追加したなら、注文が実際に成立することを確認する。検索アルゴリズムを変更したなら、検索結果が妥当であることを確認する。

検証には、自動化が難しい項目の手動チェックも含まれる。ログに予期しないエラーがないか確認する。リリース後に処理されたデータを確認する。新しい機能が通常のユーザーインターフェースから到達可能であることを確認する。

スモークテストとの重要な違いは深さだ。スモークテストは「生きているか?」を問う。検証は「正しく動作しているか?」を問う。

ヘルスシグナルを監視する

スモークテストと検証は能動的なチェックだ。リクエストを送信し、レスポンスを観察する。しかし、受動的に行うもう一つのチェック層がある。それはヘルスシグナルの監視だ。

ヘルスシグナルとは、システムの状態が良好かどうかを示すメトリクスだ。CPU使用率、メモリ消費量、リクエストレート、エラーレート、レスポンスタイム、データベースコネクションプールの利用率、キューの深さ。これらの数値は、ユーザーがシステムと対話するにつれて継続的に変化する。

リリース後は、これらのシグナルに異常がないか監視したい。エラーレートの急上昇は危険信号だ。メモリ使用量の漸増はリークを示唆する。レスポンスタイムの着実な上昇は、新しいコードが負荷下で遅いことを意味するかもしれない。

ヘルスシグナルと能動的テストの違いは重要だ。能動的テストは、あなたが明示的に問い合わせたときに何が起きるかを教える。ヘルスシグナルは、ユーザーがシステムを自然に使う中で何が起きているかを教える。両方が必要だ。

これらのメトリクスを以前のバージョンのベースラインと並べて表示するダッシュボードを設定する。さらに良いのは、メトリクスがしきい値を超えたときにトリガーされるアラートを設定することだ。グラフを1時間も眺めて問題を発見したくはない。何かがおかしいときにシステムが教えてくれるようにしたい。

どのくらいの期間チェックすべきか

リリース後のチェック期間は変更内容によって異なる。

小さなバグ修正やマイナーな機能追加であれば、数分間のスモークテストとヘルスシグナルの観察で十分だ。最初の10分間に何も壊れなければ、その変更はおそらく安全だ。

大規模な機能、データベースマイグレーション、インフラストラクチャの変更には、より長い時間が必要だ。数時間の監視を計画する。大規模リリース後は、丸一日業務時間をかけて注意深く監視するチームもある。その理由は、特定の問題が表面化するまでに時間がかかるからだ。メモリリークはシステムが数千のリクエストを処理するまで現れないかもしれない。低速クエリは同時ユーザー数が一定のしきい値に達するまで可視化されないかもしれない。

重要なのは、リリース前にチェック期間を定義することだ。何を、どのくらいの期間チェックし、どのしきい値でロールバックするかを合意する。インシデントの最中にこれらの判断を下してはならない。

問題が発生した場合

リリース後のチェックで問題が見つかった場合、何をするかを決定する必要がある。選択肢は通常、ホットフィックスかロールバックの二つだ。

ホットフィックスは、問題が小さく、局所的で、迅速に修正できる場合に適している。設定値のタイポ。新しいエンドポイントの権限不足。特定のブラウザだけで発生するCSSの不具合。パッチを当てて、ユーザーを混乱させることなく再デプロイできる。

ロールバックは、問題が深刻な場合に適している。データ破損。セキュリティ脆弱性。ユーザーフロー全体を壊す機能。システムを使えなくするパフォーマンス低下。このような場合、サービスを復旧する最速の方法は以前のバージョンに戻すことだ。

この判断はリリース前に行うべきであり、危機の最中に行うべきではない。何がホットフィックスに値する問題で、何がロールバックに値する問題かを定義する。文書化する。チームの全員が基準を理解していることを確認する。

実践的なリリース後チェックリスト

以下はチームに合わせて調整できる短いチェックリストだ。アプリケーションとリスク許容度に基づいて項目を調整すること。

  • スモークテスト:メインページがロードされる、重要なAPIが応答する、データベースに接続されている
  • 自動検証:変更した機能をカバーするテストサブセットを実行する
  • 手動検証:ログを確認する、新機能にアクセスできることを確認する
  • ヘルスシグナル:エラーレート、レスポンスタイム、リソース使用量を確認する
  • アラート設定:監視アラートが有効でしきい値が設定されていることを確認する
  • ロールバック基準:何がロールバックをトリガーするか、誰が判断するかをチームが把握していることを確認する

本当の教訓

リリース後のチェックはオプションではない。自信を持って出荷するチームと、祈りながら出荷するチームを分けるステップだ。目標は本番のすべての問題を排除することではない。それは不可能だ。目標は、多くのユーザーに影響が及ぶ前に問題を迅速にキャッチし、見つけたときに対応するための明確な計画を持つことだ。

デプロイが完了するのは、パイプラインがグリーンになったときではない。新しいバージョンが本番で、実際のトラフィック下で、正しく動作していることを確認し、ヘルスシグナルが正常であることを確認したときだ。その瞬間こそ、リリースが成功したと真に言える時なのだ。