最適なブランチ戦略を考える

# 結論
- CICDが充実している状態（リグレッションテスト・リリースがほぼ自動で行われる）であれば、トランクベース開発・GitHub Flowで良き
- 定期リリースが必要（ネイティブアプリ開発）であればGit Flow
- 開発環境が足りないのであれば環境ブランチの導入を
- リリース単位はうまいこと小さく分けられる（はず）
- ブランチ戦略はシンプル is ベスト

# はじめに
これまで6年間、複数の会社・プロジェクトで、様々なブランチ戦略を経験してきました。
私自身は、経験した中で、このようなケースバイケースで最適なブランチ戦略はあると思いつつ、言語化するのがなかなか難しかったので、ここでまとめてみようと思います。
これを使って様々なプロジェクトに活かしていこうと思います。
皆様にとっても、参考になればと思います。

# 様々なブランチ戦略

世の中に様々なブランチ戦略が提示されていると思います。
今回は、理想のブランチ戦略から、状況に応じて複雑化していく様を見ていこうと思います。

## ①理想のブランチ（トランクベース開発？）

```mermaid
gitGraph
  commit id:"メジャー版リリース" tag:"v1.0.0"
  commit id:"マイナー版リリース" tag:"v1.1.0"
  commit id:"パッチ版リリース" tag:"v1.1.1"
  commit id:"マイナー版リリースその2" tag:"v1.2.0"
  commit id:"パッチ版リリースその2" tag:"v1.2.1"
  commit id:"メジャー版リリースその2" tag:"v2.0.0"
  commit id:"マイナー版リリースその3" tag:"v2.1.0"
  commit id:"パッチ版リリースその3" tag:"v2.1.1"
```

理想的なブランチ戦略とは、おそらくこのような形ではないでしょうか。トランクベース開発においても、このような形になるかと思います。

さまざまなブランチ戦略における、いわゆる `release` ブランチに相当するものとも言えるかもしれません。

個人開発の場合は、自然とこのような形で運用されている方もあるかもしれません。

また、トランクベース開発を採用しており、Pull Request を作成せずにモブプロやペアプロを行っている企業では、このような運用がされている可能性もあります。

とはいえ、少人数でなければ一つのブランチを占有するのは難しく、この運用では限界がある場面も出てくるかと思います。

## ②GitHub Flow

```mermaid
gitGraph
  commit id:"Initial commit" tag:"v1.0.0"

  branch feature-1
  commit id:"Feature 1 開発1"
  commit id:"Feature 1 開発2"
  checkout main

  branch feature-2
  commit id:"Feature 2 開発1"
  checkout feature-1
  commit id:"Feature 1 開発3"
  checkout feature-2
  commit id:"Feature 2 開発2"
  checkout main
  merge feature-1 tag:"v1.1.0"
  
  branch feature-3
  commit id:"Feature 3 開発1"
  checkout feature-2
  commit id:"Feature 2 開発3"
  checkout feature-3
  commit id:"Feature 3 開発2"
  checkout main
  merge feature-2 tag:"v1.2.0"

  checkout feature-3
  commit id:"Feature 3 開発3"
  checkout main
  merge feature-3 tag:"v1.2.1"
```

先ほどご紹介したブランチ戦略に比べて、複数人がそれぞれ独立して開発を進められるようになった形が、この GitHub Flow だと考えています。

`main`ブランチだけを見ると先ほどの戦略と同じように見えますが、Pull Request を通じて `main` に差分を反映する点が違います。

この運用のほうが、Pull Request をリリースノートのように使えるので、いいなと感じるところもあります。

また、ブランチごとに開発環境を用意できるなら、この運用でも特に問題はないと思います。

自分としてもこの運用で特に困ったことはないのですが、リリース頻度やタイミングの都合で、即時リリースが難しい場面もあるのが現実です。



## ③Git Flow

