このエントリでは、Yegor Bugayenkoによる記事、Continuous Integration is Deadを紹介する。 (Yegorから和訳と転載の許可は得た。) 以下はその全文の和訳だが、意訳超訳が混じっているので、もとのニュアンスを知りたければ元記事を読んでもいいし、読まなくてもいい。


数日前、「なぜ継続的インテグレーションは機能しないのか」という私の記事がDevOps.comに公開された。 それとほぼ同じ日に、Twitterで非常に否定的な批評が送られてきた。

継続的インテグレーションが機能しないとはどういうことだ。この人気なすばらしいアイデアが。

その求めてもない質問への返事をここに書く。

私はこの分野に関して多少の経験があるが、それに基いた論拠は挙げない。 代わりにロジックだけを頼りにする。

ところで、私には50以上のオープンソースや営利プロジェクトで5年間Apache Continuum、Hudson、CruiseControl、Jenkinsを利用した経験がある。 さらに、数年前fazend.com(2013年にrultor.comに改名)というホスト型継続的インテグレーションサービスを開発した。 現在TravisAppVeyorのアクティブユーザでもある。

継続的インテグレーションはどう機能すべきか

考え方はシンプルで明確だ。 masterブランチ(Subversionなら/trunk)に新しくコミットをする度に、継続的インテグレーションサーバ(またはサービス)はプロダクト全体のビルドを試みる。 「ビルド」というのはコンパイル、ユニットテスト、統合テスト、品質解析などを意味する。

その結果は「成功」か「失敗」だ。 もし成功だったら「ビルドがクリーン」であると言う。 もし失敗だったら、「ビルドが壊れている」と言う。 通常、ビルドが壊れるのは、以前通っていたユニットテストを通らなくするような新しいコードをだれかがコミットしたからだ。

これは問題の技術的な面だ。 この部分はいつも上手くいく。 まあ、依存が直書きされてるとか、ビルド環境が十分分離されていないとか、ビルドの並列性が完全じゃないとか、そういう問題はあるかもしれないが、この記事はそれらについてではない。 アプリケーションが上手く書かれていてユニットテストが安定しているなら、継続的インテグレーションは簡単だ。 技術的には。

組織的な面を見てみよう。

継続的インテグレーションというのは、ビルドを実行するサーバだけを指すのではなく、上手く機能すべき管理的/組織的プロセスだ。 プロセスが上手く機能するとは、Jez Humbleが「継続的デリバリー: ビルド、テスト、デプロイの自動化による確実なソフトウェアリリース」の55ページで言っていることそのものを意味する。

もしビルドが失敗したら、開発チームは何をやっていたとしてもそれを中断して、そのビルドの問題を速やかに直す。これが重要だ。

これが上手くいかず、上手くできないことだ。

誰がこれを必要としているのか

既に述べた通り、継続的インテグレーションとは、開発チーム全体を止めて壊れたビルドを修正させることだ。 繰り返すが、ビルドが壊れたら直ちに、それを修正し、ビルドを安定した状態に戻すコミットを入れることに全員が集中すべきだ。

ここでひとつ疑問が生じる。誰が、活動中のチーム内の誰がこれを必要としているのだろうか?

一刻も早く新しい機能をリリースしたいプロダクトオーナ? または、締め切りに責任を持つプロジェクトマネージャかもしれない。 もしくは、他の誰かが作りこんだバグをプレッシャーを受けながら修正すること嫌うプログラマかもしれない。

誰がこの継続的インテグレーションを好み、誰が必要としているのか?

誰でもない。

実際に何が起こるのか

教えよう。 私は何度も見たことがある。 シナリオはいつも同じだ。 継続的インテグレーションのビルドステータスは単に無視されるようになる。 ビルドがクリーンか壊れているかにかかわらず。 そして以前のやり方が継続される。

Jez Humbleが推奨するように開発を止めて問題に対応したりしない。

代わりに、継続的インテグレーションサーバから来る情報を無視する。

しばらくして、次の日かもしれないし月曜日かもしれないが、空いた時間を探してビルドの修正に取り組む。 これは単に、ダッシュボードの赤いボタンが嫌で緑に変えたいからだ。

規律についてはどうか

そう、これには別の見方もある。 チームに規律を徹底させることもできる。 ビルドは常にクリーンで、壊した人は何らかのを受けるという厳格なルールを設けることができる。

これを試すとなると、恐怖駆動型開発を実施することになる。 プログラマは、ビルドを失敗させたら少なくとも謝罪しなければならなくなるため、リポジトリへのコミットを恐れるようになる。

この場合の厳格な規律(私は大好きだが)は、単に状況を悪化させる。 開発プロセス全体が遅くなり、プログラマはビルドを壊さないように自身のコードをできるだけ長い間手元に保持する。 いざコミットするとなった時、変更は巨大になっていて、マージは非常に難しいか、時に不可能になる。

結果、プログラマが書いた多くのコードがコミットされること無く捨てられる。 あの恐怖因子のせいだ。

OK。解決策は?

それについては以前書いた。 「読み取り専用マスタブランチ」だ。

これは単純で、masterへのマージを一切禁止し、誰でも実行できるスクリプトを作る。 このスクリプトがマージ、テスト、コミットを実行する。 このスクリプトには例外が全く無い。 どんなブランチであっても、たった一つのユニットテストに失敗しただけでも、ブランチ全体が却下される。

言い換えると、masterにそのコードが入る前に赤いフラグを揚げる。

これで全ての問題が解決する。

第一に、ビルドは常にクリーンだ。 ビルドをクリーンに保たないコードは誰もコミットできないので、単純に言ってビルドを壊すことはできない。

第二に、何かを壊すという恐怖が無い。 単に技術的に壊せないのだ。 マージスクリプトから却下されることしかできない。 その場合、エラーを修正してスクリプトに再挑戦を命じる。 誰もこのやりとりを見ていないので、謝罪する必要が無い。 恐怖因子は消えた。

ところで、君のプロジェクトでrultor.comを利用して、この「読み取り専用マスタブランチ」原則を徹底してみてくれ。


以上がYegorの記事。

CIは死んだというセンセーショナルなタイトルではあるが、ニーチェとは違って神なるCIを否定しているわけではない。 CIって意外と上手くいかないけど、こうすれば改善できるよという主旨の記事だ。

Yegorが指摘している、ビルドステータスが無視されるようになるという一つ目の問題は、実例を多く見たことがあるわけではないが確かになんだかよく起こりそうな話だ。 そういえば私もPcap4JのTravisでのビルドエラーをもう数か月無視している。 まあこれはTravis側の問題が原因で、回避策を入れるのが気が進まないだけなんだけど。

Yegorのやり方は、GitHub Flowmasterは常にデプロイ可能としているのを、より厳密に守るように仕組化する感じであろうか。

GitHub Flowを世に広めたScott Chaconによれば、

テストされていなかったり、ビルドを破壊するようなコードをmasterにpushした場合には、開発チーム間におけるソーシャルな取り決めを破ることになり、ちょっと気まずい思いをすることになる

とのことで、これはまさにYegorが恐怖因子と指摘した二つ目の問題である。 慣れない内のリポジトリへのコミットの緊張感や、CIサーバからエラー通知が来た時の焦燥感は、多くの人のストレスになっているんじゃないだろうか。 masterの更新をスクリプトに任せてしまえば、それでなおビルドが壊れたとしてもスクリプトのせいにできるので気が楽だろう。

実装が簡単そうなアイデアでもあるので、いつかCIを実装する日まで覚えておきたい。