はじめに
入門継続的デリバリーや DORA の調査など、CI/CD に関する本や資料を読むと多くの記事でトランクベース開発を行い、main ブランチ1に頻繁にマージすることを推奨しています。
一方で、私の経験上(経験は少ないですが)長命なブランチを切って開発を行っている開発現場が多いように感じます。
git-flow や GitHub Flow などは長命なブランチ戦略と認識しており、それらの技術記事も多く見かけます。
そこで今回はそれぞれの開発フローについて調査し、どのような開発フローが良いのかを私なりに考えてみました。
今回の記事は一個人の考えも多く含んでいるため、あくまで一つの意見として捉えていただければと思います。
また、皆様からのご意見やご指摘をお待ちしております。
そもそもトランクベース開発とは
こちらの記事によるとトランクベース開発には流派はが 3 つほどあるみたいです。
3 つ目の Coupled “Patch Review” System は、開発者が 2 人から 4 万人ほどのケースの場合みたいで、今回はそれ以外の 2 つの流派について説明します。
Committing Straight to the Trunk
こちらは、直接 main ブランチにコミットするスタイルです。
1 人から 100 人までの開発者数に対して適しているとされています。
直接 main ブランチにコミットするため、開発スループットが高いとされています。
ただし、コミットする前に自動テストやレビューによって、コードが壊れないことを保証することが前提とのことです。
また、コードを壊した場合は即座にロールバックする必要があるともされています。
これは main ブランチにコミットするため、main ブランチが壊れることは許容できないものかつ、本番環境にも影響が出るためと思われます。
自動テストにかかる時間と、コミットにかかる時間を比較したときに、コミットの方が明らかに時間が短い場合は地獄を見ることになり、他のスタイルが推奨されるとのことです。
Short-Lived Feature Branches
こちらは、main ブランチから短命(2 日以下)な feature ブランチを切り、main ブランチにマージするスタイルです。
このスタイルは 2 人から 1000 人ほどの開発者数に適しているとされています。
直接 main ブランチにマージする方法と違って、feature ブランチを切ることで main ブランチを壊すリスクを減らすことができます。
feature ブランチという名前ですが、一つの機能が完全に動作する単位でブランチを切る必要はないみたく、機能を動作させるために必要なタスク単位でブランチを切り、短命であることを保つことが重要とされています。
また main への統合前に PR などの仕組みを利用できるのも特徴です。
直接マージを行う場合 PR などの仕組みを利用することが難しく、コミット前の同期的なレビューやペアプログラミングが必要とされていますが、Short-Lived Feature Branches では PR による非同期的なレビューも可能となります。
トランクベース開発のメリット
紹介した 2 つのスタイルの共通点は main ブランチへ頻繁にマージすることが挙げられます。
また管理する長命なブランチが main 一つだけというのも大きいと思います。
これによりコンフリクトの発生やバグ発生時の切り戻し修正などのマージによるリスクやブランチ運用の複雑性を減らすことができると思います。
長寿なブランチが多くあるとコンフリクトは起こりやすいですし、コンフリクトが大きくなりやすくマージそのものが難しくなります。
また、ブランチが多いことでブランチの運用も複雑になり、意図しない不整合が発生することもあるかと思います。2
トランクベース開発のように頻繁にマージするということは、
- 変更が小さくなる
- コンフリクトが発生してもそれ自体が小さいので、解消も容易
- 一回に発生するレビューの負担が小さくなる
- 開発者が main ブランチから最新のコードを頻繁に取得できる。3
- 他の開発者が開発したコードを即座に把握/利用できる
- 開発者はすぐに自分のコードが統合されるので、自分のコードに対する様々なフィードバックを得ることができる
などのメリットがあります。
トランクベース開発での考慮事項
一方トランクベース開発を行う際には、以下のような考慮事項があると思います。
- 自動テストやレビューに投資する必要がある4
- 頻繁に main ブランチにマージするため、コードが壊れないことを保証する必要がある
- 頻繁にマージするため、レビューの頻度が高くなる
- 機能単位よりも細かい、短命ブランチで終わる程度のタスク分解が求められる
- 細かくタスク分解するためにスキルや工数が必要
- 未完成の機能が main ブランチにマージされることを許容できる必要がある
- トランクベース開発のマージ頻度では一つの機能が完全に揃うまで main ブランチにマージすることが難しい場合がある
- 未完成の機能が main ブランチにマージされ、本番環境にデプロイされても問題ないような仕組みやルールが必要
- これには、feature flag などがあるが、それ自体の運用が複雑性を持つ
その他の開発フローについて
次にトランクベース開発ではないかつ一般的に人気のある git-flow, GitHub Flow について見ていきます。
git-flow
git-flow は、main, develop, feature, release, hotfix という 5 つのブランチを使って開発を行う手法です。
主な開発フローは
- feature ブランチで機能を開発
- 機能開発が終わり次第 feature ブランチを develop ブランチにマージ
- develop ブランチで機能が揃ったら release ブランチを切り、QA などのリリース準備を行う
- もし修正があれば release ブランチで修正を行い、問題がなくなり次第、main,develop ブランチにマージ
- main ブランチでリリースし、問題があれば hotfix ブランチを切り、修正を行い、main,develop ブランチにマージ
といった流れになります。
トランクベース開発と違って、main ブランチ以外にも develop,feature などの長命なブランチが存在します。
git-flow の決まり事ではないですが、ブランチ毎にデプロイ環境(本番環境、ステージング環境など)を対応させることが多い印象です。
これにより本番環境に影響を与えずに、本番環境にデプロイする前にステージング環境などでテストすることができます。
main や本番環境に与える影響を抑え、機能毎に並行して開発できることが人気の理由の一つではないかと思います。
ただし、この git-flow を広めたとされる Vincent Driessen さんのブログでは現在以下のようなことが述べています。
Note of reflection (March 5, 2020)
This model was conceived in 2010, now more than 10 years ago, and not very long after Git itself came into being. In those 10 years, git-flow (the branching model laid out in this article) has become hugely popular in many a software team to the point where people have started treating it like a standard of sorts — but unfortunately also as a dogma or panacea.
During those 10 years, Git itself has taken the world by a storm, and the most popular type of software that is being developed with Git is shifting more towards web apps — at least in my filter bubble. Web apps are typically continuously delivered, not rolled back, and you don't have to support multiple versions of the software running in the wild.This is not the class of software that I had in mind when I wrote the blog post 10 years ago. If your team is doing continuous delivery of software, I would suggest to adopt a much simpler workflow (like GitHub flow) instead of trying to shoehorn git-flow into your team.
If, however, you are building software that is explicitly versioned, or if you need to support multiple versions of your software in the wild, then git-flow may still be as good of a fit to your team as it has been to people in the last 10 years. In that case, please read on.
To conclude, always remember that panaceas don't exist. Consider your own context. Don't be hating. Decide for yourself.
git-flow は、複数のバージョンをサポートする必要がある場合などに適しているとされており、Web アプリケーションなどの複数のバージョンを持つ必要がなく、継続的にリリースするような場合は、GitHub Flow などのシンプルなフローを採用することを推奨しています。
そのため、次は GitHub Flow について見てみます!
GitHub Flow
GitHub Flow は GitHub が提唱するシンプルなブランチ戦略です。
開発は main ブランチと feature ブランチのみで行い、feature ブランチで機能を開発したのち、main ブランチにマージするものです。
GitHub のドキュメントには feature ブランチに対する寿命などは明確に記されていないですが、逆に 頻繁な main へのマージというトランクベース開発の肝となる部分についても明示されていないため、feature ブランチは長命であることが許可されている、一つの機能が完全に揃うまでは main にマージしない、といった解釈でこの記事では扱います。5
メリットは、feature ブランチで開発を行うので、main ブランチを壊しにくいことや、PR を使ってレビューを行うことができることが挙げられます。
またブランチ自体の数も少なく、ブランチの運用が簡単であることもメリットとして挙げられます。
Short-Lived Feature Branches との違いは、一機能を完了するまで生きるブランチを運用することで、完全に動作する機能のみが main ブランチにマージされることです。
git-flow,GitHub Flow の共通点
git-flow と GitHub Flow に共通する点としては、長命なブランチを切ることだと思います。
長命なブランチを切ることで
- main ブランチへの影響を気にせずに開発を行うことができる
- 実験的なコードを書くことができる
- もしバグを埋め込んでしまっても、main ブランチにマージしない限り本番環境に影響を与えない
- 即座に main ブランチにマージされるわけではなく、マージ前に別ブランチでレビューやテストを行うことができる
- 非同期的にレビューを行うことができる
- main へのマージ頻度が多くないため、それに伴いレビューの頻度も抑えることができる
- 完成した機能のみが main ブランチにマージされやすい
- 前述のようにトランクベース開発だと仕掛かり中のコードが main にマージされリリースされる可能性があり、それを防ぐことができる
などといった、トランクベース開発にはないメリットがあると思います。
それぞれのメリット・デメリット考察
それぞれの開発フローに関する特徴を表にまとめました。
各開発フローとの相対的な評価になっていますので、少し辛口な評価になっているかもしれません。
また、それぞれの項目には一般的に可能性が高いものを挙げているため、実際の開発現場ややり方によっては異なる可能性があります。
ご了承ください。
項目 | トランクベース開発 | git-flow | GitHub Flow |
---|---|---|---|
コンフリクトの解消 | 容易 | 難しい | 難しい |
手戻りの大きさ | 小さい | 大きい | 大きい |
フィードバックを得られる頻度 | 高い | 低い | 低い |
レビュー方法 | 同期が望ましい | 同期でも非同期でも可 | 同期でも非同期でも可 |
レビュー頻度 | 高い | 低い | 低い |
レビュー対象コードの大きさ | 小さい | 大きい | 大きい |
ブランチの運用 | 簡単 | 難しい | 簡単 |
main ブランチのコード | 未完成の機能が含まれることがある | 完全に動作する機能のみが取り込まれる | 完全に動作する機能のみが取り込まれる |
複数環境へのデプロイ | 一つのブランチで複数環境をデプロイ | 複数ブランチで複数環境をデプロイ可能 | 複数ブランチで複数環境をデプロイ可能 |
上記を見る限り、どの開発フローもメリットとデメリットがあり、どれが良いということは一概には言えないと思います。
そこで、私なりにどのような開発フローが良いのかを考えてみました。
僕の考える開発フロー
私にとって、コンフリクトの解消が容易であることや、手戻りの頻度が低いこと、自分の変更に対するフィードバックが高速に得られるは開発体験として重要だと思っています。
また、ブランチも少ないほどブランチ自体の運用は簡単なはずですし、main ブランチを SSOT(シングルソースオブスルース)の考え方で利用することもできるようになります。
よって、できるだけトランクベース開発に近い開発フローが私の好みです。
ただし、トランクベース開発のように main ブランチに頻繁にマージすることは、本番環境に影響を与えるリスクが高いのも事実です。
よって、以下の要件を満たす開発フローや仕組みが重要だと思いました。
- できるだけ頻繁に main ブランチにマージする
- main ブランチを SSOT として扱う
- main ブランチへのマージによる本番環境に与える影響を最小限に抑える、コントロールできるようにする
このような要件を満たす開発フローを考えると、以下のような開発フローが良いのではないかと思います。
- ブランチは main ブランチと 短命な feature ブランチのみ
- feature ブランチを main ブランチにマージする際は PR を利用し、レビュー,自動テストを行う
- main ブランチへのマージによって本番環境にデプロイするアーティファクトを生成する
- アーティファクトは main ブランチのコミットと対応づけて保存する
- ステージング環境や本番環境にデプロイする際はアーティファクトを選択することで達成し、マージとは別のサイクルで行う
- マージを行っても本番環境に影響を与えない
- 複数の環境に一つのアーティファクトでデプロイできる
- 本番環境が壊れた場合でもアーティファクトを選択し直すだけで切り戻しを行うことができる6
- もし main ブランが壊れた場合は、壊した本人が直ちにロールバックを行う
- デプロイ環境が壊れたら、アーティファクトを選択し直して、ロールバックを行う
上のような開発フローにすると、main ブランチに頻繁にマージすることができ、main ブランチを SSOT として扱うことができます。
頻繁にマージすることによる本番環境へのリスクもデプロイ方法を工夫することで最小限に抑えることができているのではと思います。
未完成の機能が main へ統合されるリスクや、頻繁なレビューといった点はまだありますが、それらは適切なルールや仕組みを設けることで解消できると考えています。
そもそも未完成な機能も本番環境に影響さえ出さなければ他の開発者がすぐにコードを把握できるメリットがあると思いますし、頻繁なレビューも時間は取られるもののレビュー対象のコードは小さいはずなので、レビューの負担は小さく、開発者は頻繁にフィードバックを得られ、レビュワーも頻繁に進捗やコードを把握できるので悪いことだらけではないのでは?と個人的には思います。
デプロイ方法に関しては開発フローで定義する領域を超えているかもしれませんが、main への頻繁なマージをしてもリスクを最小限にする仕組みとして重要と考えており、記載させていただきました。
次の章ではデプロイ方法に関する考えを説明させていただきます。
CI と CD の分離
各ブランチの変更とともにブランチに対応している環境にデプロイする方法は CI/CD の実装として採用されているケースがあると思います。
ただし各ブランチの変更と環境へのデプロイを対応させてしまうと、main へ頻繁なマージのリスクはそのまま本番環境にも伝播してしまいます。
そのため、main へのマージと本番環境へのデプロイを切り離すことが重要だと思いました。
main へのマージ後はアーティファクトの生成までを行い、デプロイに関しては別の関心毎として扱うとうまくいくのではないかと思います。
これに近い考え方として GitOps という考え方があると思います。
これはコードのリポジトリとデプロイなどに関する設定のリポジトリを分け、コードリポジトリの変更をトリガーしてアーティファクトを生成し、設定リポジトリに記載されている構成と同期するように継続的にデプロイする方法です。
つまり CI と CD を分離して考えることで、別のサイクルとして考えることができ、安全かつ高速に main へのマージを可能にするといったメリットを得ることができます。
終わりに
今回は様々な開発フローについて調査し、それぞれのメリット・デメリットを考察しました。
また、それぞれのメリット・デメリットを踏まえて、どのような開発フローが良いのかを考えてみました。
このように調査や考察を重ねることで、様々な開発フローのメリット、デメリットがなんとなくわかってきたり、GitOps といった前から知ってはいたものの、実際にどのようなメリットがあるのかをより理解することができ、良かったです。
開発フローやデプロイ方法について考えてはみたものの、実際このような開発フローを長期間で実践したことはないため、今後どこかでやってみようと思います。
その際の所感や学びなどもまた記事にしていけたらと思います。
最後まで読んでいただきありがとうございました!
もし何かお気づきの点やご指摘があれば、お気軽にコメントいただけると嬉しいです。
-
今回の話は git をバージョン管理ツールとして使っている前提で話を進めます。 ↩
-
私は最近 develop ブランチと main ブランチになぜか不整合が発生し、気づくことに時間を要したことがあります。 ↩
-
もちろん頻繁に取得しなくてはいけないというコストもあります。また、それを徹底する必要もあります。 ↩
-
GitHub Flow においても同様に自動テストを整備することは大事ですが、トランクベース開発の場合は main へ直接マージされるため特に重要とされています。 ↩
-
そうではないケースもあるとは思いますが、トランクベース開発との比較がしやすくするためにこのように記載しました。 ↩
-
ただし、DB スキーマ の変更などのリスクは別途考慮が必要かと思います。 ↩