```mermaid
%%{init: { 'gitGraph': {'showCommitLabel':true, 'mainBranchOrder': 0}} }%%
gitGraph
  commit id:"Initial commit" tag:"v1.0.0"

  branch release/yyyymmdd order:1
  branch develop order:5
  checkout develop

  branch feature-1 order:10
  commit id:"Feature 1 開発1"
  commit id:"Feature 1 開発2"
  checkout main

  branch feature-2 order:15
  commit id:"Feature 2 開発1"
  checkout feature-1
  commit id:"Feature 1 開発3"
  checkout feature-2
  commit id:"Feature 2 開発2"
  checkout develop
  merge feature-1
  
  branch feature-3 order:20
  commit id:"Feature 3 開発1"
  checkout feature-2
  commit id:"Feature 2 開発3"
  checkout feature-3
  commit id:"Feature 3 開発2"
  checkout develop
  merge feature-2

  checkout release/yyyymmdd
  merge develop

  commit id:"bug修正"

  checkout develop
  merge release/yyyymmdd

  checkout main
  merge release/yyyymmdd tag:"v1.x.x"

  checkout feature-3
  commit id:"Feature 3 開発3"
  checkout develop
  merge feature-3
```

定期的にリリースを行う場合、資材を一箇所にまとめておく必要があります。  
たとえば `release/yyyymmdd` のようなブランチを作成し、その日にリリースする内容をそこに集約してからリリースする、という形です。

このブランチ運用だと、資材をマージした際にどのような挙動になるかを確認する必要があるため、これまでの運用と比べると少し手間が増える印象です。  
`release` ブランチで発生したバグについては、そのブランチ上で修正し、`develop` ブランチにも取り込むのが自然な流れだと思います。

ただ、ここで問題が発生するケースもあるかと思います。  
それは、「結合用や開発用の環境が足りない」といった課題です。

図でいうと `feature` ブランチが3つあるとすれば、少なくとも3つ以上の環境が必要になりますが、すべてのプロジェクトにそのような環境が揃っているとは限りません。

そこで、次にご紹介するようなブランチ戦略が必要になってくるのではないかと思います。


## ④Git Flow + 環境ブランチ

```mermaid
%%{init: { 'gitGraph': {'showCommitLabel':true, 'mainBranchOrder': 0}} }%%
gitGraph
  commit id:"Initial commit" tag:"v1.0.0"

  branch release/yyyymmdd order:1
  branch develop order:5
  branch dev-env order: 100
  checkout develop

  branch feature-1 order:10
  commit id:"Feature 1 開発1"
  commit id:"Feature 1 開発2"
  checkout main

  branch feature-2 order:15
  commit id:"Feature 2 開発1"
  checkout feature-1
  commit id:"Feature 1 開発3"
  checkout feature-2
  commit id:"Feature 2 開発2"
  checkout dev-env
  merge feature-2
  checkout develop
  merge feature-1
  
  branch feature-3 order:20
  commit id:"Feature 3 開発1"
  checkout feature-2
  commit id:"Feature 2 開発3"
  checkout dev-env
  merge feature-2
  checkout feature-3
  commit id:"Feature 3 開発2"
  checkout develop
  merge feature-2

  checkout release/yyyymmdd
  merge develop

  commit id:"bug修正"

  checkout develop
  merge release/yyyymmdd

  checkout main
  merge release/yyyymmdd tag:"v1.x.x"

  checkout feature-3
  commit id:"Feature 3 開発3"
  checkout dev-env
  merge feature-3
  checkout develop
  merge feature-3
```

仮に、メンバーが自由に使える開発環境が1つしかないとした場合を考えてみます。  
（ここでは、本番環境用の `main` ブランチや、プレ本番環境用の `release` ブランチは除外しています）

このような場合、その環境を全員で共有する必要が出てきますが、図のように「環境ブランチ（ここでは `dev-env` ブランチと呼ぶことにします）」を1つ設けて、各 feature ブランチをそこにマージし、開発環境へデプロイしていく運用が必要になるかと思います。

もちろん、各 `feature`ブランチごとに環境を切り替えて使う、という運用も理論上は可能かもしれませんが、実際にはそこまでうまく調整できないケースが多い、というのがこれまでの経験から感じているところです。


