http://martinfowler.com/bliki/SemanticConflict.html

私が同僚とフィーチャーブランチについて話しているのを聴いた人は、我々があのパターンの大ファンではないということを知っている。 我々の反論の重要な点は、ブランチ作成は簡単だが、マージは大変だという見解にある。 時折耳にする議論としては、モダンなバージョン管理ツールがあれば、 マージは十分に簡単になるので、フィーチャーブランチには行う価値があるというものがある。

確かに、モダンなツールは、私が若い頃よりもマージに関してずっといい仕事をしてくれる。その威力のいい例の一つが、リネームしてもマージしてくれるというものである。 私がlorem.rbの中身の何かを変更した一方で、Jezがファイル名をipsum.rbに変更したような状況を適切にマージすることができるのだ。

それは全くもって結構なことではあっても、あくまでもテキストレベルの衝突を解決してくれるだけであり、意味的な衝突には役に立たない。 ここで意味的な衝突と言っているのは、Jezと私が、テキストレベルでは安全にマージできるが、プログラムが以前と別な振る舞いをしてしまうような変更を加えたような場合のことを指している。

最も簡単な例としては、関数をリネームするような場合が挙げられる。 例えば、私が、clcBlというメソッドはcalculateBillという名前にした方が取り扱いやすいんじゃないかと考えたとしよう。 モダンなリファクタリングツールではこんなものは朝飯前だ。 単にShift+F6を押し、新しい名前をタイプすれば、ツールがすべての呼び出し元を変更してくれる。 けれども、問題は起こるのだ。もしJezが自分のフィーチャーブランチで、このメソッドへの呼び出しを追加したならどうなるだろう。 2つがマージされると、テキストレベルのマージはうまくいくだろうが、プログラムは同じようには動かないだろう。

ブランチの一方で関数をリネームすると、もう一方でリネーム前の名前で追加した関数呼び出しはマージ後にエラーになる

メソッドのリネームは単純な例だし、 静的型付けを行う言語ではコンパイルに失敗するので、 発見が簡単でもある。 でも、このようにきれいにはマージできないような、 もっと微妙な意味的衝突はいくらでもある。 仮に、私がそのcalculateBillメソッドを見ていて、 このメソッドは明細を計算する(calculating the bill)だけではなくて、 同時に会計科目を会計システムに送付しているということに気づいたと想像してみよう。 副作用は好まないので、 私はその部分を別個のnotifyAccountingメソッドとしてくくりだす。 そうして、calculateBillを呼んでいる箇所をすべて探し出して、 notifyAccountingへの呼び出しを追加する。 けれども、Jezは自分のブランチの中ではそれに気づいていないのだ。

つまり、ここでの最初のポイントは、ツールがどれだけ優秀だとしても、 それは自分をテキストレベルの衝突から守ってくれるに過ぎないのだということだ1。 特にいやな点は、意味的な衝突は、見つけ出すのも直すのもより難しいということだ。

意味的な衝突は、自動的に解決することはできない。 もしかすると、いつの日か、 ツールが衝突のうちのいくつかには対処することができるようになるかもしれない。 けれども、私は、いやらしい複雑な衝突については我々とともにありつづけるのではないかと疑っているー少なくとも、コンピュータが我々の心を読み取って、自動的に意図を推測してくれるようになるまでは。とはいえ、そうした衝突に取り組むにあたり、大いに役立つ戦略がいくつかある。

その最初のひとつが、自己テストコードだ。テストは、我々のコードについて、テストから見た視点でのコードのあるべき意味と、実際のコードとの間に、整合性がとれているかを有効にチェックしてくれる。 もしJezが、自分が呼んでいるコードで、何か特定のことが起こることを期待しており、なおかつそれに関するテストを作っていたなら、彼がインテグレーションを行ったタイミングでそのテストが失敗するだろう。もちろん、これは完璧な回答にはなっていない。 テストが完璧になることはない。そうはいっても、 テストは実際のところ、多くの意味的な問題を発見してくれる。 また、テストは衝突を見つけたからといってそれを直してくれるわけでもないけれど、衝突を見つけるということが闘いの大きな部分を占めるのだ。

役に立つもうひとつのテクニックは、もっと頻繁にマージをする、というものだ。 もしJezが私の変更を数日後でなく、数時間後に発見したなら、そのほうが ずっと困難は軽減されるだろう。そうすれば、もう古い動作をしている大量のコードをビルドすることもない。 だから、我々はこれほどにも継続的インテグレーションの大ファンなのだ。

ツールがフィーチャーブランチを我慢に耐えうるものにしてくれる、という考え方を喧伝する人たちには、2つのグループがあるようだ。その1つは、 「エンタープライズグレードの」バージョン管理システムの御用商人らだ。 ただ、彼らのことはどうでもよい。 もう1つのグループは、DVCS(分散型バージョン管理システム)のファンらだ。 どちらかというとこのグループのほうを私はいささか心配している。 よく、人々は、 いかにフィーチャーブランチを簡単に作れるかということでもって、DVCSの正当化を試みている。 しかしそれは、意味的衝突の件を見落としている2。 DVCSを使うにあたってのよい理由というものはいくらでもあるのだから、 何も、そういうよいツールを問題のあるテクニックと抱き合わせることはない。

(訳: ooharak)

  1. それに、もし我々が全く同一のテキストに変更を加えたら、マージツールはgit-rerereのようなものを使わない限りそのどちらも救ってくれないのが普通だ。ただし、それは意味的な衝突に比べたらほんの小さな問題だ。

  2. もし機能が数日内で簡単に作れたなら、意味的衝突に出会うことは少なくなる(そして一日未満なのなら、結局CIと同じことだ)。けれども、そんなに短いフィーチャーブランチというものは、それほど見かけるものでもない。