Facebook のエンジニアが git のメーリングリストに投げたメール[1]が話題になっていた。大体こんな内容だ。
Facebook ではgitを広く使っているが、リポジトリがでかくなるにつれて性能が問題になってきた。今後も git を使い続けるべきか検討するために、テスト用の超巨大リポジトリを作って性能評価をしてみたところ、遅すぎるという結果がでた。性能を上げるにはどうしたらいいだろうか?
これに対して、リポジトリを分割すれば?という反応が寄せられたが、元のエンジニアは、以下のような感じに答えている。
リポジトリを分割するという試みはすでに始めている。が、これは長期的なプロジェクトだ。我々のコードは複雑に絡み合っている。ひとつの巨大なリポジトリを共有することでたくさんのメリットがあるから、無闇に分割するのはよくない。そもそも、バージョンコントロールシステムの性能上の制約のせいで、コードをどう構成するかが決まるなんておかしな話だろう。
これはおもしろい問題だ。性能上の問題を解決するのは簡単そうじゃないし、かといってリポジトリを分割するのも難しい。どうなるのか、今後の展開が気になる。
というのは前置きで、私は巨大リポジトリのファンだ。巨大レポジトリにはいろいろメリットがあるが、一番大きいのは「呼び出し側のコードをまとめて変更できる」ことだと思う。これは YAGNI と premature complication[2] の中で少しだけ触れたテーマだ。
よく言われるようにAPIの設計というのは難しい。最初からすべての使われ方を想定することなんかできないし、使われ始めてから判明する問題も多い。いくら時間をかけて考えたって、最初から完璧なAPIを設計するなんて無理なのだ。とりあえず作ってみて、使い始めてから徐々に改善していく、という方が現実的でスピードも早い。何しろ、延々とAPIを議論をする代わりに、さっさと製品に機能を追加してリリースできるのだ。
これをするために重要なのが「呼び出し側のコードをまとめて変更できる」ことだ。関数の名前がひどい?じゃあ呼び出し側も含めて全部直そう。パラメータを一つ足したい?追加すればいいじゃないの。巨大リポジトリだと、こういう変更がアトミックに1つのパッチでできるのだ。あとで直せるからって最初から手抜きしすぎてはいけないが、あとで直せるのは心強い。
もしこれが細かく分割されたリポジトリだったらそうはいかない。関数の名前がひどい?じゃあ新しい名前の関数を作って、呼び出し側をそっちを使うように変更して。。えーと、この関数、10個の別々のリポジトリからも使われているんですけど、これ10個パッチ作って直すの面倒くさいな。そこまでしてやる価値ないよね?各リポジトリが個別のリリースサイクルを持っていたりすると、話は更にややこしい。こんな具合で古いAPIは残り続ける。技術的負債だ。
巨大リポジトリのもうひとつのメリットはコードの共有がやりやすいということだ。スレッド間の排他処理やら、イベントループやら、正しく実装するのが非常に難しいという類のコードがある。こういうのは、できるやつが実装して、みんなでコモンズとして共有したい。ここでまた重要なのが「呼び出し側のコードをまとめて変更できる」ことだ。新しい要求やアイディアに応じてコモンズを育てていくためには、呼び出し側のコードを変更できると都合がいい。
一方、リポジトリが細かく分割されていると、コモンズは育たず、コードのサイロ化が進む傾向がある。みんなで共有するコードのためのリポジトリを作ったとしても、うまくいかない可能性が高い。何しろ、別のリポジトリにコードを入れるのは面倒なので、つい自分のところのリポジトリに入れてしまうのだ。それに、「呼び出し側のコードをまとめて変更できる」力がなければ、共有リポジトリに入れたコードを健全に育てるのは難しい。
というわけで、巨大リポジトリというのは、なかなかよいものだ。もちろんデメリットもたくさんあって、それを解決するためのコストはかなりでかい。一概にどちらがいいとはいえないが、個人的には巨大リポジトリのやり方が気に入っている。
[1] http://thread.gmane.org/gmane.comp.version-control.git/189776
[2] 4.html