「直したら別のところが壊れた」を止めるために、STG・回帰テスト・プレビュー環境を整理した話

git 歴 1 年ほどの私が、3 人 + AI エージェントの小さなチームで 2 つの Web サービスを立て続けにリリースし、その直後に直面した問題と、整理した考え方をまとめます。専門用語はできるだけ、日常の言葉に置き換えています。

起きたこと

リリースまではスピード優先で、main ブランチに直接 push して本番へ出していました。リリース後、テスターが見つけた不具合を直していくと、前は動いていた機能が、修正によって動かなくなるケースが出始めました。直したつもりが後戻りする。この現象を業界では「退行(リグレッション)」と呼びます。

原因ははっきりしていて、「変更で既存の機能が壊れていないか」を機械で確かめる仕組みが無かったことです。新機能のテストは書いても、それが蓄積されて毎回走る形になっていませんでした。

用語を整理する

まず、会話の中で混ざっていた言葉を分けました。テストには「目的」と「形式」の 2 つの軸があります。

目的(何のために試すか)

目的 確かめること
受け入れ 新しく作ったものが約束どおりか
回帰 変更で既存が壊れていないか
スモーク デプロイ直後に起動して主要画面が開くか
探索的 決めた手順に無い操作で壊れ方を探す(テスターの仕事)

形式(どうやって試すか)

形式 範囲 手段
単体 関数 1 つ コードから直接呼ぶ
結合 API や DB を含む複数の部品 サーバーを起動して叩く
E2E 画面から DB まで全部 ブラウザを自動操作
目視 見た目 人がスクリーンショットを見る

「回帰テスト」は目的の名前、「E2E」は形式の名前です。だから「E2E の形式で書いた回帰テスト」という組み合わせが成り立ちます。料理でたとえると、回帰は「味見をする理由(前と同じ味か)」、E2E は「味見の仕方(一皿をコース通りに食べる)」です。

回帰テストはいつ走らせるか

ここで私が引っかかったのは「STG(ステージング環境)で確認するなら、STG にマージしてデプロイしないと動くかどうか分からない。そうすると時間が空いて、不具合を潰す速さが落ちるのでは」という点でした。

答えは、回帰テストを STG に載せないでした。

段階 いつ どこで 何を確かめる
1 PR を開いた瞬間、自動 CI(使い捨ての DB) 単体 + 回帰テストの最低セット
2 DB の形や設定を変えた PR だけ、手動 STG 本番と同じ手順で壊れないか
3 1 が緑のとき、人が押す GitHub マージ
4 マージで自動、テストが緑のときだけ 本番 デプロイ
5 デプロイ直後、自動 本番 スモーク(読み取りだけ)

普通の修正は 1 → 3 → 4 で、待つのは CI の十数分だけ。STG を通す必要があるのは、データベースの構造や環境設定を変えるような一部の PR だけです。

「マージせずに STG に出す」が分からなかった

もう 1 つ引っかかったのが、「STG に出すのにマージは要らない」という説明でした。私の理解は「STG に PR → マージ → 確認 → 本番に PR → マージ」だったからです。

原稿と印刷にたとえると腑に落ちました。

  • ブランチ = 下書き。作業中の下書きが何本もある
  • main = 決定稿
  • ビルド = 原稿を版下にする
  • デプロイ = 印刷して店に並べる
  • マージ = 下書きの内容を決定稿に書き写す

本番は「決定稿に書き写してから印刷する」順番です。でも試し刷り(STG)は、下書きをそのまま印刷機にかければ済みます。印刷機(GitHub Actions)は「どの原稿を刷るか」を指定できるので、下書きのブランチを指定すれば下書きが刷れる。決定稿を書き換える必要はありません。

試し刷りのたびに決定稿を書き換えると、決定稿が試作の内容で汚れて、本番に出すときに何が正しいか分からなくなります。実際、STG 用のブランチを別に持っていたプロジェクトでは、STG と本番のコードが大きく食い違っていました。

だから PR は 1 本だけ。下書きのまま STG で試し、OK なら同じ PR をマージして本番へ、という流れになります。開発しながら何度も直す前提だからこそ、決定稿は最後の 1 回だけ更新します。

STG が 1 台だと同時に試せない

3 人 + AI エージェントが同時に別々のブランチを進めると、STG が 1 台では取り合いになります。人数分の STG を用意する必要があるのか、と思いましたが、答えは「1 台でいい」でした。

PR ごとのプレビュー環境という考え方です。STG のサービスは 1 つのまま、その中に PR ごとのコピー(Cloud Run ではリビジョンにタグを付ける機能で実現)を置き、pr-123 のような専用 URL を付けます。ローカル開発で、ブランチ A を 3001 番ポート、ブランチ B を 3002 番で同時に立ち上げて見比べる、あの感覚をクラウドでやるものです。

費用が増えないのは、Cloud Run が「リクエストが来たときだけ動く」仕組みだからです。人感センサー付きの照明のように、誰かが URL を開いた瞬間に起動し、しばらくアクセスが無いと止まる。PR が 10 本あっても、誰も見ていなければ費用はかかりません。

DB だけは共有の 1 台なので、DB の構造を変える PR は一度に 1 本ずつ、という規律が要ります。ただ、大半の PR は画面や API の変更で DB の形を変えないので、実際に順番待ちになるのは月に数本です。

整理した結論

  1. PR は main 宛てに 1 本。STG 用のブランチや PR は作らない
  2. 回帰テストの最低セット(主要な流れ 10 本前後、E2E 形式)を PR ごとの CI で走らせ、赤ならマージできない
  3. 各機能の受け入れテストは、完了時に回帰テストの束へ移す(束を育てる責任者を決める)
  4. STG は 1 台 + PR ごとのプレビュー環境。DB は共有し、DB を変える PR は直列
  5. 本番デプロイはテストが緑のときだけ。直後にスモークテスト

古いバックログについても、リリース前の環境で起きた不具合は前提が変わっているので、一括で「保管」扱いにして、同じ現象が今の本番で再現したら新しく起票する、と決めました。古い記述に沿って直すと、いま動いている機能を壊す方向に働くからです。

反省

「E2E をしっかりやる」「STG を整える」とは何度も言っていましたが、目的と形式を分けて考えるまで、何が足りないのかを言葉にできていませんでした。足りなかったのは「既存を守るテストを蓄積して毎回走らせる仕組み」で、STG の台数でも E2E の量でもありませんでした。

岡山出身、1977年生まれ、現在ドバイ在住。ネット広告、EC、アドテク、マーケツール、健康、ハピネスなどに興味があります。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA