みなさんはチーム開発をするとき、どのようにブランチを切っていますか?
Gitのブランチ戦略としてはgit-flowやGitHub flowなどが有名ですね。
Aipoの開発でもGitHub flowを採用し、masterブランチはいつでもデプロイが可能な状態に保っています。しかし運用してみるといくつか課題が出てきました。
目次
課題
ブランチの命名規則がルール化できない
GitHub flowには「説明的な名前のブランチをmasterから作成する」というルールがあります。しかし、説明的な名前を考えるのは意外に難しいものです。日本語で書かれている機能要件を各エンジニアが英語に翻訳してブランチ名をつけていたため、ネーミングセンスが問われます。
例えば「登録する」を翻訳するときに、英語としては意味が通じる「register」と「enter」のどちらを使うか、技術英単語としてはどちらが一般的か判断できるだけの知識と経験が求められます。
また、せっかく悩んでつけたブランチ名も開発途中で要件が変わりブランチ名と実態が合わなくなることも発生していました。
featureブランチが長期間放置される
GitHub flowでは新しい機能を開発するときにfeatureブランチを作成します。featureブランチをmasterにマージするタイミングでプルリクエストが発生します。
Aipoの開発スプリントは1週間ですが、規模が大きい開発になると数スプリントのあいだ、featureブランチがオープンしたままです。スプリントを重ねるうちにfeatureブランチの変更内容が大きくなり、masterにマージするころにはレビューの負担が非常に大きくなります。レビューにも時間がかかってしまうため、なかなかリリースされない状況でした。
WIP,Draft Pull Requestではだめなのか
featureブランチが大きくなってしまう問題の解決方法としてWIPやDraft Pull Requestが挙げられることがあります。WIPやDraft Pull Requestを使うことで、コードを書く前にやることをリストアップし、進捗状況を可視化できます。
この方法は経験年数やスキルにばらつきのあるエンジニアで構成されているチームだとみんなが同じレベルを保つことが難しいと考えています。
これらの課題を解決するため、現在はGitHub flowを少しだけアレンジしたブランチ戦略をとっています。
前提
前提としてAipoの開発、リリース体制についてまとめておきます。
- スクラムで開発
- 1スプリントは1週間
- できたものからリリースしてユーザーの反応を見る
- リリース頻度は1週間に2回ほど
- 小さくリリースするためデプロイ環境によってブランチを分ける必要がない
プロダクトの環境は4つ
目的に合わせ、4つの環境を用意しています。
環境名 | 用途 |
---|---|
開発環境 | エンジニアが開発するための環境 |
テスト環境 | 動作検証をするための環境 masterの更新で自動デプロイ |
ステージング環境 | リリース直前に最終的な動作確認を行う環境 BeanstalkでBlue/Green デプロイを実施 |
本番環境 | 実際にユーザーがアクセスする環境 |
GitHub flowを改良した点
ブランチの命名規則をシンプルに
AipoではBacklogを使っていますが、Backlogのチケット番号をブランチ名にしています。(例:PRODUCT-1234)
これでブランチ名で悩むことがなくなりました。
エピックブランチを導入
「年90回以上のアップデートを実現する、Backlogを使ったスクラム開発」で紹介しているように、Backlogの「エピック」と「タスク」を使って開発をしています。
エピックブランチとは「エピック」用に作成したブランチから「タスク」ごとにブランチを作成し、「タスク」が完了するごとに「エピック」用のブランチにマージを行っていく運用方法です。
手順
具体的な例を見てみましょう。大きな機能の中にいくつもの機能があり、その機能を開発していくことを考えてみます。例えばこんな感じです。
- ブログ機能(「エピック」チケット番号:PRODUCT-1)
- Modelの作成(「タスク」チケット番号:PRODUCT-2)
- 登録機能(「タスク」チケット番号:PRODUCT-3)
- 編集機能(「タスク」チケット番号:PRODUCT-4)
- 一覧機能(「タスク」チケット番号:PRODUCT-5)
- 詳細機能(「タスク」チケット番号:PRODUCT-6)
- 削除機能(「タスク」チケット番号:PRODUCT-7)
1.エピックブランチの作成
まずはmasterからエピックブランチを作成します。(ブランチ名:PRODUCT-1)
2.タスクブランチの作成
ここではModelを作成しないと始まりませんので、エピックブランチ(ブランチ名:PRODUCT-1)からブランチを作成します。
このときブランチ名の命名ルールとして「親のブランチ名_子のブランチ名」のようにしておきます。(ブランチ名:PRODUCT-1_PRODUCT-2)
このようにすることでブランチの親子関係が見えやすくなります。同じエピックにひもづくブランチは同じPrefixになるためGitHub上でマージ漏れも見つけやすくなります。
3.プルリクエストの作成
Modelの作成が終わってレビューを依頼する際にはmasterではなくエピックブランチ(ブランチ名:PRODUCT-1)に対してプルリクエストを出します。
4.レビューとマージ
レビュアーはプルリクエストの内容をレビューして問題がなければエピックブランチにマージします。
タスクごとにプルリクエストのレビューをするため、レビューの負担を最小限にできます。
マージが完了したブランチ(ブランチ名:PRODUCT-1_PRODUCT-2)は削除します。
あとはこれを繰り返していくことで、エピックブランチをmasterにマージする際にはレビューがほぼ不要になります。
スプリントで確実に成果を積み上げていくためには、1スプリント内でレビューまでを完結させる必要があります。「エピック」の単位だと難しいですが、「タスク」単位まで落とし込むことで実現が可能です。
ルール
エピックブランチをシンプルな運用にするため以下のようなルールにしています。
- プルリクエストは親に対してのみ出す
- コンフリクトを回避するためブランチは常に親子関係のみにする
- 基本フローはGUIのみで操作できるようにする
もしもレビューが進まなかったときには?
今回紹介したケースの場合、Modelの作成が完了(レビューが終わってエピックブランチにマージが完了)しなければ登録機能の開発に着手できません。
もしもレビューが終わる前に登録機能を作る場合には、Modelの作成のブランチからブランチを作るようにします。このときのブランチ名の命名ルールも「親のブランチ名_子のブランチ名」にしています。(ブランチ名:PRODUCT-1_PRODUCT-2_PRODUCT-3)
レビューがたまるほどブランチ名が長くなっていくことになるので、プルリクエストからレビューまでのサイクルを短くする意識が働きます。
根底にある考え
しくみはいくらでも複雑にできます。しかし、複雑になることで学習コストが高くなっていきます。
- 他のfeatureブランチとの整合性を意識する
- デプロイ環境ごとにどのブランチが適用されているか意識する
- リリースの順番を意識する
などを考えなくていいように、シンプルなしくみにすることでエンジニアの負担を減らしたいと考えています。
一緒に働く仲間を募集しています。
新卒採用・中途採用を問わず、年間を通して、さまざまな職種を募集しています。「すぐに仕事がしたい」「話を聞いてみたい」「オフィスを訪問してみたい」など、ご応募をお待ちしています。共に未来をカタチにする仲間を待っています。