uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

CIのメリットは何か私案

(本論は、CIのメリットを「テストの自動化そのもののメリット」など他の絡み合ったメリットから分解して論じ、明確化することを目的としています)。

ソフトウェアの開発において「インクリメンタル開発」という方法論もしくは方針があります。

インクリメンタル開発は、まず開発対象ソフトウェアが実現する機能を果たすための意味のある最小限の機能コアを、最優先で作り上げ、それが動作する状態を保ちつつ、機能の追加を繰り返していくことで最終的な機能を持つところまで到達させるという開発手法(手順)です。

インクリメンタル開発における機能追加の際には、追加した結果、全体が依然として正しく動作するままであるかを、その都度確かめ、うまくいったときに限って次のステップに進みます。もしうまくいかないようなら、次の段階には進みません。順を追って、すでに作った部分を土台にして(新しい部分の動作を確認するための基準として用い)、一歩ずつ進んでいきます。ステップを飛ばして、連結しないぜんぜん別の先のほうに着手したりはしません。なぜなら、「屋根を作ってそれを放り投げて空中にある間に急いで柱を立てる」のはたいていうまくいかないからです(不可能ではないかもしれないが、難易度が高い)。

インクリメンタルな機能追加の単位は小さければ小さいほど安定します。なぜなら、少しだけの機能追加の際には、システム全体への影響が限られ、原因追求や対処も迅速に行えるからです(システムが動作していない不安定な時間を最小化する)。これは電車の遅れにたとえると、遅れた電車のダイヤを回復するのに、1本が少し遅れるのなら単純に回復可能ですが、遅れの発見が遅れ、複数路線に渡って大幅にずれると、その回復は至難の業である、ということに似ているかもしれません。ダイヤの回復途中は事故が起きる危険度も増大します。遅れていない状況はただ一通りですが、遅れている状況は多様で一回性が高いからです。電車システムは、遅れていないときが一番安定していて、安全なのです。同様に、「システム開発の過程で、いついかなる瞬間もその整合性を保ち、成長する」というモデルは一番作りやすいのです。それが文字通りそのままは達成不可能にせよ、その状態に少しでも近づくことが、品質にも開発期間にも良い影響を及ぼします。

CI(継続的統合)のメリット(の一つ)は、このような「一歩一歩の開発」もしくは「インクリメンタル開発」が効果的だという成立原理に同様に立脚しています。結合ビルドの間隔が1日開けば、不安定な状態での開発が1日分進んでいて、無駄な作業や手戻り、分析や対処をすぐにできるチャンスを1日遅らせていることになります。開発対象のソフトウェアを、そのような不安定な状態に放置することを、可能な限り、一刻でも、抑止するというのがCIの利点の直感的な理解です。これを遅らせると、ずれはずれを呼び、ベースを試験の基準として日々機能させることができなくなります。追加した部分を、しっかりと動作確認できるためにも、ベースが正しくビルドされ検証され正しく動作していることが、常に期待されます。CIはそのためのものです。

そして、基準は1つであることも必要です。何かビルドエラーや環境設定の問題が感知されたとき、Aさん、Bさん、Cさんが開発者だったら、Aさんの環境ではうまくいくがB,Cさんの環境でうまくいかない、というときに、

  • AさんとBさんの環境の違い
  • BさんとCさんの環境の違い
  • CさんとAさんの環境の違い

をそれぞれ調査しなくても、ビルド環境と、自環境の違いを調査すればいいということになります。O(N!)ではなくCI環境をピボットに使うことでO(n)の調査ですむということです。要は一元管理、マスター管理です。

この根本のメリットを理解でき、CIがそれに貢献できることが直感的に理解できるなら、ことさらにCIのメリットを言う必要はなくて、CIがない状態がいかにだめかを言えば良いでしょう。(結語がいまいちまとまらん・・・)

なお、自動ユニットテストのメリットにも同じことが言えます。基準を作ったうえで、それからのズレを感知しフィードする、というサイクルのなかでコードを成長させるということです。

インクリメンタル開発を行う際に自動ユニットテストが必須であるわけではありませんが、自動ユニットテストは(細粒度の)インクリメンタルな開発の効率化に顕著に役立つでしょう。