Skip to content.

Sections
Personal tools
You are here: Home » コミュニティ » XP-jp » XP関連記事 » 継続的インテグレーション

Document Actions

継続的インテグレーション

原題: Continuous Integration


Martin Fowler
Chief Scientist, ThoughtWorks

Matthew Foemmel
ThoughtWorks

「確実なビルドを行う」 -- これはどんなソフトウェア開発プロセスであれ重要なことだ。そのわりには、このことがきちんとされていないことに驚かされる。本論文では、Matt が ThoughtWorks 社でのある大規模プロジェクトにおいて採用したプロセスを紹介する。このプロセスは全社的な広がりを見せつつある。テスト部分も含めて「全てが自動化された」「再現可能な」ビルドを、「日に何度も」行うことに力点がおかれている。このプロセスを用いれば、開発者はインテグレーションを毎日行うことになるので、インテグレーションに伴う問題を減らすことができる。

議論には登るが、実行に移されることの少ない「ベストプラクティス」がソフトウェア開発にはいっぱいある。その中でも、もっとも基本的かつ重要なもの一つが、「完全に自動化されたビルドとテストプロセス」だ。これは、プロジェクトチームが自分のソフトウェアのビルドやテストを一日に何度も行うことを可能にするものである。もちろん、毎日のビルド(デイリービルド)、という考えが議論されたのはこれが初めてではない。McConnnell はデイリービルドをベストプラクティスとして推奨しており、またマイクロソフトでの開発アプローチの特徴としても長く知られている。しかし我々は、XP コミュニティの声にならって、「デイリービルド」は最小限の要件でしかない、と主張したい。完全に自動化されたプロセスにより一日に何度もビルドを行うことは、実現可能であるし、また実現させるだけの価値があることなのだ。

XPのプラクティスの一つとして掲げられている「継続的インテグレーション」という単語を我々は使用している。しかし、このプラクティス自体はXPに関係なく今までもずっとあったわけだし、またXPを自分の仕事に使おうとは絶対に思わないような人々でも、このプラクティスを使っている人も多い。我々は XP を自分たちのソフトウェア開発プロセスの規準として採用しているので、我々の言葉使いやプラクティスには XP の影響を深く被っている。しかし、あなたが他のXPプラクティスには従わなかったとしても、この「継続的インテグレーション」を用いることは可能である -- 実のところ我々は、この「継続的インテグレーション」は、あらゆるまともなソフトウェア開発活動の基礎となるものだと考えているのだ。

自動化されたデイリービルドの実現に必要なことは、以下の点にまとめられる。

  • 全てのソースコードが格納され、誰でも最新の(そして過去の)ソースを取り出すことが可能な、ただ一つの出入口を設定すること
  • ビルドプロセスを自動化して、誰でも、コマンド一つでソースからシステムを作り出すことができること
  • テスト作業を自動化して、コマンド一つでいつでもテストスィートを実行できるようにすること
  • 現行の実行可能モジュールで、もっとも適切であると自信を持てるようなものを、誰でも入手可能であるようにすること。

上に掲げたものの実現には全て、ある程度の訓練を必要とする。実践してみて我々は、プロジェクトにこれらを導入することは容易ではないことを学んだ。しかし一方、いったん導入された後の維持には、それほど労を要さないこともわかった。

継続的インテグレーションの恩恵

継続的インテグレーションについて語る際に、言葉で伝えにくい部分がある。それは、継続的インテグレーションは開発全体に関わる「パターン」への移行をもたらすものだということであり、こればかりは、実地で体験してみないと理解しがたいのだ。実は、普通の人でも、個人で作業しているあいだは雰囲気としてこの開発パターンをつかんでいる -- というのもその時は、インテグレートする相手といえば自分しかいないからだ。チームによる開発となると、チーム開発につきものといってもよい幾つかの「面倒」が出てくるものだと一般に考えられている。継続的インテグレーションを使うには訓練が必要とされるが、その代わりに、この「面倒」を取り除いてくれるのである。

