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 の形を変えないので、実際に順番待ちになるのは月に数本です。
整理した結論
- PR は main 宛てに 1 本。STG 用のブランチや PR は作らない
- 回帰テストの最低セット(主要な流れ 10 本前後、E2E 形式)を PR ごとの CI で走らせ、赤ならマージできない
- 各機能の受け入れテストは、完了時に回帰テストの束へ移す(束を育てる責任者を決める)
- STG は 1 台 + PR ごとのプレビュー環境。DB は共有し、DB を変える PR は直列
- 本番デプロイはテストが緑のときだけ。直後にスモークテスト
古いバックログについても、リリース前の環境で起きた不具合は前提が変わっているので、一括で「保管」扱いにして、同じ現象が今の本番で再現したら新しく起票する、と決めました。古い記述に沿って直すと、いま動いている機能を壊す方向に働くからです。
反省
「E2E をしっかりやる」「STG を整える」とは何度も言っていましたが、目的と形式を分けて考えるまで、何が足りないのかを言葉にできていませんでした。足りなかったのは「既存を守るテストを蓄積して毎回走らせる仕組み」で、STG の台数でも E2E の量でもありませんでした。