Gitブランチ戦略

Gitブランチ戦略

1. はじめに

Gitブランチは「機能ごとに分けるためのツール」程度に考えがちですが、実際の業務におけるブランチ戦略は、協業方法、開発環境へのデプロイとテスト、QA検証、運用環境へのデプロイ、hotfix対応まで直接つながっています。複数人が同時に開発し、決められたスケジュールに合わせてデプロイしなければならない環境では、特にブランチ戦略が単なるGitの使い方を超えて、チームの仕事の進め方そのものになります。

最初は、Git FlowやGitHub Flowのような名前の付いた戦略を基準に、実務でのブランチ構成を理解しようとしました。しかし、実際のプロジェクトで目にしたブランチ戦略は、教科書と完全に一致するものではありませんでした。feature、develop、stage、prod、releaseなどのブランチがあっても、実際の運用方法は、デプロイ周期、QA方式、人数規模、CI/CD環境、承認手順に応じて変形していました。

この記事では、代表的なブランチ戦略を簡単に整理したうえで、実際に経験した環境ブランチとデプロイ候補ブランチの構成を中心に説明します。この戦略を自分で設計したわけではありませんが、featureブランチを作成し、開発ブランチにマージしてテストし、デプロイ候補ブランチに機能を集めて運用環境へデプロイする流れを経験する中で、そのメリットと注意点を理解できました。

この経験で最も強く感じたのは、「自分たちのチームはGit Flowを使っているのか、GitHub Flowを使っているのか」という名前よりも、各ブランチの役割が明確か、マージ方向が一貫しているか、デプロイ基準と同期手順をチームメンバーが同じように理解しているかどうかのほうが、はるかに重要だということでした。

2. 代表的なGitブランチ戦略

2.1 Git Flow