# どのブランチ戦略が最適？
文中でも少しずつ触れてきましたが、ここで各ブランチ戦略をいくつかの観点から比較してみたいと思います。
当初は表形式でまとめようと考えていたのですが、整理がなかなか難しかったため、トピックごとに箇条書きのような形で記載してみます。

## デプロイ頻度
| ブランチ戦略 | デプロイ頻度 |補足|
|:-:|:-:|:-:|
|①トランクベース開発|即時可能|`main`ブランチのコミット単位で|
|②GitHub Flow|即時可能|`main`ブランチのに差分が入り次第|
|③Git Flow|リリースブランチ単位|`release`ブランチが`main`ブランチにマージされたら|
|④Git Flow + 環境ブランチ|リリースブランチ単位|`release`ブランチが`main`ブランチにマージされたら|

`main`ブランチの差分きっかけでリリースされるような仕組みになっています。
資材を高スパンでデプロイしていきたいのであれば、上の方のブランチ戦略が良いと思われます。

## 資材の管理工数
|ブランチ戦略|管理工数|補足|
|:-:|:-:|:-:|
|①トランクベース開発|無|ブランチが1本なので|
|②GitHub Flow|小|`feature`ブランチのみ気にすれば良い|
|③Git Flow|中|`release`ブランチの確認工数が増える|
|④Git Flow + 環境ブランチ|大|環境ブランチの管理工数が増える|

①は言うまでもありませんが、②についても基本的には短いスパンでリリースが行われる想定のため、特別な管理工数を気にする必要はないと考えています。
他のブランチとのコンフリクトのリスクはあるものの、それは③や④でも同様です。

③の場合は、「どの資材がどの状態にあるか」といった管理が必要になります。これが意外と煩雑になることが多く、開発側の問題に限らず、他部署との調整（例：「リリース時期がまだ決まっていない」「確認作業が終わっていない」など）によって管理の手間が増えてしまうケースもよくあります。
運用で乗り切れないこともないのですが、「②の運用で済めば良いのに」と感じるプロジェクトは少なくありませんでした。

④については、本質的には③と近いものの、環境ブランチに対して未完成の資材が複数入り混じり、収拾がつかなくなることがあります。
最悪の場合はベースブランチから作り直せばよいのですが、開発者が混乱して環境ブランチをもとに作業ブランチを作成してしまったりと、なかなかにカオスな状態になってしまうこともあります。

なお、ネイティブアプリのようにリリース前に審査が必要な場合は③の運用が適していると思いますが、そうでなければ②の形が理想的かと感じています。（ちなみに、①は小規模な開発や重要度の低いツールなどであれば問題ないと思います）

## 大型案件の場合はどう対応するか？
案件によっては、リリースまでに数ヶ月を要するような大規模なものもあります。
その場合、③や④のような運用が自然に思えるかもしれません。

ただ、必ずしもそうであるとは限らないと考えています。
たとえば、以下のような体制や仕組みが整っていれば、②のブランチ戦略でも十分に対応可能です。

- プロジェクト体制として、こまめなリリースができるようになっている

- サーバーサイドでは後方互換性を保ったデプロイと、Feature Flag による環境変数・制御ツールでの切り替えが行われている

- フロントエンド側でも同様に、Feature Flag による制御が行われている

- Feature Flag の活用に伴い、リグレッションテストが自動化されている

これらが実現できている場合、大型案件であっても②の戦略で運用していくことは十分に可能だと考えています。

また、②のような戦略を採用していれば、そもそも「大型案件」という考え方自体が薄れていき、ユーザーへ継続的かつこまめに価値を届けられるようになる、そんな好循環も生まれるのではないでしょうか。

# 最後に
個人的には、可能であれば②の戦略で開発を進めていくのが理想的だと考えています。
とはいえ、プロジェクトや組織の都合によって、③や④のような形を取らざるを得ない場面があることも、もちろん理解してます。

ただ、③や④でなければならない理由については、一度立ち止まって丁寧に議論してみると、意外と②の形で進められる可能性も見えてくるのではないかと思っています。

今後もさまざまなプロジェクトや企業と関わることになるかと思いますが、できる限り GitHub Flow をベースにした運用ができないかを常に意識しながら、業務に取り組んでいきたいと考えています。