同じコード管理のシステムで複数のチームがアジャイル開発を行っている時、お互いによるトラブルのリスクを抑制するにはどのようにすればいいでしょうか?各イテレーションの終わりにクリーンでリリース可能なバージョンをいつでも用意できているようにするにはどうすればいいでしょうか?このレポートでは複数のチームが動いているアジャイル環境において、どのようにバージョン管理を行えばいいかを説明します。このスキームは"Scrum and XP from the Trenches(InfoQのミニブック)(参考・日本語版「塹壕より Scrum と XP」)に出てきた企業で私たちが新しく採用した方法です。
このレポートはバージョン管理のエキスパートを第一の対象にしたものではなく、実際そのような人はこのレポートを読んでも新しい発見をすることはないかもしれません。それ以外の人たちで、シンプルかつ有効なコラボレーションの方法を知りたいと思っている人たちを対象にしています。アジャイルソフトウェア開発に関わる人であれば自分の役割に関わらずーなぜならブランチを作ったりマージしたりするのは構成管理者だけでなく、全員が関係することですからー興味を持ってくれるかもしれません。
(編集部注:InfoQ.comでは本稿のダウンロード版を用意しておりますが、InfoQ Japanでは用意しておりません。ご了承ください。)
目次
はじめに
このレポートでは複数のチームが動いているアジャイル環境において、どのようにバージョン管理を行えばいいかを説明します。なお読者は既にScrumやXP、タスクボードなどについての基本事項を知っていると想定しています。ここで説明するスキームは私が作り上げたものでなく、「メインラインモデル(mainline model)」あるいは「安定トランクパターン(stable trunk pattern)」を基礎としています。これらについての詳細は、本レポート最後にあるリファレンスの項に紹介している情報元を参照してください。
私がこのレポートを書いたのは、私がどこへ行ってもこのようなレポートを必要としているチームに出会うからです。ほとんどのチームは一度このモデルを理解すれば非常に気に入ってくれます。またこのスキームは"Scrum and XP from the Trenches"に出てきた企業で私たちが新しく採用した方法でもあります。このスキームは私たちがよりアジャイルなやり方でソフトウェアを開発してリリースするのを強力に支援してくれました。このモデルの説明を分かりやすい文章でレポートにすることで、私がまたホワイトボードを使って説明をする必要がなくなることを期待しています :o)
またこのパターンは数あるパターンの一つにすぎず、銀の弾丸ではないことも予め知っておいてください。このパターンを使うことを選択する時には、ここに書いてあることをご自分の状況に合わせて適用することになると思います。
目的
複数のチームが動いているアジャイル環境では、以下の目的を実現するバージョン管理モデルが必要になります。
- フェイルファースト
- フェイルファーストとはコードのコンフリクトや統合での問題を可能なかぎり早期に発見することです
- 大きな問題を数回のタイミングで修正するよりも、小さな問題を何度も修正していく方が賢明です
- 常にリリース可能
- どんなに悪いスプリント(イテレーション)だったとしても、その成果物は何かしらリリース可能なものでないといけません
- シンプル
- このスキームはチームのメンバ全員に毎日使われることになるので、ルールや定型作業は明確かつシンプルでないといけません
紙1枚にまとめた要約図(壁張り用)
この図を見て分からないことがあっても構いません。この先を読んでください。
この図を見て分からないことがなくても、この先を読んでください。
バージョン管理のパターン
ブランチのオーナとポリシー
以下のはシンプルなルールです。
ルール: 各ブランチ(トランクも含む)にはオーナとポリシーを決める
シンプルといってもこれが守られないと悲惨なことになります。ポリシーではそのブランチにどのような物をチェックインしていいかが記述されます。オーナはポリシーを決めて、そのとおりになっているかをチェックする責任を負う人物がなります。
“Done(済み)”の考え方
ユーザストーリはどの時点で“Done”になるのでしょうか?もっと細かく言えば、タスクボード上であるユーザストーリを“Done”の列に移す時、実際的にどのようなことを意味しているのでしょうか?
私は次のように考えています。
仮定: Doneの定義=“リリース可能“
つまりあるストーリがDoneしたとチームの誰かが言って、ストーリカードがDoneの列に移動されたなら、別の誰かが部屋に駆け込んで来て「よくできたな!それじゃあ今動かしてみよう!」と言ってきても「いや、ちょっと待ってくれ」と言う声が上がらないということです。
Doneの定義はどのようであっても構いません。しかしその定義が“リリース可能”を満たさないのなら、Doneに含まれてない事は何か、そしてその事は誰がいつするのか、もしDoneの後になって何か問題が起きたらどうなるか、ということを考慮する必要があります。
Doneブランチ
ストーリにはDoneとなった時に帰るべき場所が必要です。私のDoneの定義(=“リリース可能“)が意味することからいえば、そのストーリを製品へ組み込むのに必要なブランチがバージョン管理システム上にないといけないということになります。このブランチがDoneブランチです。
どのブランチがDoneブランチであっても構いません。このレポートではトランクをDoneブランチとして使います(初めはこの方法がいいでしょう)。これは「メインライン」(幹線)と呼ばれることもあります。
仮定: トランクがDoneブランチ
トランクポリシー
- いつでもリリース可能であること
- なるべく早くリリースしてほしいものであること
「いつでもリリース可能」はそのままの意味です。どの時点であっても、製品のオーナはトランクから新しいプロダクションリリース版を作るように指示することができるのです。
簡単な例をあげましょう。
下の青い線はトランクを表し、緑の丸はチェックインを表しています。このスプリントでは5回チェックインが行われていることになります。リリースは通常各スプリントの最後で行われるものですが、それでもトランクからのリリースはいつでも可能になっています。もしスプリントの終盤でチームのメンバが病気になり、ストーリNo.5が終えられなくてもリリースは可能です。もちろんリリースをしないでストーリNo.5を含めた次のスプリントへ移ることもでいきます。
「なるべく早くリリースしてほしい」というのは、ストーリを実行したいと思う(あるいは実行しても構わないと思う)まではそれをチェックインしないということです。上の例で、もしストーリNo.3がリリースしたくないものであれば、このブランチはもはやリリース可能でなく、最も原則的なブランチポリシーが破られたため、このブランチは潰されてしまったことになるのです。今のスプリントでリリースしたくないストーリは、別のブランチで作るべきでしょう。
ルール: 一つのブランチに異なるリリースを混ぜてはいけない
どのような時に新しくブランチをつくるか?
ブランチの追加はなるべく少なくしましょう。このような経験則があります。
勧告: 新しいブランチをつくるのは、チェックインしたいものがあり、かつブランチポリシーを破ることなく使用できる既存ブランチがない時のみ
作業ブランチ
とりあえずナイスでクリーンなトランクができ、いつでもリリースが可能になりました。
いえ、ちょっと考えてみましょう。リリース可能とは結合テストが行われたということです。つまり結合テストしないといけません。トランクにチェックインした時点ではリリース可能になってないといけないので、結合テストはチェックインの前にする必要があります。
それではきちんとコーディングしたと思っているコード、しかしトランクにチェックインする前にきちんとしていることを確かめないといけないコードはどこにチェックインすればいいでしょう?もちろんローカルマシンでテストはできますし、そのままトランクにチェックインすることもできます。しかしそれはちょっと恐ろしいことです。みなさんも「だけど私のマシンではきちんと動いたんですよ」というトラブルに巻き込まれたことがあるでしょう。
「よし、今日の分は終わったから帰ろう。でもどこにこのコードをチェックインしたらいいだろう?まだテストは終わってないからトランクにチェックインはできないけど、どこかにチェックインしてチームのみんなが続きをできるようにしておきたい」という問題もあります(アジャイルチームはコード共同所有者ですからね)。
チェックインしたいものがある、けれでもブランチポリシーを破ることなくチェックインできるブランチがない。これは上の勧告に従って新しいブランチをつくれる状況ですね。新しいブランチをつくりましょう。
このブランチを作業ブランチと呼び、全てのチームメンバが共有します。一部の人々には開発ブランチと呼ばれているものです。
チームAの作業ブランチポリシーを次のようにします。:
- このブランチのコードは、コンパイルとビルドができ、単体テストもパスする
素晴らしい、これで2つのブランチができました。安定ブランチ(トランク)と少し不安定なブランチ(チームAブランチ)です。チームAブランチは赤く色で表されていて、まだ安定ではない、つまりそのブランチにあるコードは単体テストをパスしたものの、結合テストはまだの可能性があり、リリースするほど安定してない可能性があるものです。とりあえず、作業の途中でチェックインできる場所がチームに与えられました。
それでは、ブランチの同期についてはどうでしょうか。それをこれから説明します。
作業ブランチからトランクへ公開する
ある時点であるストーリがDoneとなる(そうであってほしいものです)、つまりある時点で作業ブランチがリリース可能な状態になります。その時点でトランクへ移すことが可能になるので(またそうすべきでしょう)、作業ブランチにある新しいコードをトランクにコピーします。これを行ってトランクと作業ブランチが同等なものとなります。
私たちはこれを「公開(publishing)」と呼んでいます。その理由はリリースのためトランクへ「公開する」前に様々な事前作業を行うことから、公開というのがいいメタファになるからです。
例をあげましょう。今2つのストーリ「登録する(Register)」と「預金する(Deposit)」が実装されたとしましょう。この2つはDoneとなっている、つまり単体テストと結合テストが終わり、リリース可能になっています。「引き出す(Withdraw)」というストーリにも手をつけていますが、まだDoneとはなっていません。この時点のタスクボードは以下のようになります。:
ボード上の黄色い付箋はタスクで、ストーリを達成するために必要な一作業を表します。例えば、クラスXを作る、ビルドスクリプトを更新するといったことです。各タスクは通常1人日の作業を表し、また各ストーリは通常3人日から8人日かかる作業を表します。
このブランチの履歴は下のようになります。
まずチームはRegisterの実装に取りかかります。そして作業ブランチにチェックインし、結合テストを行い、バグを修正し、再びチェックインし、テストを繰り返します。そしてRegisterはDoneになりました。トランクに公開しましょう。
次の実装対象はDepositです。これは一度のチェックインだけで済みました。結合テストもパスしたので、これもトランクにリリースします。
今度はWithdrawの実装で、今チームはその半ばまで来ました。今のとこ2回のチェックインがされていますがDoneにはなっていません。
注意点としては、「トランクにリリースする」というのが、あるストーリに当たるコードをトランクにコピーすることではないことです。そうではなく、作業トランクから全てのコードをコピーする、つまり完全に同期させることなのです。
ここで2つの興味深い問題が出てきます。
- もしチームが複数のストーリを平行して実装していたらどうなるか?
- もし他のチームもトランクに公開しようとしていたらどうなるか?
まず一つずつ見ていきましょう。
もしチームが複数のストーリを平行して実装していたらどうなるか?
もしチームが一度に一つのストーリだけ実装しているなら、トランクへの公開になんの工夫もいりません。ストーリを実装し作業ブランチでテストを終えたらすぐに全部をトランクへコピーすればDoneとなります。
しかしチーム内で複数のストーリを同時に進行させていたらどうでしょう。RegisterがDoneとなった時にDepositが進行中だったら?
この時点でトランクへ同期を行うと、Depositストーリは不完全なのでリリース可能な状態とはなりません。これではトランクポリシーを破ってしまいます。
もちろんDepositが完了するまで待つこともできます。
では待ってみましょう・・・
さあDepositが出来上がりました!素晴らしい!ではトランクに・・・ちょっと待ってください・・・誰かがWithdrawを始めてしまっています!同じ問題が起きてしまいました!
この時にもしDepositのあるテストが失敗すると、それがDepositコードのせいなのか不完全なWithdrawコードのせいなのかを究明するのは困難です。2つのコードは同一トランク上にあるからです。
せっかく待ったのに役には立ちませんでした。これでは雪球を転がしているようなものです。将来全てのストーリが完成する時点(完成すればの話ですが)でビッグバンリリースをしようとしているのです。
これは極めて一般的な問題です。ではどうすればいいでしょうか?
対応策はいくつか挙げられます。
- そもそもそのような平行開発はしない。チームには一つのストーリにだけ専念させる。
- Registerが終わる前に誰かがDepositoに手を付けるなら、Registerが終わるまではDepositをチェックインしないようにする。あるいはブランチのお手玉が好きなら、Depositを別の一時ブランチにチェックインするようにする。
- Registerが終わる前に誰かがDepositoに手を付けるなら、変更してもブランチのリリース可能には影響しない安全なところから始める。例えば、Depositのために新しいコードと既存コードの変更の両方が必要なら、新しいコード(新しいメソッド、新しいクラス、新しいテストなど)を実装し、変更は行わない。もしDepositが新しいGUI要素を必要とするなら、それを表示しないようにして実装し、Registerが完成してトランクにリリースされたらDepositの残りの部分を実装する。
このためにうってつけのルールもあります。
- 最優先のストーリに取り組んでいる人が王様になる
- チームの他のメンバは全員が家来になる
- 王様になるためには、最優先ストーリに役立つことを見つける
- 王様が助けを求めたなら、家来はすぐにサービスしないといけない
- 家来は王様に逆らってはいけない
- 家来はリリース可能でないコードを作業ブランチにチェックインしてはいけない。王様は自分が望めば何でもチェックインして良い(もちろんブランチポリシーを破るようではいけませんよ)
- 最優先ストーリがDoneとなったら、次のストーリに取り組んでいた人が王様となる
チームにはいくつかの王冠が必要になりますね :o)
一般的に見て、複数のストーリを同時に実装することで得られるメリットは、多くのチームで過大評価されているようです。スピード感があるように感じるかもしれませんが、それは幻想かもしれません。なぜならそのことで最後のマージと結合、そしてテストにかかるリスクと時間が増大しているからです。
Scrumが小さなチーム(9人未満)を想定しているのはこのためです。それにより全員が緊密に協力して成果に集中することができます。もし全員が各自でストーリを進めていたら、協力関係はおそらく続かないでしょう。もちろん先を見越して次のストーリを準備し、そして実装にも少し手を付けるような人はいるでしょう。しかしどんな時であれ、チームの力全体としては最優先ストーリに集中がされているべきです。
複数のチームについては話が別です。複数のチームは複数のストーリを平行して実装したい時に特に編成されるものです。複数チームでの作業については後で触れます。まずはリグレッションテストとチーム内でのコード相違について説明します。
Doneにはリグレッションテストも含まれる!
ストーリがDoneとなった時、ストーリをDoneの列に移し、作業ブランチからトランクへコピーをします。トランクは常にリリース可能でないといけません。ここにはある重要な暗黙の了解があります。
ルール: トランクに触った者は、それ以前の全ての機能も含め、トランク全体がリリース可能なままであることを保障する責任を負う
このルールが実際に意味していることは、ストーリAのテストでは、それまでに実装されたストーリに関連するリグレッションテストを全て実行することを含めるということです。ストーリAのコードがそれ以前のストーリを壊してしまったら、そのストーリが動作するとは言えないのです。
ちょっと待てください。本当にそれはリリース不可能ということなのですか?全てのリグレッションテストを毎回ストーリが完了する度に実行しないといけないのですか?
そうですね、まず私が言ったのは、関連する全てのリグレッションテストということです。基本となるトランクはクリーンでリリース可能なものでしたね。そして今私たちはたった一つのストーリを追加しようとしているだけです。これはちょっとした追加にすぎません。もしリグレッションテストが自動化されていれば全てのリグレッションテストを実行できるでしょう。しかし何かしら手作業で実行するリグレッションテストがあるなら何を実行するか上手く選択する必要があります。
これはリスクとコストのトレードオフの問題です。手作業で行うリグレッションテストのそれぞれについてテストのコスト(つまりどれだけそのテストをするのに作業しないといけないか)と重大な欠陥が見つかる可能性とを評価しないといけません。もちろんそのテストを自動化するするコストも検討しますよ :o)
コードの相違(マージコンフリクト)
私があるウィジットクラスを呼び出すコードを嬉々として書いていたとします。しかし1時間前にリファクタリングが行われた時に同じチームのジムがそのウィジットクラスを取り除いてしまったことを私は知りません。ここでコードの相違が起きます。私としてはそのウィジットクラスを呼び出すコードをさらに書いてしまう前にできる限り早くこのことに気付く必要があります。
ルール: 作業ブランチと自分のコードの同期は常に行う(=可能な限り頻繁に)
この場合の同期には2つの方向性があります。作業ブランチから最新版を落としてマージするということと、自分のコードをチェックインするということです。前者のことは「キャッチアップ」(=他の人が何をチェックインしたか知ろう)と呼ばれ、後者のことは「公開」(=他のチームメンバも自分の変更を利用できるようにしよう)と呼ばれます。.
一時間に一回同期を行うのは良い習慣です。そして作業対象をあるタスクから他のタスクに切り替えた時、またコーディングをしていない時には基本的に同期するようにしましょう。これは「誰か他の人が自分のコードとコンフリクトを起こすようなコードを書いてないかなるべく早く知りたい」というためだけではなく「自分が他の人のコードとコンフリクトを起こすようなコードを書いてないかなるべく早く確認してもらいたい」ということでもあるのです。ただし作業ブランチのポリシー(単体テストはパスしている、など)は破らないようにしないといけません。
このルールは当然のことに思われるかもしれませんが、どうかきちんと覚えておいてください。複数チームを対象とする時にこの考え方をより細かくして再利用するので、今の段階で強調しておきたかったのです。
複数チーム-もし他のチームもトランクに公開しようとしていたらどうなるか?
今度はチームAとチームBがあったとしましょう。この2つのチームはフライト予約システムを作ろうとしています。チームAは予約プロセスにフォーカスしていて、チームBは間接事務プロセスにフォーカスしています。
今この2つのチームが、あるスプリントを開始しようとしていたとします。それぞれのチームには2つのユーザストーリがあります(一つのスプリントには通常それ以上のストーリがあります)。
それぞれのチームでトランクに公開する前にテストを行う必要があるので、それぞれで作業ブランチを持ちます。
ここで興味深い問題が表れます。私がチームAにいたとします。チームAには自分たち用の作業ブランチがありますが、そこに何の変更がなくてもトランクに変更が生じる可能性があります。なぜならもう一つ別のチームがそこにはいて、あるストーリを終えた時点で向こうはトランクへの公開をするはずだからです。
そのため、どの時点であってもトランクに私の知らない新しいコードができている可能性があるのです。そしてそのコードが私のコードとコンフリクトする可能性もあるのです(そんなことあってはならないのに!)。チームBの誰かが私が使っているウィジットクラスの名前を変更してしまったかもしれません、それに・・・あれ、このことは今話したばかりでしたね。
そうです、これは同じ問題なのです。そして対応策も同じです。ただしスケールが少しだけ違います。
ルール: 毎日トランクを自分たちの作業ブランチへマージする
毎日仕事に取り掛かる際に、チームの誰かが最新版をトランクからチームの作業ブランチにマージするようにします(=トランクに加えられた変更に「キャッチアップ」する)。
私のチーム(チームA)はコンフリクトを見つけ、ただちに解決します。これは最優先事項です!もしチームB(あるいは私たちのコードとコンフリクトを起こすようなコードを書いた人)の助けが必要なら、その人(人たち)を捕まえて一緒に解決するのです。重要なのは問題を解決するのは私たちのチームで、解決のための作業を私たちの作業ブランチ上で行う(トランクではなく)ことです。
ルール: 一番安定してないブランチでコンフリクトを解決すること
もちろんトランクへの公開が規則どおり行われなければ、トランクからのマージは時間の無駄になるだけです。チームAとチームBとの相違は、誰かがトランクへ公開しないと表れないのです。そこで次のルールを設けます。
ルール: 作業ブランチからトランクへのマージは規則どおり行うこと。例えばストーリが完了するごとに行うべきマージを、スプリントの終わりまで待っていてはいけない。
おもしろい副作用も起きます。
副作用: 最初にチェックインしたもの勝ち!
もし2つのチームがお互いにコンフリクトを起こすようなコードを書いていたとしたら、最後にチェックインしたチームがコンフリクトを解決しないといけません。これはなかなか良い副作用で、チームが素早くチェックインするように仕向けます :o)
例題のスプリント全体ではこのようになります。
2つのチームが6日間のスプリントを行っています。チームAではBook(予約)とCancel(取消)の実装を計画していて、チームBはInvoice(請求)とBlacklist(ブラックリスト)の実装を計画しています。何が起きたか追ってみましょう。
Day | チームAの観点 | チームBの観点 | トランクの観点 |
1 | トランクからマージする。新しいものはなし。Bookに取り掛かる。自分たちの作業ブランチにチェックインする。 | トランクからマージする。新しいものはなし。Invoiceに取り掛かる。自分たちの作業ブランチにチェックインする。 | 今日は何も起きず。 |
2 | トランクからマージする。新しいものはなし。Bookの実装完了。結合テストを行う。完了。おランクにコピーする。Cancelに取り掛かる。 | 昨日と同じ。 | Bookが完了! |
3 | トランクからマージする。新しいものはなし。引き続きCancelに取り組む。 | トランクからマージする。おっと!変更発見!Bookが追加されている。チームBのブランチのコードにマージする。コンフリクトは解決する。それからInvoiceに引き続き取り組む。 | 今日は何も起きず。 |
4 | 昨日と同じ。 | トランクからマージする。新しいものはなし。Invoice完了。結合テストを行う(Bookも含めた状態で!)トランクにコピーする。Blacklistに取り掛かる。 | Invoice完了! |
5 | トランクからマージする。おっと!変更発見!Invoiceのコードが追加されている!チームAのブランチのコードにマージ、 コンフリクトは解決する。それからCancelに引き続き取り組む。 | トランクからマージする。新しいものはなし。 引き続きBlacklistに取り組む。 | 今日は何も起きず。 |
6 | トランクからマージする。新しいものはなし。Cancel完了、トランクにコピーする。 | 昨日と同じ。 | Cancel完了! |
スプリントが終わりました。Blacklistのストーリだけ完了してませんが大丈夫です。それでもリリースできるのですから。逐一マージして統合していったおかげです。もしスプリントの終わりまでマージをしなければ、コードの相違は都合の悪い時(修正する時間がない時)に発見されることになるでしょう。
リリースブランチ
スプリント1が終わりバージョン1.0.0のシステムがリリースされたとしましょう。そしてスプリント2の半ばまでやってきたところで、リリースバージョンに重大な欠陥が見つかったと報告が入りました。なんてことでしょう!さあこれからどうすればいいでしょうか?
一番シンプルなやり方はトランクを修正してバージョン1.1.0をリリースすることです。そうするとスプリント2の現段階で実装されている全てのストーリが新しいリリースに含まれることになります。トランクはDoneブランチであり、Doneはリリース可能を意味するので、理論上は問題ありません。トランクに何があったとしても、トランクはいつでもリリースすべきものであるべきなのです。
しかし今はまだ新しいストーリをリリースしたくない理由があるかもしれません。例えば以下のような理由です。:
- 重大な欠陥があったということは、リリース時にトランクが間違っていたということだ。それはスプリント2のストーリが間違った基礎の上に作られているということだから、新しいストーリを加える前の状態を修正する必要がある。
- 関係者はスプリントの途中で新しい機能をリリースすることは望んでいない。
- トランクから新しい機能を加えた新しいリリース版をビルドするのには少し時間がかかる。だからシンプルな“ホットフィックス”の仕組みを使って取り急ぎバグへの対応をすべきだ。
ではどうすればいいでしょう?
- バージョン1.0.0がリリースされた時点のトランクを元にリリース1.0という名前のリリースブランチを作る。
- リリースブランチ上の欠陥を修正する。
- リリースブランチでリリースされたらすぐ、リリースブランチからトランクへマージを行う(そして将来のリリースではこの修正が含まれることになる)。
注意点として、バージョン1.0.0をリリースした時点でリリース1.0ブランチを作る必要はありません。欠陥が現れて、そのブランチに対して対処をしなければいけなくなった時に始めてリリースブランチを作ればいいのです。
全体像
このパターンをどのように用いるか具体的な例を使って説明してきたので、今度は少し後ろに下がって全体像を眺めてみましょう。
メインラインモデルでは、ブランチはコードラインと呼ばれます(正確にはブランチはコードラインの実装と見なされます)。あるいはストリームと呼ぶ人もいます。
コードラインの親(つまりコードラインが生成された時の生成元)はベースラインと呼ばれます。そしてメインラインはベースラインを持たないコードラインとなっています。
上の例についてまとめるとこうなります。
- トランクはメインラインで、親はありません(そうですよね?)
- 他の全てのコードライン(リリース1.0ブランチ、チームA作業ブランチ、チームB作業ブランチ)はトランクをベースラインとしている。
下のはもう少し複雑な例を表しています。
この図が意味しているのはこうです。:
- プロジェクトXのコードラインはメインラインから派生したもので、このプロジェクトは完了して今はブランチがクローズしている。
- チームAはメインラインから派生した作業ブランチを持ち、それはまだアクティブである。
- チームAは進行中のスパイク(突出しているが後で消えるものというニュアンスがある)を持ち、それは作業ブランチから派生したものである。
- リリース2.3ブランチはクローズしていて、バージョン2.3のシステムは稼動してなく、メンテナンスもされない。
各コードラインにはベースラインを基準にした堅固レベルというのがあり、コードラインはベースラインより堅いか堅くない(柔らかい)のどちらかになります。
- 堅いコードラインは安定していて、十分テストがされ、変更されることはほとんどなく、リリース間近のもの
- 柔らかいコードラインは不安定で、テストがほとんどされてなく、変更もしばしばで、リリースには程遠いもの
堅いコードラインのブランチは上向きに、柔らかいコードラインのブランチは下向きに描きます。よって上の図からは次のことが分かります。:
- リリース2.3ブランチはメインラインより堅固である
- チームAの作業ブランチはメインラインより柔弱である
- チームAのスパイクはチームAの作業ブランチより柔弱である
上で示した描き方はブランチの履歴を図示するのには便利ですが、ブランチがある場合にはちょっと大変なことになります。下の図はよりシンプルな描き方で、現在存在しているコードについてだけ表示し、それらがどこから派生したのかを示します。
みなさんもブランチをこの図で表してチームの部屋の壁に貼ってみてはいかがでしょうか。統合問題について議論するときその図を見るのにはとても役に立ちます。
ここでの重要なルールは、全ての変更はつながっている線に沿って行うということです。ですので、チームAの作業ブランチにあるコードをチームBの作業ブランチに直接マージしてはいけません。もしそのようなことをすればあらゆる混乱を引き起こします。チームAの作業ブランチはメインラインに統合されてからチームBの作業ブランチに反映されないといけません。
メインラインより上にあるものは全てリリースコードラインと呼ばれ、これはメインラインより堅固なコードラインを意味します。
メインラインより下にあるものは全て開発コードライン(または作業コードライン)と呼ばれ、これはメインラインより柔弱なコードラインを意味します。
コラボレーションの黄金ルール:
- 安定のための変更を常に受け入れること
- 不安定の原因になる変更を持ち込まないこと
この言葉の違いはコードライン的には何を意味するでしょうか?
上の図は以下のことを色付けして表したものです。
- リリースコードラインで変更があった時は、直ちにベースラインへマージしていき、最終的にメインラインまでマージすること。
- 例: あるバグがR.2.4.2で修正された。それは直ちにR2.4へマージされ、さらにメインラインまでマージされないといけない。
- リリースコードラインはベースラインからの変更を受け入れてはいけない。
- 例: メインラインに新しいコードがチェックインされても、それをR2.3やR.24に反映させてはいけない。
- ベースラインの変更は常に開発コードラインに反映する。
- 例: メインラインに対して行われた変更は何であれ、直ちにTeam AおよびTeam Bへ、さらにTeam AからはA Spikeにマージされないといけない。
- 開発コードラインの変更は安定した時にのみベースラインへ反映する。
- 例: Team Bのストーリは、それが完了してテストも済んだ時のみメインラインに反映させる。
あるコードラインおよびそのベースラインの両方に変更があった時には、それに応じたマージ作業が必要になります。コードマージは下手をするとエラーの原因となりかねない作業のため、このような場合には柔らかいコードラインの方から行います。そのマージを終えて確認作業が済んでから、堅い方のコードラインへとマージを行います。
私たちは柔らかいコードラインの方へ先にマージすることをこのようにルールなルールとしてシンプルに表しています。:
ルール: マージダウンしてからコピーアップする
例: チームAはメインラインが更新されたことに気付いた。チームAではその変更を開発ブランチへマージダウンし、コンフリクトを全て直した。チームAの開発ブランチが安定したので、今度はメインラインの方へコピーアップした。当然ながらチームAはメインラインに他の変更が加えられていないことを確認してからコピーアップした。
バリエーション
“バージョン管理のパターン”の項ではメインラインモデルを実際にどのように使うかを例示しました。
“全体像”の項ではより一般的な観点からメインラインモデルを説明しました。
ここではこのパターンを実際に使うにあたって用いられる典型的なバリエーションを紹介します。
Doneの定義は必ずしも「リリース可能」でなくてもいい
Doneの定義をどのようにするのであっても、その定義に従ってDoneと言えるようなストーリを集めたブランチを設けるようにしましょう。その場合でもDoneについての重要な事項を忘れないでください。もしDoneが結合テストを済ませたという意味がなければ、一体いつ結合テストがおこなわれるでしょうか?
トランクがメインラインである必要はない
メインラインはこのパターンのとおり使えるものでないといけませんが、トランクである必要はありません(そうはいってもほとんどの場合トランクは自然な選択となるでしょう)。
チーム専用のブランチを持たなくてもいい
同じブランチを複数のチームで共有すること、あるいはメインラインへ直接チェックインすることも可能です。しかしブランチのポリシーは守られるようにしましょう。
それでもチーム間の干渉を起こしうる不完全なストーリに関わらないようにチーム専用のブランチを持つことが多いです。
毎回新しいリリースブランチを作らなくてもいい
あるスプリント後に新しくリリースブランチを作らないで、前のと同じリリースブランチを直接使うと決めることもできます。このようなリリースブランチは「現行」、あるいはそれに類した名で呼ばれます。一度に二つ以上のプロダクションバージョンを持たないことは確かに有益です。
スプリントごとにリリースする必要はない
各ストーリが終わったあとでリリースしたり、3つのスプリントが終わるごとにリリースすることも可能です。自分たちに合ったペースを見つけましょう。
各ストーリごとにリグレッションテストを行う必要はない
リグレッションテストや結合テストを行うのがかなり難しい状況であるなら、スプリントの終わりまでそれらを行わないでいて、2,3のストーリをまとめてテストしたり、統合したりすることも可能です。ただし、リグレッションテストや結合テストがDoneの定義に含まれるなら、スプリントの終わりに問題があった時、そのスプリントでDoneとなったストーリが0になるリスクはあります。
FAQ
継続的インテグレーション(CI)はこのモデルのどこにあてはまりますか?
CIサーバ(変更を検知してビルドなどを自動的に行うサーバ)が対象とするのはどのブランチでしょうか?これは状況によることなのですが、初めての場合には以下のようにするといいでしょう。
トランクのポリシーを「Doneかつリリース可能」、「単体テストをパスする」を作業ブランチのポリシーとします。
- CIサーバは各作業ブランチを自動的かつ継続的にチェックし、ビルドを行い、全ての単体テストを行う。
- テストが失敗したら警告画面をスクリーンに映し出し、発煙装置を作動させる。
- CIサーバは各作業ブランチに対して自動的かつ(継続的に行ってないなら)定期的に結合テストおよびリグレッションテストを行う。
- これらのテストでエラーがあれば、それはブランチポリシーに反するので、CIサーバは警告を表示する。
- 作業ブランチからトランクにコードを公開しようとしている時に実装したストーリがちゃんとDoneであるかをチェックする一つの手段として、これらのテストを手動で起動する。
- CIサーバはトランクに対して自動的かつ継続的に結合テストおよびリグレッションテストを行う。
- テストが失敗したら警告画面をスクリーンに映し出し、発煙装置を作動させ、サイレンを鳴らし、USBロケットミサイルを発射し、州兵を呼び出す。
このバージョン管理モデルに最も適したツールはなんでしょう?
私にも分かりません。ただPerforceはなかなか使えますし、subversionもいいと思います。CVSについてはあまり分かりません。どんな情報でもお知らせください。
しかし一つ重要なことがあります。それは、コラボレーションが効果的に行われないことのコストに比べればツールを変えるコストは大抵かなり低いということです。なので、どのように作業をしたいかを考えてから、それを支援するのに適したツールを探すようにしましょう。
ユーザストーリに関係のないチェックインについてはどうでしょう?
全てのコード変更がユーザストーリと関係するわけではありません。このレポートの例では話を分かりやすくするためにそうしただけです。このモデルはどんなチェックインコード(あるいはコードでないドキュメントでも)に対しても適用可能です。
マージは骨が折れるから、なるべくしたくない!
どうやら仕事をするのがお嫌いなようですね。それは根拠のないマージ恐怖症です。マージが骨折りなのは、なるべくしないようにしているからです。もっとマージするようにすれば苦労は軽くなりますよ :o)
もっと質問があります!
これから挙げるリファレンスを参照してください。ほとんどの質問に対しての答えになってくれるのは間違いありません。
リファレンス
もしもっと知りたいのであれば、以下のリソースを強くおすすめします。
Practical Perforce(Perfoceはバージョン管理システム製品)
Laura Wingerd氏の本です。ここにあるサンプル(PDF・英語)でメインラインに関するほとんどのことをカバーしてくれるでしょう。
これはPerforceの本ですが、メインラインモデルはPerforceに固有のものであるわけではありません。
High level best practices in Software Configuration Management
バージョン管理に関する一般的なベストプラクティスを要約したもの(source)で非常に役に立つ本です。
筆者Laura Wingerd氏とChristopher Seiwald氏です。
Branching and merging - an agile perspective
Robert Cowham氏による興味深い記事(source)です。
The Flow of Change
メインラインモデルを良く要約した本で、これもLaura Wingerd氏によります。
このレポートの”全体像”の項のほとんどはこのスライド(PDF・英語)を元にしています。
筆者について
Henrik Kniberg氏(source)はストックホルムのCrisp社(サイト・英語)のコンサルタントで、Javaやアジャイルソフトウェア開発のスペシャリストです。これまでスウェーデンで幾つかのソフトウェア企業を設立し、ソフトウェア開発のテクニックを学んだり教えたり生かしたりすることに情熱を燃やしています。氏は総体的なアプローチを用い、マネージャや開発者、スクラムマスタ、指導者、コーチといった役に着くのを楽しんでいます。また氏は人気のあるInfoQミニブック「Scrum and XP from the Trenches: How We Do Scrum」(参考記事・日本語版「塹壕より Scrum と XP」)の著者でもあります。
原文はこちらです:http://www.infoq.com/articles/agile-version-control