ある人の作ったバグが他の人の作業に影響してしまい、しかもそのことに当人たちが気づいていない、という状況でのデバッグ作業に時間を取られてしまうことがある。継続的インテグレーションの最大の恩恵は、こういったセッションを無くしてくれるということにある。この手のバグ発見は非常に難しい。バグが、一人の担当範囲内ではなく、二つの作業結果が引き起こす相互作用の中に潜んでいるからである。時間の経過がこの問題をさらに困難なものにする。この手のインテグレーションバグが組み込まれた時点は、バグが最初にその姿を現す何週間も何ヶ月も前だということが多い。結果として、原因発見にも長い時間が費やされることになる。

継続的インテグレーションを使えば、この手のバグの大多数は、組み込まれたその日のうちにすぐに姿を現すだろう。さらに、相互作用の少なくとも一方の側を担うモジュールがどれかということも即座に判明する。こうなれば、バグの捜索範囲は大幅に狭めることができる。仮にバグが見つからなかったとしても、バグ有りコードを製品に入れないようにすることはできるので、最悪でも「まだバグの取れていない機能を落とす」というラインを維持することが可能になるのだ(もちろん、バグを除くよりも機能を追加することのほうが重要だという状況もあろうが、しかしその場合でも、情報を得た上での判断(informed choice)を行うことができる)。

もちろん、この方法でインテグレーションに絡むバグは全て捕捉可能だと保障するものではない。この方法はテストをするだけで、テストは「エラーが存在しない」ことを証明できない。重要なことは、継続的インテグレーションはコストに見合うだけの十分な数のバグを捕捉してくれる、ということだ。

これらを組み合わせると、インテグレーションにからむバグの追跡に費やされる時間が節約されることから、生産性が増大することになる。この点を科学的なアプローチで研究した人は私の知る限りいないようだが、非常に有力な経験的証拠ならある。継続的インテグレーションは「インテグレーション地獄」で過ごさざるを得ない時間を減らしてくれる。それどころか、この地獄を、ありきたりの何でもないこと(non-event)に変えてくれるのだ。

たくさんやるほうが望ましい

継続的インテグレーションが及ぼす根本的な影響には、直感を裏切るところがある。それは「よりひんぱんにインテグレーションを行うほうが、まれにしか行わないよりも望ましい」ということだ。実践している人にはこれは当たり前のことだ。しかし実践していない人にとって、これは、自分のじかの経験と矛盾するように映ってしまう。

まれにしか、例えば一日以上の間隔でしかインテグレーションを行わないのであれば、インテグレーションは時間と労力を費やすやっかい事である。あまりにやっかいなので、これをもっと頻繁にやろうなどとは思わないだろう。「プロジェクトがこれだけ大きくなると、デイリービルドなんて不可能だよ」というコメントを私たちもよく聞いたものだ。

しかし実際にそのデイリービルドを行っているプロジェクトがある。50人の開発チームが作業を行う、20万行に近いコードベースをもとに、私たちは日に20回以上もビルドを行っている。マイクロソフトは数千万行のコードがあるプロジェクトで、このデイリービルドを行っている。

なぜこれが可能なのか?インテグレーションにかかる労力は、インテグレーションを行う間隔の長さに対して指数関数的に増えていくからだ。どのような測度がふさわしいかは定かでない。しかし、これの意味するところは、週に一回のインテグレーションは日に一回のインテグレーションの5倍かかる、わけではなく、おそらく25倍はかかってしまうということである。あなたがもし、インテグレーションがやっかいだと感じていたとしても、これを「さらに多くの回数のインテグレーションなどできるはずがない」ことの証拠としてとらえるべきではない。うまくやれば、何回もインテグレーションを行う作業は楽になるし、またインテグレーションそのものにかかる時間もずっと少なくなるのだ。

ここで重要となるのは「自動化」である。ほとんどのインテグレーションは自動化できるし、またそうすべきなのだ。ソースを取ってきて、コンパイルして、リンクして、徹底的にテストする、これら全部を自動化すべきなのだ。全部終わったら、ビルドが成功したかどうか、最後に結果をイエスかノーで出力する。イエスなら無視して構わない。ノーだったとしても、最後に行ったソフトウェア構成への変更を取り消してからビルドすれば、今度は成功するはずだ。なにも頭を使わなくても、「動く」ビルドを作成できるわけだ。

こういった自動化プロセスがあれば、何回も好きなだけビルドをすることができる。制約となるのは、一回のビルドにかかる時間だけだ。

「成功したビルド」とは?

