二カ月間ブランチを利用した開発フローを実践して感じたメリットとデメリット
本記事は当初SVNとGitの比較として「ブランチを用いた開発フロー」のメリット・デメリットについて記載していましたが、 「SVNでもブランチを利用できること」「分散型という言葉に対する記載の誤り」についてご指摘をいただきました。
そのため、ブランチを利用した開発フローに対して感じたことを焦点に記事を修正しております。誤った情報を記載していたこと、SVNに対して誤ったイメージをもつ可能性のある記載をしていたことに対し、深くお詫び申し上げます。
Gitをまともに使い始めて約二ヶ月がたちました。 特に、「ブランチをきる」「修正する」「レビューする」「マージする」という、おそらくGitで想定されている開発フローに沿っての開発はクラスメソッドに入社してからが初めてです。 6月に入社する以前は、開発用のソースコード管理には主にSVNを利用し、1つのバージョンの流れに全ての修正をコミットしていくフローで開発を行っておりました。
ブランチを利用した開発フローを行って、良かったこともあれば苦しかったこともありましたが、ここまで使ってみて概ねの感想は「これは良いものだ」です。
二カ月間ブランチを利用した開発フローを実践して感じたメリット、デメリットをそれぞれ記載していきます。
こちらの記事は、ソースコードを書いて、リポジトリにコミットする、という立場からの比較になります。 バージョン管理システム自体を管理する立場での比較はしません。
ブランチを使用しない開発フロー
ブランチを使用しない開発フローでは、V1(バージョン1)→V2→V3...のように、何か変更を加えるたびにバージョンが積み重なっていきます。
ブランチを利用しない開発の大雑把な流れは以下です。
ブランチを利用した開発フロー
ブランチを利用した開発フローでは、バージョンの流れが同時に複数存在します。 ブランチを使用しない開発フローではV1→V2→V3...というように、変更に対して直線的にバージョンが積み重なっていきますが、ブランチを使用した開発フローではこのバージョンの積み重なり(以降ブランチと呼ぶ)が同時に複数存在します。
どこかのタイミングで「メインのソースコードを綺麗に保ちたいバージョンの流れ(以降masterブランチと呼ぶ)」に、「何か修正を加えたブランチ」を取り込むことで変更を反映していきます。
ブランチを利用した開発の大雑把な流れは以下です。
ポイントは2つです。 1つは同時にAとBの修正をしている時に、「Aの修正からBの修正は見えない」「Bの修正からもAの修正は見えない」ことです。
もう1つは、masterブランチに、開発中の中途半端な状態のソースコードが混じらないことです。
ここまで大まかにそれぞれの開発フローのおさらいをしたので、それぞれを比較しながらメリット・デメリットについて記載していきます。
ブランチを利用した開発フローのメリット
コードを綺麗に保てる
ブランチを利用しない開発フローと比べて、を綺麗に保つことができます。 コードを綺麗に保つ、というのはつまり、
- 修正途中の中途半端なコードがmasterブランチに含まれない
- レビューしていないコードがmasterブランチに含まれない
ということです。
修正ごとに別のブランチを作成することで、「修正・レビューが完了したソースコードのみをmasterブランチに取り込む」という流れを守れば、修正・レビューが完了した変更のみをmasterブランチに取り込むことができます。 ブランチを利用していなかった頃は、バージョンに取り込まれた変更をレビューする、という流れでの開発をしていましたが、レビューをまだしていないコードや、修正途中のコードがバージョンに取り込まれていました。
このため、「今のソースコードはリリースしても問題ないのか?」「今のソースコードでテストが通らないのはバグなのか?修正途中なのか?」といったところを開発メンバーとコミュニケーションをとって確認したり、状況を把握する必要がありました。
また、すでにリリースされているアプリケーションに対して細かい修正をいくつも行う場合、「常に誰かが何かを修正している」状態になります。 この場合、リリースしても問題ないタイミングを調整したり、リリース用のソースコードを抽出する必要が出てきます。
例えば、
- Aの開発は完了
- Bのバグ修正は完了
- Cの機能修正は途中で、あと修正に3ヶ月かかる
という状況でAとBが完了したアプリケーションをリリースしたい場合、Cの修正がリリースしても問題ない状態になるのを待ったり、Cの修正だけを取り除いたソースコードを抽出したりする必要が出てきます。
これは非常に面倒くさく気持ちの乗らない作業です。
レビューがしやすい
ブランチを利用せずに1つのバージョンの流れにひたすらに変更を取り込んでいた頃と比べて、非常にレビューがしやすいと感じています。 理由は以下の2点です。
ある修正に対して、その修正の範囲の差分なのか関係のない差分なのか判断する必要がない
ブランチを分けない場合、基本的にそれぞれの修正が全て1つのバージョンに統合されます。
なので、以下のような状況で
修正Aのレビューをするために1と4の差分を出すと以下になります。
ここで、レビュー対象の変更は「オレンジ」の差分だけです。「緑」の差分は修正Bの範囲なので、レビュー時の差分には「オレンジ」だけ出てきてほしいんです。
余計な差分が混じると、意図的に「これは関係ない変更だからレビューの対象外」という判断をしなくてはなりませんし、判断を間違えるとレビューそのものの精度が落ちます。
ブランチを利用した開発フローでは「オレンジ」の差分と「緑」の差分が別のブランチでの作業となるため、差分に「オレンジ」と「緑」が混じることはありません。
ある修正がどのバージョンから始まって、どのバージョンで完了したのか判断する必要がない
上記と似ているのですが、ある修正に対してレビューする時はその修正における全ての変更をレビューする必要があります。 ブランチを利用しない場合、「修正途中のソースコードがすでにバージョンに含まれている」ため、すでに修正されたソースコードの中で「修正直前のバージョンはどれで、修正完了したバージョンはどれ」という判断をする必要があります。
以前はコミット時のコメントや、修正した人に確認することでレビュー対象のバージョンを確認していましたが、ブランチを利用した開発フローではこの作業が不要です。なぜなら、修正用のブランチにおいてはその修正において必要な変更が全て含まれるからです。
また、どのバージョンとどのバージョンの差分をレビューすれば良いのか、という判断を間違えると一切レビューしていないコードがそのままにになる危険がありますが、これもブランチを利用した開発フローでは防ぐことができます。
開発フローの中で「レビュー」というステージを明確にしやすい
個人的にはこれが修正ごとにブランチを分けることの最も大きいメリットだと思っています。 どういうことかというと、
- ブランチを作成
- ソースコードを修正
- レビュー
- masterブランチに取り込み
というフローを明確にしておけば、レビューしていないソースがmasterブランチに取り込まれないということです。
ブランチを利用しなくてもルールを厳密に守ることで、レビューしていないソースコードを許さない、という運用は可能ですが、実際問題としてレビューを全くしていない変更が反映されることが多々ありました。ルールで守る運用は絶対に漏れが出ると思っています。ルールよりも仕組みで運用した方が精度が高いです。
これは例えば以下のような状況です。
- ある修正AのついでにちょっとしたバグBを修正した。
- 修正Aに対するレビューをした。バグBの修正は修正Aの変更とは関係ないため未完了の別件の修正であるとみなした。
- 結果、バグBの修正は誰もレビューしないままになってしまった。
ブランチを利用した開発フローでは「ブランチを作成→修正→レビュー→masterブランチに取り込み」というフローの中で、「修正の完了」と「masterブランチへの取り込み」が明確に分かれていることや、差分は全て「その修正の変更部分」なので全ての差分はレビュー対象と判断できるため、意識的にレビューせずにmasterブランチへの取り込まない限り、無意識にレビューが漏れることを防げます。
また、GitHubを利用する場合は「そもそもレビューしていないコードを取り込む操作をさせない」という制限をすることも可能です。
About protected branches - User Documentation
バージョンの流れを綺麗にしやすい
修正ごとにブランチを切らない場合、明確に「この修正はこのバージョンにまとまっている」という区切りがありません。 例えば、バージョン1からバージョン5にかけてA機能の追加を行なった場合に、バージョン3が全然関係のないB機能の追加である可能性があります。
さらに、開発者が「ここでは1つの修正をしている」という意識をしづらいため、バージョン5にはA機能の追加と、C機能の変更途中のコードが含まれているかもしれません。 この状況はブランチを利用した開発でも発生しうりますが、やはり「1つのブランチでは1つの修正をする」という意識が向きやすいように感じます。
修正ごとにブランチを切らない場合、構造がシンプルな分ルールを作って運用しても「バージョン付きのアップローダー」という認識で使いたくなるところも原因のように思います。
バージョンの流れが綺麗になると何が良いかというと、特定の機能が追加されたタイミングでのソースコードを取得したり、切り戻したりしやすいというところです。
以下のような状況で
修正Aだけが取り込まれたソースコードが欲しい場合、実際に欲しいソースコードは以下です。
しかし、そんなバージョンはないため「自分で欲しい状態のソースコード」を作る必要があります。 これは面倒な上、あまり生産的な作業ではないためストレスがたまります。
その上、この作業でミスをすると不具合の原因になるため、「新しく作り出したソースコード」をテストする必要があります。
ブランチを利用した開発フローでは、完了した修正ごとにmasterブランチに変更を取り込むため、ある修正が完了したタイミングで開発中の中途半端なソースが混じる、ということを防ぐことができます。
ブランチを利用した開発フローのデメリット
操作が複雑
上記の使用イメージを見ていただいて感じた方もいると思いますが、ブランチを利用した開発フローの方が明らかに複雑です。 開発者として使用する場合も、日常的に使うコマンドの数が単純に多かったり、ブランチの概念を理解したりするにはそれなりに学習コストがかかります。
修正ごとにブランチを切らない場合は、アップロード・ダウンロードの概念が理解できていれば半日でほぼ問題なく利用できるようになりましたが、ブランチを利用した開発ではハマらずに使えるようになるまでに3日ほどかかりました。
ブランチの切り方が難しい
ブランチを利用した開発フローでは何かまとまった修正をするごとにそれぞれブランチを作成しますが、「どういう単位でブランチを切ればいいのか?」ということを考えるのにそれなりにコストを使います。 ブランチの切りどころを誤ると、例えば以下のような問題が起きます。
AさんとBさんがそれぞれ異なる機能を開発していたが、それぞれ全く同じ機能が必要で同じものを2つ作ってしまう
このような問題はブランチを切らずに1つのバージョンの流れに全ての変更を都度適用する場合でも発生しうるのですが、ポイントは「Aさんの開発途中のソースコードがBさんから見えない」というところです。 ブランチを切らずに1箇所に全ての修正を反映する場合、Aさんが共通機能をコミットしたタイミングでBさんが「Aさんが同じ機能作ったからそれを使おう」と気付ける可能性がありますが、修正ごとにブランチを切る場合では他人が開発しているブランチを意識的に見に行かない限り気づくことができません。
そのため、想像力を働かせて「共通機能だけのブランチを作る」「他の人も使いそうだから、共通機能として作ることを共有しておく」のように、開発の手順的なところに頭を使う必要があります。
ブランチを利用した開発でもレビューのタイミングで気づくことはできるのですが、共通機能の開発規模が大きい場合無駄な作業が膨らんでしまうため避けたい状況です。
1つの修正に対して複数の細かいブランチを切りすぎた結果、開発のフローが複雑になり、コンフリクト解消の作業が増える
修正ごとにブランチをわけない場合、バージョン管理サーバと同期したタイミングで各開発者のコードに他人の修正が反映されます。 そのため、もし複数人が同一のファイルを更新しファイルがコンフリクトした時も、誰かがそのファイルを修正したタイミングでコンフリクトに気づくことができます。
一方、修正ごとにブランチをわけた場合、他人の修正ブランチを監視していない限りコンフリクトに気づくことができるのは「誰かの修正が完了し、masterブランチにマージされたタイミング」となります。 何が問題かというと、コンフリクトに気づくタイミングが遅れるので修正範囲が気づかないうちにどんどん大きくなる可能性があるということです。
「AさんがZクラスを使ったモジュールを20個実装」「BさんがZクラスの名前を変更」のようなことをした時に、Aさんが先にmasterブランチにマージしていればBさんはAさんが実装したクラスをひたすらに直す必要がありますし、Bさんが先にマージしていればAさんは20個のモジュールをそれぞれ修正しなくてはいけなくなります。
「ブランチの切り方が難しい」というのは、このような状況を起こさないために「BさんがZクラスの名前を変更してからAさんが作業を開始」したり、「AさんがZクラスを使ったモジュールを全て実装し終わってからBさんがクラスの名前を一括で変更」するようなことをしなければならないということです。 この作業を別々のブランチで同時に行ってしまうと面倒なことになります。
まとめ
大雑把に言えば、ブランチを利用した開発フロー「ソースコードを綺麗に保ちやすい」「レビューがしやすい」の2点で、デメリットは「操作が複雑」「考えることが多い」の2点かと思います。 単純にバージョンが管理できるアップローダーとしての利用であればともかくレビューを前提とした開発を行う場合は大きなメリットがあると感じました。
私からは以上です。