Git Flowは、ブランチの役割を明確に分けるリリース中心の戦略です。mainまたは以前の名称であるmasterは、運用環境へデプロイ可能なリリース履歴を管理し、developは次回のデプロイに向けた統合ブランチの役割を担います。feature/*は機能開発、release/*はデプロイ前の安定化とバージョン準備、hotfix/*は運用環境での緊急修正に使用します。

feature/* -> develop -> release/* -> main
hotfix/*  -> main, develop 또는 현재 release/*

明示的なバージョンリリースがある場合や、リリース前に別途安定化および検証の段階が必要な場合、複数のバージョンを維持する必要があるプロジェクトによく適しています。ただし、ブランチが多く、マージ方向も定められているため、チーム内でのルール共有、ブランチ保護、自動化が重要になります。すべてのプロジェクトにデフォルトのように適用するのではなく、まずリリース方式やチーム規模に適しているかを判断する必要があります。

2.2 GitHub Flow

GitHub Flowは、mainブランチを中心とするシンプルな戦略です。機能開発は短期間のfeatureブランチで行い、Pull Requestによるレビューとテストを経てmainにマージします。重要なのは、mainに入ったコードが常にデプロイ可能な状態でなければならないという点です。

feature/* -> Pull Request / CI / review -> main -> deploy

実務では、mainにマージされると即座に自動デプロイされるチームもあれば、手動承認や段階的なデプロイを経るチームもあります。そのため、「mainにマージすれば必ず即座にデプロイされる」というより、「mainにマージされたコードはいつでもデプロイ可能でなければならない」と理解するほうが正確です。

シンプルでPR中心の協業によく適していますが、まだユーザーに公開してはいけない機能は、feature flag、権限制御、設定値、段階的なrolloutなどの方法で制御する必要があります。mainが壊れるとデプロイフロー全体が止まるため、自動テストとブランチ保護ルールも重要です。

2.3 GitLab Flow

GitLab Flowは、GitHub Flowのシンプルなfeature branchからMerge Requestを経てmainにマージする流れを基盤とし、プロジェクトのデプロイ方式に応じてproductionブランチ、stagingブランチ、releaseブランチ、タグ、CI/CD環境を組み合わせ、運用フローを表現する方式です。

예시 1: feature/* -> main -> staging -> production
예시 2: feature/* -> main -> release/x.y -> production deploy

Git Flowよりシンプルでありながら、GitHub Flowよりもデプロイおよび検証の段階を明確に表現できます。QA、staging、UAT、productionのように、環境ごとの検証段階が重要なプロジェクトで有用です。

ただし、GitLab Flowが常にstagingまたはproductionブランチを必ず設けるという意味ではありません。GitLab CI/CDのenvironment機能だけでもQA、staging、production環境を管理できますし、リリースブランチやタグを基盤としたデプロイを選択することもできます。つまり、環境ブランチはGitLab Flowの代表的な形の一つであり、必須条件ではありません。

2.4 Trunk-Based Development

Trunk-Based Developmentは、一つの中心ブランチ、通常はmainまたはtrunkに変更を頻繁に統合する戦略です。ブランチをまったく使わないという意味ではなく、長期間存続するブランチを避け、小さな単位の変更を短期間のブランチまたは直接コミットによって迅速に統合することが核心です。

main 또는 trunk
└─ short-lived branch -> main 또는 trunk

trunkは常にビルド可能かつデプロイ可能な状態を維持する必要があるため、高速なCI、自動テスト、小さな作業単位、迅速なコードレビューが重要です。大きな機能を一度に長期間隠しておくのではなく、feature flag、branch by abstraction、設定値による制御などを活用し、未完成の機能がユーザーに公開されないよう管理します。

CI/CDが十分に整備され、自動テストを信頼できる組織によく適しています。反対に、テストが遅かったり、手動検証への依存度が高かったり、機能ブランチが長期間維持されたりする組織では、trunkを安定して維持することが難しくなります。

3. 実務で定石の戦略がそのまま適用されない理由

実際のプロジェクトでは、一つの戦略がきれいに適用されるよりも、複数の戦略の要素が混在する場合が多くあります。ブランチ戦略は単なるGitの使い方の問題ではなく、デプロイ周期、QA方式、運用障害への対応、チームの人数、CI/CDの成熟度、業務上の承認手順すべてとつながっているためです。

たとえば、develop、stage、prodブランチがあるからといって、必ずしもGit Flowとは限りません。stagingやproductionブランチがあるからといって、常にGitLab Flowだと断定することもできません。mainからfeatureブランチを作成して再びmainにマージするからといって、必ずしもTrunk-Based Developmentではありません。Pull Requestを使っているからといって、GitHub Flowになるわけでもありません。PRはブランチ戦略そのものではなく、コードレビューとマージのための協業方式です。

実務でブランチ戦略を把握する際には、名前よりも、次の質問に答えられるかどうかのほうが重要でした。

  • 運用環境へのデプロイ基準となるブランチは何かを確認する必要があります。
  • 開発環境と検証環境にそれぞれデプロイされるブランチが何かを確認する必要があります。
  • featureブランチはどこから作成され、どこに最初にマージされるのかを確認する必要があります。
  • 今回のデプロイに含めるfeatureは、どのような方法で選択されるのかを確認する必要があります。
  • 運用環境へのデプロイ後、開発・検証ブランチはどのように同期されるのかを確認する必要があります。
  • hotfixはどのブランチから開始し、どのブランチへ戻すのかを確認する必要があります。

これらの質問への答えが明確なとき、チームメンバーは同じ基準で動くことができました。反対に、ブランチ名は馴染みのあるものでも、実際の役割が曖昧であれば、開発ブランチに入った機能がすべてデプロイされるのか、検証ブランチが運用候補なのか単なるテスト用なのかといった点で、誤解が生じる可能性がありました。

4. 実務経験:環境ブランチとデプロイ候補ブランチの構成

私が経験したプロジェクトには、運用ブランチ、開発ブランチ、検証ブランチが存在していました。実際のブランチ名はプロジェクトごとに異なる可能性があるため、この記事では名前ではなく役割を基準に説明します。

  • 運用ブランチは、実際の運用環境へのデプロイ基準となるブランチです。通常は保護ブランチとして設定し、直接pushすることを制限します。
  • 開発ブランチは、開発環境へのデプロイおよび統合テスト用のブランチです。運用環境へのデプロイ対象全体を意味するものではありません。
  • 検証ブランチは、QA、staging、pre-productionなど、運用前の検証環境にデプロイされるブランチです。
  • featureブランチは、個別の作業のためのブランチです。選択的なデプロイを考慮すると、作業の原本を安全に保存する役割も担います。
  • デプロイ候補ブランチは、運用環境に出すfeatureだけを集め、最終検証を行うための一時的なブランチです。

核心は、運用環境へのデプロイ前に別途デプロイ候補ブランチを作成し、今回のデプロイ対象となるfeatureだけを集めたという点です。開発ブランチに入ったすべての機能が今回の運用環境へのデプロイに含まれる構成ではなかったため、運用ブランチを基準にデプロイ候補ブランチを作成し、デプロイ範囲を明示的に制御しました。

この構成を定石のGit Flowだと断定するのは難しいでしょう。GitLab Flowのように環境ブランチを活用する側面もあり、Git Flowのreleaseブランチのように運用前の安定化ブランチを設ける側面もあります。ただし、developからreleaseブランチを作成する典型的なGit Flowとは異なり、運用ブランチを基準にデプロイ候補を作成し、選択したfeatureだけを集めたという点で、実務的なハイブリッド構成に近いものです。

4.1 一般的な開発フロー

各開発者は、運用ブランチまたはチームで定めた最新の基準ブランチからfeatureブランチを作成し、開発が終わると開発ブランチにマージして、開発環境でテストしました。ここでいう基準ブランチは、チームのポリシーによって異なる場合があります。重要なのは、どのブランチから開始し、どのブランチに最初にマージするのかを、チーム全体が同じように把握していなければならないという点です。

feature/A -> 개발 브랜치 -> 개발계 배포 및 테스트
feature/B -> 개발 브랜치 -> 개발계 배포 및 테스트
feature/C -> 개발 브랜치 -> 개발계 배포 및 테스트

ここで重要なのは、開発ブランチが運用環境へのデプロイ基準ではないという点です。開発ブランチに入ったすべての機能が、今回の運用環境へのデプロイに含まれるわけではありませんでした。

개발 브랜치 = 운영 코드 + feature/A + feature/B + feature/C
이번 배포 대상 = feature/A + feature/B

上記のような状況で開発ブランチをそのまま運用環境へマージすると、feature/Cも一緒にリリースされることになります。そのため、featureブランチは単なる作業スペースではなく、デプロイ範囲を選択できる単位としても管理する必要がありました。開発ブランチにマージしたからといってfeatureブランチの原本をすぐに削除したり、作業履歴を失ったりすると、選択的なデプロイの際に再び集めることが難しくなります。

4.2 デプロイ候補ブランチの作成

デプロイ日が近づくと、運用ブランチを基準にデプロイ候補ブランチを作成し、今回のデプロイに含めるfeatureだけを一つずつマージしました。チームによってはmergeを使用することもできますし、必要なコミットだけを選別することもできます。どの方法を使う場合でも、重要なのは、運用環境に出す変更だけをデプロイ候補ブランチに含めることです。

개발 중: feature/A, feature/B, feature/C -> 개발 브랜치 -> 개발계 테스트
배포 준비: 운영 브랜치 -> 배포 후보 브랜치 <- feature/A, feature/B
최종 흐름: 충돌 해결 -> 의존성 확인 -> 검증 환경 테스트 -> 운영 PR -> 운영 배포

この構成の最大のメリットは、デプロイ範囲を明確に制御できることです。開発ブランチにどのような機能が混在していても、運用環境に出す機能だけをデプロイ候補ブランチに集めることができます。

ただし、feature間に依存関係がある場合は注意が必要です。たとえば、feature/Bが内部的にfeature/Cのコードに依存している場合、Bだけをデプロイ候補に含めてCを除外することは、不可能または危険になる可能性があります。選択的なデプロイが必要な構成では、featureを独立して分割する設計、依存関係の確認、feature flag戦略を併せて用意する必要があります。

4.3 統合、検証、運用環境への反映

リリース候補ブランチで最も注意すべきだったのは、conflictと最終検証でした。featureブランチは作業領域を分離するだけで、統合コストをなくしてくれるわけではありません。共通コンポーネント、ルーティングファイル、型定義、設定ファイルなどを複数のfeatureが同時に変更すると、リリース候補ブランチに集約する時点でconflictが発生する可能性があります。

また、開発ブランチでテストした組み合わせと、リリース候補ブランチの組み合わせが異なる場合があります。例えば、開発ブランチではA+B+Cの組み合わせでテストしていても、本番候補にはA+Bだけが含まれることがあります。したがって、最終検証は開発ブランチではなく、実際に本番へリリースするリリース候補ブランチを基準に行う必要があります。

検証が終わると、リリース候補ブランチを運用ブランチにマージするPull Requestを作成しました。このPRは単なるマージ手順ではなく、今回のリリースに何が含まれるのかを確認する基準点として機能しました。含まれるfeature、除外されたfeature、conflictの解決内容、検証結果、リリース設定の変更有無、rollback基準が一か所に整理されていれば、リリースの安定性が高まります。

まとめると、リリース候補ブランチは「リリースする機能を集めておく一時的なブランチ」であると同時に、「本番リリース前の最終検証基準」です。したがって、リリース直前に急いで作るよりも、十分な検証時間を確保して作成するほうが安全です。

4.4 リリース後の同期とhotfix

本番リリースが終わった後は、運用ブランチを基準に開発ブランチと検証ブランチを再び合わせる工程がありました。そうすることで、リリース直後の基準点が明確になり、ブランチ間の差分が継続的に蓄積する問題を減らせます。このとき、運用ブランチの変更内容を開発ブランチと検証ブランチへ反映することもできますし、チームのポリシーに応じて、特定の環境ブランチを運用基準に再度合わせることもできます。

ただし、共有ブランチをforce pushまたはresetで初期化する方法は危険なので、例外的にのみ使用すべきです。リモートブランチを合わせるだけでは終わらず、チームメンバーのローカルブランチも併せて再同期する必要があるためです。コマンドそのものより重要なのは、チームのポリシーと周知です。基準が不明確だと、リリース後の同期中に削除したコードが再び復活したり、逆に必要な作業が失われたりする可能性があります。

hotfixの流れも別途定める必要があります。本番障害や緊急修正は通常のfeatureの流れとは分けて考えるべきで、一般的には運用ブランチからhotfixブランチを作成し、修正と検証が終わったらまず運用ブランチに反映します。その後、同じ修正が次の開発フローから消えないように、開発ブランチ、検証ブランチ、作業中のリリース候補ブランチにも反映する必要があります。hotfixにはリファクタリングや別の機能変更を混在させず、範囲を最小限にするほうが安全です。

4.5 この構造はどのような戦略と考えられるか

私が経験した構造は、純粋なGit Flow、GitHub Flow、GitLab Flow、Trunk-Based Developmentのいずれか一つだと明確に分類するのは困難です。開発ブランチと運用ブランチが分離されている点はGit Flowに似ており、検証ブランチや運用ブランチのように環境をブランチで表現する点はGitLab Flowに似ています。リリース候補ブランチを作成して本番前の安定化と検証を行う点は、releaseブランチ戦略にも類似しています。

しかし、開発ブランチ全体をreleaseに送るのではなく、運用ブランチを基準に選択したfeatureだけを集める点で、典型的なGit Flowとは異なります。また、featureブランチがリリースまで維持され、リリース候補ブランチで最終統合される点で、Trunk-Based Developmentとも異なります。したがって、この構造は環境ブランチとリリース候補ブランチを組み合わせた、選択的リリース型のハイブリッド戦略と理解するのが最も正確でした。

5. メリットと注意点

5.1 メリット

  • リリース範囲を明確に制御できます。開発ブランチに複数のfeatureが混在していても、リリース候補ブランチには今回のリリース対象だけを集めることができます。
  • 開発環境でのテストと本番リリースを分離できます。featureブランチを開発ブランチにマージして開発環境で先にテストし、本番前にはリリース候補ブランチで実際のリリース構成を個別に検証します。
  • 本番リリースの単位が明確になります。1つのリリース候補ブランチが今回のリリース範囲を示すため、どの機能が含まれるのかを追跡しやすくなります。
  • 運用ブランチを安定した基準点として維持できます。運用ブランチからリリース候補ブランチを作成すれば、開発ブランチに混在している未リリース機能が意図せず本番に含まれるリスクを減らせます。
  • リリース後の基準点が整理されます。運用ブランチを基準に開発・検証ブランチを合わせれば、次の作業を現在の本番コードから再開しやすくなります。

5.2 注意点

  • 統合コストがリリース直前に集中する可能性があります。複数のfeatureで共通ファイルを変更すると、リリース候補ブランチを作成する時点にconflictが集中します。
  • 開発ブランチでのテスト構成と、本番候補の構成が異なる場合があります。最終検証は必ずリリース候補ブランチを基準に行う必要があります。
  • feature間に隠れた依存関係があると、選択的リリースが破綻する可能性があります。Aだけをリリースするつもりでも、実際にはBやCのコードに依存していることがあります。
  • mergeやcherry-pickなどの反映方法が混在すると、履歴の追跡が難しくなる可能性があります。チームでどの方法を使うのか、明確に決めておく必要があります。
  • 共有ブランチの初期化後にローカル同期を忘れると、削除したコードが再び復活する可能性があります。共有ブランチを操作する際は、事前の周知とチーム単位の手順が必要です。
  • 未完了の作業はfeatureブランチに安全に残しておく必要があります。開発ブランチが初期化される可能性がある構造で、作業の原本を開発ブランチだけに残しておくのは危険です。
  • 隠す必要がある機能は、ブランチだけで管理しないほうがよいでしょう。長期間公開しない機能については、feature flagや権限制御も併せて検討する必要があります。

5.3 チームルールとして残すべき項目

ブランチ戦略は図や名前ではなく、運用ルールとして残しておくことで、ミスの可能性を減らせます。私が経験した構造であれば、すべての項目を長く文書化するよりも、実際に混乱しやすい基準を短く明確に定めておくほうがよいでしょう。

  • 各ブランチの役割と保護の有無
  • featureブランチを作成する基準ブランチと、最初にマージする対象ブランチ
  • 開発環境、検証環境、本番環境へのリリース基準
  • リリース候補ブランチを作成するタイミングと、含めるfeatureの選定方法
  • 運用ブランチへのPR承認条件とリリースチェックリスト
  • 本番リリース後に開発・検証ブランチを同期する方法
  • hotfixの開始ブランチ、マージ対象、後続の同期手順
  • feature flagの使用基準と未リリース機能の管理方法

6. まとめ

Gitのブランチ戦略に唯一の正解はありません。Git Flow、GitHub Flow、GitLab Flow、Trunk-Based Developmentはそれぞれにメリットがある優れた基準ですが、実際のプロジェクトではリリース周期、QAの方法、運用上のリスク、CI/CDの成熟度、チーム規模に応じて変形されます。

私が経験した構造も、正統なGit Flowだと断定することはできませんでした。feature、開発、検証、運用、リリース候補の各ブランチが併用され、本番リリース後には環境ブランチを運用基準に再び合わせるポリシーもありました。理論上の戦略というより、実際のリリースの安定性と協業の効率を高めるために作られた、実務的な構造に近いものでした。

この経験で最も印象に残っているのは、2つあります。1つは、featureブランチは統合コストをなくしてくれるわけではないという点です。それぞれのブランチで作業していても、リリース前には1つのブランチに集約する必要があり、そのときにconflictが発生する可能性があります。「どこで作業するか」と同じくらい、「いつ統合するか」と「どこで最終検証するか」が重要です。

もう1つは、ブランチ戦略の本質は名前ではなく、ルールの明確さにあるという点です。各ブランチの役割、マージの方向、リリース基準、リリース後の同期手順が、チームメンバー全員に同じ内容で共有されていなければなりません。優れたブランチ戦略とは、有名な戦略をそのまま採用することではなく、自分たちのチームの状況に合わせて一貫性を持って運用される戦略です。

参考

- Git Flow: Vincent Driessen, nvie.com

- GitHub Flow: GitHub Docs

- GitLab Flow: GitLab公式サイト、GitLab Docs

- Trunk-Based Development: TrunkBasedDevelopment.com

- Trunk-Based Development: Atlassian

green

Site footer