何をもって「成功したビルド」とみなすのか?これを決めることは重要である。そんなものは当たり前だと思われるかもしれないが、これがなかなか手強いのだ。あるプロジェクトのレビューを Martin が行ったことときのことだ。「デイリービルドを行っているか?」という質問に対してメンバーは「やっている」という答えを返した。しかし、幸いにも Ron Jeffries がその場にいて、彼がさらに突っ込んで「ビルドのエラーが起きた場合はどうするのか?」と訊いた。答えは「担当している人にEメールで通知する」というものであった……実際には、プロジェクトではもう何ヶ月もビルドに成功していない。これではデイリービルドではない。ビルドを「しようとする毎日の試み」でしかない。

私たちは非常に欲張りな定義を「成功したビルド」に課すことにする。

  • 全ての最新のソースファイルが構成管理システムからチェックアウトされる
  • 全てのファイルが、まっさらな状態からコンパイルされる
  • 生成されたオブジェクトファイル(この場合は Java クラスファイル)が、 リンクされて実行可能な場所へ配置される(jarファイルの中に入れられる)
  • できたシステムの実行を開始して、同時にこのシステムに対してテストスィート(私たちの場合は、約150個に及ぶテストクラス)を実行する。
  • もし上記の全てのステップが、エラーも起こさずに、また人の手も借りずに実行され、かつ全てのテストにパスしたのであれば、私たちは「成功したビルド」を手にしたことになる。

普通の人は、コンパイル作業とリンク作業のことをビルドだと考えている。しかし私たちは、ビルドには少なくとも「アプリケーションを実行すること」「簡単なテストを行うこと」まで含めるべきだと考える(McConnnell はこれを「スモークテスト」と呼んでいる -- スイッチを入れたら煙がモクモク……、などとはならないがことを確かめているわけだ。)。これらに加えて、さらに徹底的なテストを行えば、継続的インテグレーションのもたらす価値はより大きなものとなるので、それも併せて行うほうが望ましいだろう。

シングルソースポイント

簡単にインテグレーションを行うためには、あらゆる開発者が、現行のソースコード全一式を簡単に手に入れられるようにしておかなくてはならない。「最新のコードはどこだ!」とたくさんの開発者に聞いてまわり、その上でコードをコピーして、さてこれはあのディレクトリにおいて…などと配置を考えたりしないとビルドが始められないというのは最低である。

満たすべき基準は簡単だ:「だれでも、真っ新なマシンを一台用意して、ネットワークにつなげば、後は開発中のシステムのビルドに必要な最新のソースファイルの全てをコマンド一発で呼び出せる。」これである。

この問題の明らかな(と信じたいが)解決方法は、構成管理(ソースコード管理)システムを用いて、ここに全てのコードをおくことだ。ふつう構成管理システムはネットワーク越しに使用されることを前提としているし、ソースの進展を追跡するためのツールも用意している。さらに、版管理の機能も付いていて、各ファイルの旧版を取り出すことも簡単である。コストも問題にならない。というのも、CVS というオープンソースのすぐれた構成管理ツールがあるからだ。

これが機能するには、全てのソースファイルを構成管理システムに入れる必要がある。この「全て」には、ふつうに想定されている以上のことが含まれる:スクリプト、属性ファイル、データベーススキーマを定義したDLL、インストール用スクリプト、ともかく真っ新なマシン上でビルドを行う時に必要なもの「全て」である。管理下にあるコードは見つかったが、肝心なファイルで見つからないものがあり、探さなくてはいけない、ということがよくあるからだ。

だから、全てのものが、ただ一つのソースコードツリー下にあるようにするべきだ。よく、別個のコンポーネントに対して構成管理システムの別々のプロジェクトを割り当てる人がいる。これだと「どのコンポーネントのどのバージョンが、他のコンポーネントのどのバージョンとうまく動くのか」覚えていなくてはならない。どうしてもソースを分けなくてないけない状況もあるだろう。しかしそんな状況は思われているよりずっとまれである。ソースコードツリーを一つにしても、そこから複数のコンポーネントのビルドを行うことは可能だからだ。こういうことは、保存領域の構造で分けるのではなく、ビルド用スクリプトの処理でふりわけるべきなのだ。

自動化されたビルドスクリプト

