継続的インテグレーション
Martin Fowler の Continuous Integration を取得して読み、 理解した内容を自分の言葉で書いたもの。本文の引用はしていない。
このリポジトリの各所に 壊れてほしくないものは CI に入れる が出てくるが、CI そのものの設計が空いていた。ここがそれ。
CI は道具の名前ではない
最初に、言葉の使い方を正す必要がある。
チームの各人が、少なくとも毎日、自分の変更を同僚の変更と一緒にコードベースへ統合すること。
「CI サービスを使うこと」は CI ではない。
要点は方向にある。CI が要求するのはメインラインへの完全な統合、 つまり自分の作業を共有ブランチへ戻すことであって、 共有ブランチから取り込むことではない。
フィーチャーブランチ上で自動ビルドを回すのは「半統合」。 メインラインの変更を手元で合わせてはいるが、自分の仕事は戻していない。
基準は明快で、どのコードもブランチ上に数時間以上留まらない。
なぜ効くのか
統合の費用は、溜めた量に対して線形ではない。
統合するコードが2倍あるなら、統合にかかる時間は4倍になりやすい。
統合で表に出る衝突は2種類ある。
| 誰が見つけるか | |
|---|---|
| テキスト上の衝突(マージ衝突) | バージョン管理が検出する |
| 意味上の衝突 | テストかコンパイルでしか捕まらない。 2人が関連するロジックを両立しない形で変えた場合 |
厄介なのは後者。 マージは成功するのに、動きが壊れる。
毎日統合していれば、コミット間の変更は数時間分しかないので、 問題が潜んでいる場所がそもそも限られる。 差分をたどって原因を絞り込める。
そしてもう一段深い理由がある。
統合とは、第一にコミュニケーションのこと。
頻繁に統合していれば、変更が育っていく途中で互いに気づける。 完成してから初めて見せ合うのではない。
バグにも同じ非線形が働く。 検知されない欠陥は積み上がり、いずれ危機になる。
実践
ビルドに必要なものはすべてリポジトリから取れる
ビルドが自動で、かつ自己テストする
開発者は手元のビルドを通したうえで、最低でも毎日メインラインへ押す
メインラインへのコミットのたびに、基準となる環境で統合ビルドが走る
ビルドが壊れたら、それより優先度の高い仕事は誰にもない
自己テストするビルドの水準が高く置かれている。 健全なテストスイートなら、そもそも壊れた状態を赤くせずに通すことはない、 という前提で運用する。
ビルドは10分
XP の「10分ビルド」は十分に妥当な指針。
根拠は算術で、節約した1分が、全開発者の1日のコミット回数だけ効いてくる。
10分に収まらないとき
段階に分ける。
| 段階 | 中身 | 時間 |
|---|---|---|
| コミットビルド | 単体テスト。遅いサービスはテストダブルに置き換える(インメモリの偽データベース、外部サービスのスタブ) | 10分程度 |
| 二次ビルド | 実際のデータベース、エンドツーエンド | 数時間になりうる |
メインラインの健全性を守るのはコミットビルドの側。 二次ビルドは資源に余裕があるときに走らせる。
ここが一番効く指針。
二次ビルドが落ちたら、コミットビルドに適切なテストを足す。
遅いフィードバックを受け入れるのではなく、テストを上流へ押し上げる。 そうしないと、二次ビルドが「たまに落ちるもの」として放置されていく。
並列化と、本番の複製
テストが互いに独立しているなら、並べれば速くなる。 クラウドなら、ビルド用のサーバーを一時的に何台も立てられる。
試験環境はできるかぎり正確に本番を模す。 コンテナと再現可能な環境構築のスクリプトで、ここの摩擦は下げられる。 試験で起きないことが本番で起きる状態を減らすため。
ビルドの決定性と、 リリースとデプロイの密閉ビルドが、 そのまま前提になる。
壊れたら、まず戻す
最善の直し方は、メインラインから直近のコミットを revert して、 既知の良いビルドまで戻すこと。
理由は、チームを即座に解放するから。 原因の診断と修正は、別の開発環境でやればよい。 直し方を考えているあいだ、全員を止めておく必要はない。
メインラインを壊さないために、事前検証済みのコミットだけを通す仕組み (Pending Head)を使うチームもある。 ただし規律があるなら、この防御は必須ではない。
フィーチャーブランチとの関係
統合の「頻度」と「様式」は別の軸。
フィーチャーブランチは半統合を提供する。 メインラインからは定期的に取り込むが、押すのは機能が完成したときだけ。
ここに非対称が生まれる。 互いの変更は、どちらかが機能を完成させて統合するまで見えない。
長命なブランチが CI と衝突する理由は3つ。
統合の費用が非線形に膨らむ
レビューを待つあいだの文脈切り替えが遅延を生む
マージが痛いと、リファクタリングが高くつきすぎてやらなくなる
3つ目が効いてくる。 頻繁な統合は、リファクタリングする側とそれ以外の全員を定期的に同期させるので、 このジレンマを解く。
ブランチ戦略のトランクベース開発と、 未完成のものをどうトランクに置くかが、 実装面での対になる。
既存の記述との対応
| 既にある記述 | ここでの位置づけ |
|---|---|
| 壊れてほしくないものは CI に入れる | 何を入れるか。この文書はどう回すか |
| テストの規模 | コミットビルドと二次ビルドの分け方の基準 |
| 依存をどう扱うか | テストダブルの使い分け |
| ブランチ戦略 | トランクベース開発 |
| デプロイ戦略 | CI が整っていることが前提になる |
| 静的解析 | パイプラインのどこで出すか |
| パフォーマンス予算 | 性能の回帰をコミットビルドで止める |
| アクセシビリティの検証 | 自動チェックを入れて「対応した」と見なさない |