アプリ(iOS)のブランチ運用を考え直してみた (Webとの比較も)

## はじめに

iOSも少しずつ担当するようになったバックエンドエンジニアの Takeya です。
今のところ、環境周りのコミットをしたりして微力ながら改善に取り組んでます。

今回新メンバーも増えてGitのブランチ運用がよくわからない、わかりづらいという話になったので、iOSチームで再考してみました。
([SPORTS BULL](https://sportsbull.jp/) のiOSリポジトリの話になります)

## メジャーなGit運用フロー

そもそもGitの運用フローにはメジャーなフローがあります。以下の説明がよくまとまってます。
- git-flow
- GitHub Flow

https://atmarkit.itmedia.co.jp/ait/articles/1708/01/news015.html

特徴をざっくり書きます。

### [git-flow](https://danielkummer.github.io/git-flow-cheatsheet/index.ja_JP.html)

https://danielkummer.github.io/git-flow-cheatsheet/index.ja_JP.html

- 思想 (私の理解)
    - "実装できたらどんどん `develop` にマージや！ いい感じになったらリリースするで！"
- 使用するブランチ
    - `master`
    - `develop`
    - `feature`
    - `release`
    - `hotfix`
- 常時存在するブランチ
    - `master`
    - `develop`
- [git用プラグイン](https://github.com/nvie/gitflow)がある(SourceTreeは標準搭載。便利)
    - 更新は止まってる。。。(master を main にしようっていう[Issue](https://github.com/nvie/gitflow/issues/6458)が全然進まない...)

### GitHub Flow

- 思想 (私の理解)
    - "実装できたらどんどん `main` にマージや！ `main`にあるものはいつだってリリースOK、てゆーか即リリースや！"
- 使用するブランチ
    - `main`
    - `feature`
- 常時存在するブランチ
    - `main`
- ブランチの種類が少なくてシンプル

## 現状の問題点

概ね git-flow ベースのフローだったが、あまり細かいルール化まで行われていませんでした。

### 機能の取りまとめ用ブランチの切り方が明文化されていなかった

基本的にはどんどんリリースしたいのですが、リリースタイミングのコントロールをしたい時がちょくちょくあります。
(バックエンドと絡む場合や、iOSとAndroidで同時リリースしたいものなど)
が、これに関しては、 git-flow, GitHub Flow には明記がないみたいなので考える必要があります。

あるあるなやり方として、機能の取りまとめ用のブランチを作ってリリースタイミングまでそのブランチにまとめておきます。ですが、そのブランチの切り方(命名など)が明文化されていなかったので、わかりづらい状態でした。(以下、例)
- `develop_xxxx` (`develop`の兄弟)
- (`feature/xxxx-master`)

対策: 機能の取りまとめ用のブランチの切り方のルール化

### `develop`ブランチの必要性が曖昧になっていた

前項の内容も踏まえると、結局 `develop` にマージされるブランチは以下です。
- 機能の取りまとめ用ブランチ
- `feature` (単発の小さめな機能)
- `release`
- `hotfix`

ですが、これは `main` に置き換えてもそのままできそうな気がします。
そのため、 `develop` って必要? となっている人もいました。

対策: `develop`ブランチをなくしてみる (GitHub Flowをベースに必要なブランチを足していくイメージ)
(ただ、これができるのは、アプリだからこそなのかなと思いました。(後述))

### タグ打ちが徹底されていなかった

バグが発生したときなどに、 vX.X.X 時点ではどうだったか、のように遡って確認したいケースがたまーにあります。大抵の場合は TestFlight に残ってるのでなんとかなるのですが、どのバージョンがどのコミット時点のソースコードをビルドしたものかを管理できていないのはよろしくありませんでした。

対策: タグを打つ！


## 結論

- `develop` を廃止(近日予定)
- 以下のブランチのみで運用する (GitHub Flowを参考にして必要なものを追加)
    - `main`
    - `feature`: 単発の機能
    - `feature/xxx-main`: (機能の規模が大きいときの取りまとめ用)
    - (`hotfix`): 急ぎのバグ対応
        - 急ぎのバグ対応であることがわかるように名前は分けるが、運用は`feature`と同じ
    - `release`: リリース作業用
- 常時存在するブランチ
    - `main` のみ
- `feature` は `release` にマージする。 `release` ブランチにリリース対象を集めていくイメージ。事情があってリリースできなくなったらリバートして次の `release` ブランチにマージする。
- リリースしたらタグを打つ (ex. `v6.3.12`)

まだ運用し始めて1ヶ月ぐらいですが、今のところ大きな問題はなさそうです。


## 課題(というか制限事項)
- 開発中のアプリの配布は、各ブランチで実施する必要があるので、各ブランチでバージョンを変更して行う。
- `feature` のPRを作りたいタイミングで `release` ブランチを作る必要がある。
    - この対策としては、 `release` ブランチを `main` にマージ後、次バージョンの `release` ブランチを切る、を検討中。


## Webチームとの運用の違い

### アプリとWebの違い (クライアントとサーバーの違い)

アプリの場合、開発、ステージング、本番の違いは、主に以下になります。
- 接続先APIのエンドポイント
- その他各種設定(デバッグフラグ、広告IDなど)

つまり、アプリは同一ソースコードで環境に依存した設定値のみ差し替えてビルドができれば基本的に問題ないわけです。
実際Xcodeには Scheme という機構があって、同一コードで Scheme を切り替えて、開発用、ステージング用、本番用をビルドしています。

一方で、Webには以下の特徴があります。
- サーバーに配置され、常時別の環境(開発、ステージング、本番)として存在し続けている必要がある
- どうしてもサーバーに配置しないと動作確認できない・しづらいケースがある
    - 開発用サーバーにだけリリースしたい、というケースが出てくる

そのため、開発サーバー、ステージングサーバー、本番サーバーごとにブランチを分けて、対応させています。
ゆえに常時存在するブランチは以下になります。
- `develop`: 開発サーバー用
- `staging`: ステージングサーバー用
- `master`: 本番サーバー用

こうしておくことで、以下のメリットがあります。
- 各サーバーの状態を知りたくなったら、対応するブランチを見ればいい
- 各ブランチにマージされたら該当環境にデプロイ、という仕組みが作りやすい

使用しているブランチ
- `develop`
- `staging`
- `master`
- `feature`: 単発の機能
- `feature/xxx-main`: (機能の規模が大きいときの取りまとめ用)

`hotfix` は、急ぎでも必ず `develop`　に配備して動作確認するようにしているため使用していません。
`release` は、`feature/xxx-main` が同等なので不要です。

<!-- ## 参考: Androidチームはどう運用しているか? -->

## さいごに
運動通信社は
「日本を世界が憧れるスポーツ大国にする」というビジョンを達成するべく、
一緒に働く仲間を募集しています！

https://sportsbull.jp/about/#recruit