1ダースかそこらのファイル群からなる小規模のプログラムを書いているなら、アプリケーションのビルドなど、javac *.java といったコンパイラ起動のコマンド一発ですむ話だろう。大規模なプロジェクトではそうはいかない。ファイルはたくさんのディレクトリに分散している。生成されたオブジェクトファイルが正しい場所に置かれることを確認しなくてはならない。コンパイルだけでなくリンク作業もあるだろう。コンパイルの前に、他のコードから生成しておかなければならないコードもある。テストも、自動的に実行されるようにしなくてはならない。

大きなビルドには時間がかかるものだ。だから、加えた変更が少なかったときなどはビルドの全部のステップを実行したくない、と思うだろう。良いビルドツールは、ビルドプロセスのうちどこを変更すればよいか解析をしてくれる。よくある方法は、ソースとオブジェクトの日付をチェックして、ソースの日付のほうが新しい時だけコンパイルするというものである。しかし依存関係はこれだけにとどまらない。あるオブジェクトファイルが変更されたときには、そのファイルに依存している他のオブジェクトファイルも再ビルドする必要がある。コンパイラはこのような機能をサポートしているかもしれないし、していないかもしれない。

必要に応じて、別のものをビルドすることがある。テストコード付き、または無しのシステムや、普通は使用しないテストを含めたシステムなどである。単独でビルド可能なコンポーネントもある。ビルドスクリプトは「個別のケースに応じて別個のターゲットをビルドする」という要求を満たすものでなくてはならない。

コマンドラインからの入力を卒業したら、あとはスクリプトが重荷を負担してくれる。これらをシェルスクリプトで書いたり、または perlやpythonといったより高機能なスクリプト言語で書いているかもしれない。しかしそのうち、この目的で設計された環境、例えばUnixのMakeなどを使うのがよい、ということになるはずだ。

しかし私たちが行ったJavaよる開発では、すぐに、より徹底した解決案を考える必要に直面した。実際に Matt はEnterprize Java での開発用に設計された "Jinx"と呼ばれるビルド用ツールに、かなりの時間を割いた。ところが最近になって、オープンソースのビルド用ツール Antに切り替えることにした。Antは 非常にJinxに設計が似ており、どちらも、Java ファイルをコンパイルしてそのパッケージを Jar に入れる作業をしてくれるというものである。Ant に独自の拡張を書きくわえて、ビルド内で別の作業を行わせることも容易だった。

我々のチームの中でも、IDE(訳注:統合開発環境)を使っている人は多い。そして、いくつかのIDEにはビルド管理プロセスみたいなものが組み込まれている。しかし、その管理プロセスの過程でIDE専用のファイルを用いており、これらは脆弱であることが多い。さらに、IDEが無いと動かないという問題もある。よって、IDE の利用者が個人の開発をする際には自分用のプロジェクトファイルを設定してもらうが、メインとなるビルドは Ant で行うし、またマスタービルドは、Ant が走るサーバー上で実行される。

自己テスト用コード

プログラムをコンパイラに通しました、というだけでは不十分だ。強く型付けされた言語のコンパイラは多くの問題点を指摘してくれるだろうが、それでも、コンパイルが成功したときに見落とされてしまう間違いの数はあまりにも多い。これらの間違いを見つける手助けとして、私たちが強調したいのは、「自動化されたテスト」 -- XP で唱道されているプラクティスの一つ -- という規律に従うことである。

XP ではテストを、「単体テスト」と「受け入れテスト(機能テスト)」という二つのカテゴリに分類している。「単体テスト」は開発者によって書かれ、クラス一個だけとか、いくつかのクラスとかをテストするのが普通である。「受け入れテスト」は(開発者の助力を得ながら)顧客や外部のテストグループによって書かれ、システム全体の端から端までをテストするものである。私たちは両方のテストを使うし、またどちらのテストに関しても、できる限り自動化を行っている。

ビルドの一部として、"BVT"(Build Verification Tests:ビルド検証テスト)と呼んでいるテストスィートを実行する。「ビルドが成功した」と言えるには、"BVT"に含まれる全てのテストが成功しなくてはいけない。XPスタイルの単体テストは全て"BVT"に含まれる。この論文はビルドについて書かれたものであるから、BVTについての記述が中心となる。しかし、「テスト作業」の戦線にはBVTだけではなく第二陣もあるのであり、BVTだけで、テスト作業とQAにかかる工数を見積もれるとは思わないで欲しい。実際、我々のところのQAグループの人たちは、BVTをパスするまでは私たちのコードを見ることもない。彼らは動作するビルドしか扱わないからだ。

