「マージ地獄」という名の悪夢とGit Flowの限界
想像してみてください。1週間、個別のフィーチャーブランチでコーディングに励みました。ローカル環境では完璧に動作しています。いよいよリリース準備のために main ブランチにマージしようとしたその時……「ドカン」! 赤いコンフリクトが何百行も表示されます。午後の時間はすべてコードの「拾い出し」で消えてしまいました。さらに悪いことに、マージ後、予期せぬ変更の衝突によってシステムがダウンしてしまいます。
feature、develop、release など、多種多様なブランチを使い分けるGit Flowは、かつて黄金の標準でした。しかし、真のCI/CD(継続的インテグレーション/継続的デリバリー)を実現する上で、生存期間の長いブランチを維持することは最大の障壁となります。リリースの速度を不必要に遅らせてしまうのです。そこで救世主となるのが Trunk-based Development (TBD) です。
以前のチームでは、メンバーが10人に増えた頃からGit Flowの煩雑さが目立ち始めました。リリースのたびに各サブチームのマージが重なり、まさに苦行でした。Trunk-basedに移行した後、コンフリクト解消にかかる時間は週4時間から15分未満に短縮されました。チームはいつでも自信を持って本番環境へデプロイできるようになりました。
Trunk-based Developmentとは一体何か?
枝分かれさせるのではなく、すべての開発者が単一のブランチ(通常は main または master)で作業を完結させる手法です。このブランチを Trunk(幹)と呼びます。
主な導入方法には2つのパターンがあります:
- 小規模チーム向け: 開発者が
mainブランチに直接コミットし、プッシュします。 - 大規模チーム向け: 開発者が極めて短命なフィーチャーブランチ(short-lived branches)を作成します。これらのブランチは数時間から長くても1日だけ生存し、コードレビューを経てすぐに
mainにマージされます。
核心となる哲学は非常にシンプルです。「早期かつ頻繁な統合」です。コードを手元に長く留めてはいけません。最小単位の機能が完成したら、すぐにTrunkへプッシュしましょう。
TBDを成功させるための3つの柱
ソースコードを壊さずにTBDを適用するには、従来のGit Flowよりもはるかに厳格なルールセットが必要です。
1. タスクの細分化 (Atomic Commits)
2000行もあるPR(プルリクエスト)をTrunkに投げてはいけません。機能を極小のタスクに分割しましょう。1回のコミットで解決するのは1つの課題だけにします。ある機能の実装に3日かかるなら、それを5〜6回の小さなマージに分ける方法を考えてください。
# すべてが終わるのを待つのではなく、まずは骨組みをマージする
git checkout -b feat/api-user-schema
# スキーマ定義とバリデーションルールのコードを書く...
git commit -m "feat: add user schema and validation rules"
git push origin feat/api-user-schema
# PRを作成し、30分以内に main へマージする
2. Feature Flags – 万能な「スイッチ」
作りかけのコードを main にマージして、ユーザー体験を損なわないようにするにはどうすればいいでしょうか? その答えがFeature Flagsです。新しいコードを if 文の条件の中に閉じ込めます。
# 決済ロジックでの実際の例
from feature_flags import is_enabled
def process_payment(user):
if is_enabled("use_new_stripe_adapter", user):
# テスト中の新しいロジック
return stripe_v2_adapter.process(user)
else:
# 安定稼働中の古いロジック
return paypal_legacy_adapter.process(user)
Feature Flagsのおかげで、コードを毎日本番環境にプッシュしながら、非表示にしておくことができます。QAが実環境でのテストを終えたら、ダッシュボードでスイッチを切り替えるだけです。コードを再デプロイする必要はありません。
3. 「妥協なき」CIシステム
TBDにおいて、main ブランチは常にリリース可能な状態でなければなりません。テストを極めて高速に自動実行するCIパイプライン(GitHub Actions、GitLab CIなど)が不可欠です。
私の経験から:ブランチ保護 (Branch Protection) を設定しましょう。テックリードであっても、テストが100%パスしなければ main へのマージは許されません。もしビルドが壊れたら、チーム全員にとっての最優先事項はビルドの修正であり、新しいコードを書くことは一旦ストップします。
なぜ今すぐTBDに切り替えるべきなのか?
TBDを導入した多くのプロジェクトを経て、私が実感した最も顕著な変化は以下の通りです:
- バグの即時発見: コードが常に統合されるため、ロジックの衝突があればCIがすぐに通知します。スプリントの終わりまで、同僚のコードを壊してしまったことに気づかないという事態は避けられます。
- 明快な設計思考: Feature Flagsを使うためには、コードをモジュール化し、コンポーネントを分離せざるを得ません。これが結果的に、システムアーキテクチャをクリーンでメンテナンスしやすいものにします。
- デリバリー速度の飛躍的向上: 煩雑なマージプロセスを排除することで、待ち時間(リードタイム)が短縮されます。コーディングが終われば、すぐにステージングや本番環境へ反映可能です。
避けるべき落とし穴
TBDは強力ですが、以下の3つのミスを犯すと逆効果になる可能性があります:
- ユニットテストを怠る: テストカバレッジが不十分なプロジェクトでTBDをやってはいけません。自動フィルタリングなしに
mainへプッシュし続けるのは、自殺行為に等しいです。 - フィーチャーブランチの「放置」: ブランチが2日以上存在し続けるなら、それはもはやTrunk-basedではありません。あらゆる手段を講じて細分化してください。
- Feature Flagsによる技術的負債: 機能が安定した後は、
if/elseブロックを削除するのを忘れないでください。私のチームでは、コードが煩雑になるのを防ぐため、月末の午前中を使って古いフラグを掃除する時間を設けています。
結論:移行すべきか?
Trunk-based Developmentは単なるGitのテクニックではなく、仕事の進め方に対するマインドセットの変化です。スピードが求められるスタートアップや、パイプラインを最適化したいDevOpsチームであれば、ぜひTBDを試してみてください。
最初は、タスクの細分化やFeature Flagsの記述を面倒に感じるかもしれません。しかし、2〜3スプリントもすれば、「マージ地獄」の不安が消え、生産性が飛躍的に向上したことを実感できるはずです。リリースの速度はもはや障壁ではなく、チームの競争優位性へと変わるでしょう。
