開発チームのスケールに向けたブランチ戦略見直し
概要
組織の拡大に伴う開発チームの分割、独立性向上のためにGitHubの運用フロー見直しと同時にFeature Flagの導入を行いました。 結果として、独立した開発をしてもコンフリクトが発生しづらくなったことにより生産性が向上、副次的効果として部分リリースにより問題の先行発見をしやすくなり、品質向上にもつながりました。
背景:GitHubのブランチ戦略がチームスケールの弊害に
プロダクトや会社の成長に伴い、開発チームにはスピードと安定性の両面が求められるようになっていきますが、少人数のメンバーだと限界がやってきます。実際に、小さな改善はできるけど、ソフトウェアアーキテクチャ自体を見直すような大きな改善施策は、新規開発もある中で中々優先度を上げられないような状態が起きていました。
このような状況下で、開発組織としては総スループットを向上させる必要がありますが、単純に同じプロダクトの開発人数を増やしていくと、調整コストの増大、コンフリクトの発生などにより生産性が思うように上がりません。Anews開発チームでは3人の小さなチームに分割して独立性を高め調整コストを下げる方針をとりましたが、時に大きなコンフリクが発生してしまい、これを防ごうとすると調整コストが発生する状況にあり、両立が課題になっていました。
変更前の運用:コンフリクトと調整コストのトレードオフ
運用方法
- 開発項目毎にmasterからfeatureブランチを派生する
- 1開発が大きい場合、サブタスク用のブランチをfeatureブランチから派生する(図中feature/greenの例)
- 開発項目毎に、サブタスク全てが完了してからdevelopment環境にリリースし受け入れレビューを行う
- リリースは毎週実施しており、火曜日に受け入れレビューが完了している全てのfeatureブランチをreleaseブランチにマージし、staging環境にリリースしてテスト
- 木曜日にstagingブランチをmasterブランチにマージし、production環境にリリース
発生していた課題
- 規模の大きな開発の場合、コード変更がベースブランチであるmasterにマージされるまでの期間/規模が大きくなり、大きなコンフリクトが発生するリスクが高い(図中feature/greenとfeature/redをrelease/2にマージするタイミング)
- しかも、開発をしてからコンフリク解消作業の間に期間が空くため、既に他の開発に着手していることがほとんどで、割り込みによるスイッチングコストが馬鹿にならない
- 人数が少ない間は、同じ箇所の開発をなるべく同時にしないようにしたり、必要な場合は設計の示し合わせをしてからそれぞれ開発を進めることでコンフリクトをなるべく防いでいた(これが調整コスト)
- 開発項目毎にdeveloppment環境を占有して受け入れレビューを実施するため、開発並列度が高まると環境待ちが発生し、解消しようとdevelopment環境を複数作るとインフラコストと管理コストが増大する
変更後の運用:ブランチ戦略見直しとFeature Flagの導入
運用方法
- サブタスク含め全てのブランチをdevelopmentから派生する
- 単体開発が終わり次第developmentブランチにマージする
- developmentブランチの内容は常にdevelopment環境に自動リリースする
- 開発項目毎に、サブタスクが全て完了してからdevelopment環境で受け入れレビューを行う
- 火曜日にdevelopmentブランチをstagingブランチにマージし、staging環境にリリースしてテスト
- 木曜日にstagingブランチをmasterブランチにマージし、production環境にリリース
改善ポイント
- 全てのブランチの派生元をdevelopmentにし、サブタスク単位で完了次第developmentにマージすることで、ベースブランチであるdevelopmentと開発ブランチ(feature)間の差分の期間/規模を小さくし、コンフリクトリスクを小さくできる
- 図のfeature/greenとfeature/redの例では、変更前の運用ではrelease/2へのマージのタイミングでコンフリクト発生するリスクがありますが、変更後の運用ではfeature/redに着手する前にfeature/greenの変更がdevelopmentにマージされているため、コンフリクトが起きることはない
- 同時に、全ての開発内容がdevelopmentにマージされdevelopment環境にリリースされているため、1環境で同時に受け入れレビューを実施できる
Feature Flagの導入
変更後の運用はこのままでは大きな問題が1つ残っています。
1つの開発項目を複数のサブタスクに分割する場合、全てのサブタスクが完了していない状態でもコード自体はマージされているため、何もしなければそのままリリースされてしまいます。全てのサブタスクが完了するまで変更がユーザには見えないようにするために、Feature Flagを導入することでこの問題を解消しました。
具体的には、開発項目ごとのFeature Flagを作成します。Feature Flagがオフの間はstaging/production環境でその機能が表示/実行されないようにしておきます。開発項目内の全てのサブタスクが完了し、受け入れレビューが完了したタイミングでFeature Flagを削除し、staging/production環境で機能が表示/実行されるようにします。これにより、コード自体は最小単位でマージされていくが、機能は開発項目単位でリリースされていく状態を実現しました。
副次的な話になりますが、特にバックエンドのAPIやバッチ処理は部分的に機能を追加してもユーザに影響が無い事が多く、Feature Flagを用いず裏で動いている状態でリリースしてしまうような形をとっています。これにより、先に完成したバッチ処理だけproduction環境にリリースし、そこで問題を発見し、ユーザ影響が出る前に修正を行えた例が実際に起きました。
小さくマージし、小さくリリースすることで、想定外が発生してしまった場合でも問題を小さなうちに解消する事ができ、総合的な品質を高めやすくなったと言えます。
今後に向けて
シード時代、動くものを早く作るのが正義の時代から、今では多くのお客様にご利用いただくようになり、安心してご利用いただくための安定性や継続的に改善を続けられる組織のスケール性がより重要になってきています。 一方で、価値を生み続けるためには、素早く試すアジリティとチャレンジしやすい環境も欠かせません。
これらを両立していくために、ソフトウェアのリアーキテクチャ、プロダクト共通データ基盤、MLOps、ABテストなど、今後も変えていきたいことがたくさんあります。
今後の施策や、開発チームについてご興味あれば、ぜひお話しさせてください!