「開発者がコードを書くときには、コード対応するテストも書く」これが基本原則である。タスクが終了したら、製品用のコードだけではなくテスト用のコードもチェックインする。XPを忠実に実践している人なら、「テストが先」というプログラミング・スタイルを採用するだろう:最初は失敗するようなテストが書き上がるまで、コードを書いてはいけない。新しい機能をシステムに加えようとしたら、まずは、その機能が実装された時にはじめて通るようなテストを書いて、その後に、テストを通すように努力するわけだ。

我々は、Javaでテストを書いている。これは、開発に使っている言語と同じであるから、「テスト書き」も「コード書き」も同じということになる。テストの作成や整理のためのフレームワークには、 JUnitを使っている。JUnit はシンプルなフレームワークであり、これを使うと、ちょっとテストを書いたり、それをテストスィートに加えたり、対話型かバッチ型か選んでスィートを実行したりすることができる(JUnitは、ほとんどあらゆる言語に対する版が揃っているxUnitファミリーのJava版なのである)。

開発者がソフトウェアを書く時には、コンパイルを行う度に、ユニットテストの一部を実行することが普通である。これをやると、開発時間が実は短縮される。なぜならテストが、作業中のコードにあるロジックの間違いを見つけだす手伝いをしてくれるからである。間違いがあったら、あなたはデバッグ作業をするかわりに、「最後にテストを成功させたコードから加えた変更分」にだけ注意をはらえばよいのだ。これらの変更は小さいはずなので、バグを見つけるのも簡単なはずだ。

全ての人が厳密なXPスタイルの「テストが先」で作業しているわけではないが、それでも、テストとコードを同時に書くことの利点は大きい。同時に書くことは、個人の作業をスピードアップするばかりか、より多くのエラーを捕捉するためのBVTを作り上げてくれるからだ。 BVTは日に何度も実行される。よって前述した理由とおなじ理由で、BVTが検知した問題がどこにあるのか、見つけだすことは容易であるはずだ。つまり、バグをみつけるのにコードの差分しか見なくてすむから、という理由である。差分だけを見るデバッグ作業は、コードを一ステップずつ追って行くデバッグ作業よりずっと効率的である。

もちろん、テストで全部がカバーできると思ってはいけない。よく言われるように、「テストはバグの不在を証明しない」のだ。とはいえ、完全を期さない限り「よいBVTを書くことに対しての見返り」を望めない、というわけではない。不完全なテストだって、頻繁に実行されるなら、書かれざる完全なテストなどよりもずっとましである。

関連した問題に「開発者が自分自身の書いたコードのテストを書くべきか」という点がある。自分でテストをすべきではない、なぜなら自分の間違いは見過ごしやすいからだ、とよくいわれる。確かにそうなのだが、自己テストのプロセスを実現するには、テスト内容がコードベースに反映されるまでの回転期間が短くなくてはならない(訳注:他人にテスト作成を頼むと時間がかかりすぎる)。テスターを分離することよりも短い回転期間のほうが重要度が高いのだ。というわけでBVTに関しては、開発者の手によるテストに頼ることにしている。ただし、独立した別の受け入れテストも用意してある。

マスタービルド

ビルドの自動化は、個々の開発者にとっても意味のあることだが、その真価を発揮するのは、チーム全体が使うマスタービルドを作成するときである。経験では、マスタービルドの手順を整えることで、チームがまとまり、インテグレーションに伴う問題を早めに発見することが容易となった。

まず最初のステップは、マスタービルドを走らせるマシンを選ぶことである。 我々は、ほとんどビルド専業といってよい、Trebuchet という名前("Age Of Empires"は何度もプレイしたものだ)の4-CPU サーバーマシンを使っている(ビルドに非常に時間がかかった初期のころは、これくらいのマシンパワーが不可欠であった)。

ビルドプロセスは、常に起動されているある Javaクラスの中に記述されている。ビルド実行中でない場合には、ビルドプロセスはwhileループの内部で待機して、数分ごとにレポジトリをチェックする。最後のビルドをした時点から誰もコードをチェックインしていない場合には、待機に戻る。新しいコードがレポジトリにあれば、ビルドを開始する。

