- More Feature Branching Means Less Continuous Integration
つい最近、たまチームでは Git Flow を利用したブランチ管理に移行したばかりだったので、この記事を見かけた時は一瞬ムムッとなったが、実際に読んでみて、なるほどと思える反面、どのようなブランチ管理を導入するかはそのプロジェクトの性格に依る部分も大きいのではないかと感じた。というわけで、今回はブランチ管理とアジャイルの関係、そしてその理想からはやや距離を取ることになったたまチームの現状についてまとめてみたい。
Git Flow による Integration Feature Branching
以下は、Steve Smith氏によるブランチ管理方法の4分類:
これまでたまチームは、Gitのブランチ管理に関しては何も方針を決めておらず(単純に master に push されたものがステージング・本番、双方へのデプロイ可能なバージョンとしてビルドされる)、結果的に Trunk Based Development という、今回の記事で推薦されている、ブランチを極力作らないシンプルな開発モデルになっていたのだが、後述する事情で以下のような Vincent Driessen氏提案のブランチ管理に移行した。
- A successful Git branching model » nvie.com
このブランチモデルについては、Driessen氏による図を見てもらうのが一番分かりやすい。
基本のブランチとして master と develop の二つを用意し、developが開発用の基本ブランチ、masterが本番用のブランチとなる。開発の基本的なフローは、「フィーチャーの開発」と「リリース」の二つが軸となり、それぞれ、
- フィーチャー
- フィーチャーブランチを作って、終わったら develop にマージ
- リリース
- リリースブランチを作って、終わったら master と develop の双方にマージ
- masterにマージしたところで、リリースバージョンのタグを付けておく
これらのフローにまつわる git の操作を簡単にしてくれるのが、git-flow というgitコマンドの拡張である。以下のようなコマンドを打つだけで、ブランチの作成から移動・マージまで全部自動でやってくれる。
$ git flow feature start feature_name ... (code and commit) $ git flow feature finish feature_name
Smith氏は、この Git Flow によるブランチ管理を Integration Feature Branching と呼び、継続的インテグレーションを実現するためには全くお勧め出来ないとしている。
フィーチャーブランチと継続的インテグレーション
フィーチャーブランチと継続的インテグレーションの関係については、2009年に書かれた Martin Fowler氏の記事でより詳細に説明されている。
- FeatureBranch
今回の InfoQ や Fowler氏の記事で説明されているフィーチャーブランチの問題点を一言で言えば、共有ブランチに対する変更の粒度が大きくなるため、マージの際のトラブルが起こりやすくなる、ということになるだろうか。
フィーチャーブランチのメリットは、共有のブランチに影響を与えることなく個々の機能を独立して開発出来ることである。一つ一つの機能がブランチとして分かれることによって、リリース(マージ)する機能をその場で選択する cherry-picking のような運用が可能になる。しかし、独立しているが故に、個々の機能の間のコミュニケーションが不十分になり、いざ共有ブランチでマージしようとした時に、解決の難しい衝突を引き起こす可能性があるとFowler氏は指摘する。
衝突のリスクは開発者に心理的な影響を及ぼす。例えば、自分のフィーチャーブランチで行った変更が、他人のフィーチャーブランチに対して意図せぬ影響を及ぼすのが怖くなり、大胆なリファクタリングがやりづらくなる。これは一つ一つのフィーチャーブランチが大きくなればなるほど顕著になるだろう。
継続的インテグレーションのそもそもの目的は、プログラム全体を頻繁に結合することによって、結合に関する問題の粒度を小さくしようということであった。つまり、フィーチャーブランチの考え方は、そもそも継続的インテグレーションと相容れないということになる。
インクリメンタルな開発を実現するためのコスト
継続的インテグレーションの理想は、全てのコミットがリリース可能な単位になることである。その理想状態の元では、当然のことながらブランチを分けて開発する必要は無くなる。しかし、そのようなインクリメンタル開発を実現するコストは決して低くない。例えば、一つの機能の開発が一つのコミットで完了することはむしろ稀だろう。一つのコミットで終わらない場合、その中途半端に実装された機能を抱えたシステムをどのようにリリースすれば良いのか? その問題に対処するために提案されているのが FeatureToggle や BranchByAbstraction と言ったテクニックである。
上のようなテクニックがあったとしても、全てのコミットをリリース可能にするためには、Steve Smith氏も認めているように、かなりの訓練と経験を必要とする。これは以前の記事でも書いたように、システムをそれぞれ完結した vertical slice 単位で開発しなければならないアジャイルの本質的な難しさである。
たまチームの場合
理想的な継続的インテグレーション、さらにはその到達点である継続的デリバリーを実現するためには、いつでもカジュアルに本番を更新できるような体制と環境を整えておかなければならない。それは技術的な面だけではなく、文化的な面でも高いハードルをクリアしておく必要があり、残念ながらたまチームの場合は、まだそこまでのレベルに達しているとは言い難い。カジュアルに本番を更新出来ない場合、つまり本番更新の頻度が比較的少ない場合、ステージングと本番でビルド(ブランチ)を共有しているといろいろと不便なところが出てくる。例えば、どのビルドが本番に上がっているかが分かりづらくなったり、本番に対する Hotfix がやりづらくなったりする。そういった問題に対処するためにステージングと本番でブランチを分けておいた方が良いのではないかという話になり、Git Flow によるブランチ管理を採用するに至った。
フィーチャーブランチについては、現状だと一つのリポジトリは基本的に一人の開発者が担当していることが多いので、それほどこだわらずに開発者個人の裁量に任せる感じで問題ないように思える。しかし、これが複数人で共同開発するようになった場合は、フィーチャーブランチの扱いには十分気をつけなければならないだろう。