ビルドは、レポジトリから全てをチェックアウトするところから始まる。Starteam が用意した対Java用のAPIは非常にすぐれているので、レポジトリにフックをかけることは簡単であった。ビルドデーモンが、現在時刻から五分前のレポジトリの状態を見て、「その五分間の間に誰かチェックインを行ったかどうか」を調べる。もし入れていれば、安全のために五分前のコードをチェックアウトする(これで、レポジトリをロックしないままに誰かがチェックインをしている真っ最中にチェックアウトを行うことを防止できる)。

デーモンは、Trebuchet上のディレクトリにチェックアウトを行う。全てチェックアウトできたら、デーモンはantで書かれたスクリプトをそのディレクトリ上で実行する。ここで、フルビルドの実行はデーモンからantに引き継がれる。フルビルドは、全てソースから行う。コンパイル、そして生成されたクラスを半ダースに登るjarファイルに分類して、EJBサーバー群への配置に備えるところまで antで書かれたスクリプトが行ってしまう。

antにコンパイルと配置まで行わせたら、ビルドデーモンは新しい jarファイルでもってEJBサーバーを起動し、さらにBVTテストスィートを実行する。テストが走り出し、全部パスしたところで「成功したビルド」の出来上がりとなる。ビルドデーモンはStarteamに戻って、チェックアウトされたソースにビルド番号でラベル付けを行う。さらにビルド中に誰かチェックインしていないか調べて、もしいたら、さらに次のビルドを開始する。いなければ、デーモンはwhileループの中で次のチェックインを待つことになる。

ビルドが完了したら、ビルドデーモンは、そのビルドで新たにファイルをチェックインした全ての開発者に対してEメールを出す。このメールには、ビルドの状況についてのまとめが書いてある。コードをチェックインした人は、このメールを受け取るまで、建物を出たらダメ、ということになっている。(訳注:自分の作業の結果を確認するまで、職場を離れて家に帰ってはいけない、ということ)

デーモンは、各ステップのログを全てXML形式のログファイルに書き出す。Trebuchet上にはサーブレットが走っており、このサーブレットからログが覗けるので、誰でもビルドの状況がわかるというわけである。

図1: ビルド確認サーブレット

スクリーンには、現在実行中のビルドの有無、ビルドが実行中ならその開始時刻が表示される。左下には全てのビルドの履歴が、成功・失敗を問わず表示される。コンパイルの成否、テスト結果、変更内容などの詳細を見るには、そのビルドをクリックすればよい。

多くの開発者が、このウェブページを定期的に見ていることがわかった。これがあると、プロジェクトで何が起きているのか、またチェックインによって何が変更されようとしているのか、把握するのに役立つというわけだ。いつかはこのページに、プロジェクトの他の情報も載せようかと考えているが、ただ、それだとまとまりがなくなる恐れもある。

重要なのは、どの開発者も、マスタービルドを自分のローカルマシンでも模倣できるようにしておくことだ。こうしておくことで、インテグレーションに関係するエラーが発生したときでも、マスタービルドを止めること無く、問題の調査とデバッグを開発者の誰かが自機で行うことができる。さらに、開発者がチェックインをするまえにローカルにビルドを行うことで、マスタービルドで失敗する可能性を下げることができる。

ここで、マスタービルドはクリーンに、すなわち、全てソースから作成するか、それともインクリメンタルに作成してもよいか、という問題を問うことができるだろう。インクリメンタルビルドのほうがずっと速くすむが、一部をコンパイルしなかったことからくる障害が発生する危険が残る。さらに、そのビルドを再現できなくなってしまう可能性もある。我々のところではビルドは十分に速いので(20万行のコードに対して約15分ほど)、毎回クリーンビルドを行っても不満はない。しかし、ほとんどの場合はインクリメンタルビルドですまして、定期的に(最低でも一日一回)クリーンビルドを行うことで不可思議なエラーが現れる事態に備える、というソフトハウスもあるだろう。

チェックイン

自動化されたビルドを採用すると、開発者は、ある「リズム」に従ってソフトウェアの開発を行うようになる。このリズムの最重要パートは、インテグレーションを「定期的に」行うこと、である。我々が訪れたある部隊では、デイリービルドを実践してはいたが、チェックインは頻繁になされていなかった。数週間おきにしか開発者がチェックインしないのであれば、デイリービルドを実践しても意味は薄いだろう。我々のところでは、どの開発者もだいたい一日一回はチェックインを行う、というおおまかな指針を立てている。

新しいタスクを始める前には、まずは、開発者は構成管理システムとの同期をとらなくてはいけない。つまり、ローカルにコピーしたソースコードが最新のものであることを保障するわけだ。古いソースコードの上に新規追加を行ってもトラブルと混乱を巻き起こすだけである。

同期を取ったあとに、開発者は必要なファイルの変更を行って、タスクをこなしていく。タスクが完了した時や、まだ途中であっても、インテグレートをして構わない。しかし、インテグレートを行うには、全てのテストを通さなくてはいけない。

インテグレーションをするには、まず作業したコピーとレポジトリの内容との同期を再び取る、というところから始める。レポジトリ上で内容が変わったファイルは全て作業ディレクトリにコピーされ、かつコンフリクトがあれば構成管理システムがワーニングを出す。開発者はこの「同期を取った後のファイル群」に対してビルドを行い、そしてBVTを通さなくてはいけない。

ここまでできて、開発者は新しいファイルをレポジトリにコミットして構わない。コミットが終わったら、開発者は、マスタービルドが実行されるまで待機する。マスタービルドが成功したら、チェックインが成功したということになる。失敗だったらどうするか。問題の解法があきらかならば、修正したコードをコミットすればよい。問題が複雑な場合には、まずは変更を取り下げ、さらに同期を取って、ローカル環境でちゃんと動くようにする必要がある。その後に再コミットを行う。

チェックインの内容によっては、順番を定めてチェックイン作業を行う(serialization of the check-in process)必要がある、という場合がある。このときには、同時に二人以上の開発者が手にすることのないような「ビルドトークン」というものを用意する。開発者はまずこのトークンを入手して、作業内容の同期を取り、変更点をコミットして、最後にトークンを手放す。こうすれば、ビルドの最中に二人以上の開発者がレポジトリ内容を更新することを防止できる。ただ、ビルドトークンが無くてもトラブルになることは少なかったので、我々はビルドトークンを用いなかった。同じマスタービルドに対して二人以上の開発者がコミットすることなどしょっちゅうあった。しかしこれがビルドの失敗につながることは非常にまれだったし、起きたとしても修正はすぐ行うことができた。

また、チェックインに際してどこまで注意を払うか、という点に関しても、個々の開発者の判断にゆだねることにした。インテグレーションエラーが発生する可能性がどれだけあるかという見積もりは、それはその開発者に聞くしかないからだ。もしエラーが発生しそうだと思えば、開発者はローカル環境でビルドしてからチェックインを行うだろう。エラーが起きないだろうと思ったら、そのままチェックインするだろう。起きないだろうと思っていたエラーが発生したとしても、マスタービルドが実行された後にエラーに気づかされる。その開発者は変更を取り消し、どこがおかしいか突き止めなくてはならなくなる。 -- このように、発生してもすぐ見つけて簡単に取り除くことができるエラーであれば、寛大に扱ってもかまわないというわけだ。

結語

きちんとした自動化ビルドプロセスを確立することは、きちんとプロジェクトを制御する上で必須項目である -- 多くのソフトウェア・グル(指導者)がそう述べてきたが、まだまだ現場では、実現されているほうが珍しいという状況だ。

キモとなるのは、本当に「全て」を自動化して、かつプロセスを「頻繁に」行うことで、インテグレーションに関わるエラーを早期に見つけることである。そうすれば、誰にとっても、必要な変更を加えることが容易になる。「もし仮に自分がインテグレーションエラーを引き起こしたとしてもそれはすぐ見つけて直せるのだ」と自信が持てるからだ。このことの恩恵はあまりに強力だ。だから、いったん知ってしまったが最後、この恩恵を取り上げられそうな場面では、あなたはきっと、暴れたり、叫んだりするに違いない…


© Copyright Martin Fowler, all rights reserved

翻訳:小野 剛 $Id: Cont-j.html,v 1.4 2000/11/30 02:38:48 ono Exp $