トランクベース開発とは?小さく毎日マージする方法

要約

トランクベース開発とは、チーム全員が共有の main(trunk)へ小さな変更を1日1回以上マージする開発手法です。長命ブランチを避け、未完成の機能はフィーチャーフラグで隠します。DORA の研究では、この習慣が配信の速さと安定性に結び付いています。まずは1つの小さな変更を今日マージしてみてください。

暗いデスクのモニターに光る一本の縦線。共有トランクの比喩

トランクベース開発とは、チーム全員が1本の共有ブランチ(通常は main または trunk と呼ばれるもの)へ、小さな変更を少なくとも1日に1回マージしていく開発手法です。ブランチを使う場合も、寿命は数週間ではなく数時間です。完成していない作業はフィーチャーフラグの裏に隠して出荷するので、どのコミットでもトランクはリリースできる状態に保たれます。仕組みの中身はそれだけで、名前から想像するほど大げさではありません。

ここで、ひとつの場面を思い浮かべてください。3週間放置していたサイドプロジェクトを開きます。フィーチャーブランチは main より41コミット遅れていて、2つのファイルで衝突し、作っていた機能は周りのコードと噛み合わなくなっています。トランクベース開発は、この場面を起こさないための習慣です。

トランクベース開発が実際に意味すること

trunkbaseddevelopment.com の解説サイト は、開発者が1本の共有ブランチで協業し、長く残る開発ブランチを避けるソース管理モデルとして定義しています。とても小さなチームなら、トランクへの直接コミットも問題ありません。プルリクエストを使うのも構いませんが、フィーチャーブランチは短命で、1人の作業から生まれたものに限られている必要があります。

大事なルールは3つです。

逆に、何が無いかに注目してください。develop ブランチもなく、機能ごとの長いリリースブランチもなく、最後に全員がコードの噛み合わなさに気づく統合フェーズもありません。古いモデルで痛い目を見るのは、痛みを溜め込んでから一度にまとめて払うことになるからです。

太い1本の幹と短い枝だけの高い木

長命ブランチは借金だから、小さなチームほど避けたい

長命ブランチは借金のようなものです。ブランチが生きている1日ごとに、main から少しずつ離れていきます。その利子は、マージコンフリクト、古くなった前提、そして60ファイルに触れる差分のせいで終わらないレビューという形で返ってきます。

DORA の trunk-based development 解説ページ は、この習慣を、State of DevOps の調査に基づいて、配信の速さ・安定性・可用性の向上と結び付けています。リポジトリのアクティブなブランチは3本以下にする。1日に1回以上トランクへマージする。コードフリーズと統合フェーズを避ける。どれも測定できる具体的な目標で、この分野では珍しいことです。

ソロ開発者や3人のチームなら、理屈はさらにシンプルです。小さなマージはレビューしやすく、戻しやすく、半年後に22時の git blame を眺めながら「過去の自分は何を考えていたのか」と悩むときにも理解しやすい。

やる理由は3つ、やらない理由は1つです。やる理由は、差分が小さいこと、CI からの返事が速いこと、金曜日に main をデプロイしても落ち着いていられることです。やらない理由は、テストが薄いと、毎日のマージは壊れ方を速く広げるだけという点です。これは後で扱います。

短命ブランチか、main への直接コミットか

どちらもトランクベース開発に含まれます。違いは、コードが入る前にどれだけの安全策を求めるかです。

1人かペアで作業していて、ローカルのビルドが1分未満で終わり、テストを信頼できるなら、main への直接コミットが最速のループです。多くの個人開発者は、名前を付けていないだけで既にこれをやっています。

2人目、3人目が加わったときや、マージ前にクリーンな環境で CI を回したいときは、プルリクエストと短命ブランチが合っています。規律は「短命」という言葉に詰まっています。翌日まで生き残るブランチは、もう抜け出そうとしているモデルへ向かっています。

続けやすい手順は次のとおりです。

1日で終わらない変更なら、答えは長いブランチではありません。変更をもっと薄く切ることです。

完成していない作業を、main を壊さずに出荷するには

ここで多くの人が止まります。毎日マージするなら、まだ存在しない機能のコードも入れることになるからです。対処法は2つあります。

1つ目はフィーチャーフラグです。新しいコードパスはマージするが、スイッチはオフのままにして、ユーザーには見せません。フラグは環境変数の真偽値程度に単純で構いません。

const showNewCheckout = process.env.FEATURE_NEW_CHECKOUT === "true";

export function Checkout() {
  return showNewCheckout ? <NewCheckout /> : <OldCheckout />;
}

個人開発ならこれで十分です。対象の絞り込み、段階的なロールアウト、再デプロイなしのキルスイッチが必要になったら、マネージドサービスを使う価値が出てきます。

2つ目はブランチ・バイ・アブストラクションです。データベースクライアントや決済プロバイダの差し替えなど、大きな変更向けです。古い実装の前にインターフェースを置き、同じインターフェースの裏で新しい実装を作り、呼び出し元を少しずつ移してから古い実装を削除します。どのステップも、トランクへの小さなマージで、長いブランチに何かが残ることはありません。

オンとオフが混在する物理的なトグルスイッチのパネル。フィーチャーフラグのイメージ

フラグには注意点もあります。フラグは負債です。1年間コードに残ったフラグは、偽装されたブランチです。各フラグに担当者と削除日を決め、機能が完全に展開されたら削除してください。

トランクベース開発の前に、CI に必要なこと

安全網のないトランクベース開発は、速く壊れるだけです。毎日マージする習慣を始める前に、次の3つを用意しましょう。

静的解析は、安く追加できる防御の層です。明らかなバグ、セキュリティ上の臭い、重複コードなどを、人間が差分を読む前に捕まえてくれます。

レビュアーが自分ひとりなら、リスクの高い箇所を指摘してくれるツールはさらに役立ちます。何度も触っているファイルが少しずつ泥だらけになっていても、他の誰も教えてくれないからです。

初日から重いパイプラインが必要だと考える必要はありません。main をインストール、lint、テスト、デプロイする1つのワークフローファイルで、十分な出発点になります。最初のバージョンは動けば上出来で、最適化は後からできます。

リリースブランチ、ホットフィックス、それでも気をつける場面

トランクベース開発は、リリースブランチを禁じていません。リリース直前にトランクから枝を切り、固めて、出荷し、削除するチームもあります。別のチームはトランクから直接リリースし、バグが見つかったら先へ進む形で直します(fix forward)。週に数回デプロイするウェブアプリなら、先へ直すほうがほぼ常に単純です。

モバイルアプリや、ストア審査に時間がかかるものは事情が違います。すでにユーザーの手元にあるバージョンにパッチを当てるため、バージョンごとに短命のリリースブランチを残すことがあります。それでもルールは同じです。修正は先にトランクへ入れ、その後にチェリーピックします。逆順はありません。

ホットフィックスも同じ論理です。トランクで直して、デプロイして、終わり。トランクに戻されない別のホットフィックスブランチは、2つに分かれた履歴を生む典型的な原因です。

夜のカフェのテーブルに置かれた、暗いコードエディタの画面のノートパソコン

最初の1週間の現実的な進め方

1日目:今のリポジトリのブランチを確認します。開いているブランチを一覧にして、それぞれの経過時間を書き出してください。数日以上前のものは、マージするか削除する候補です。これだけで、摩擦がどこにあるか見えることが多いです。

2日目:フラグの仕組みを追加します。環境変数でも構いません。進行中の機能を1つ選び、その入口をフラグの裏に置いてから、今ある分をマージします。

3日目から5日目:習慣を変えます。作業を1日で入るサイズに切り、CI が通ったら1片ずつマージします。最初の数片は小さすぎると感じるはずですが、そうではありません。休眠中のデータベースカラムや、まだ使われていない関数を追加するコミットでも、安全で、レビュー済みで、すでに統合された立派な進捗です。

週の終わりには、2つのことがわかります。テストをどこまで信頼できるか、そしてどの部分が小さな変更を拒むのか。どちらも、長いブランチで作業しているだけでは見えてきません。

測るためにダッシュボードは要りません。ブランチがどれくらい生きているか、main がどれくらい赤くなるか、最初のコミットから本番までどれくらいかかるか、を見てください。ブランチの寿命が縮み、デプロイが退屈になっていけば、習慣は効いています。

フラグの裏に置いた機能が利用者にどう効くかを知りたいなら、実験ツールでロールアウトを実際の指標に結び付けられます。感覚ではなく数字で判断できます。

1人で開発しているなら、ノートに書き留めるだけでも十分です。1か月間、毎週、最初のコミットから本番までの時間を記録してください。1回の数字より、その傾向のほうが多くを教えてくれます。

乗り換えるべきか、待つべきか

1人、ペア、またはテストを半分信頼できて CI を改善の余地がある小さなチームなら、乗り換えてください。試すコストは、1週間の少し居心地の悪い小さなコミットだけです。長いブランチに居続けるコストは、コンフリクトと遅いレビューで払うことになり、協力者が増えるたびに大きくなります。

自動テストがまったく無く、戻す手段もないなら、待ってください。先にそこを直すべきです。テストのないトランクへ毎日マージしても、痛みが場所を変えるだけです。

完璧な方法ではありません。実用的な方法です。今のプロジェクトで次の小さな変更を1つ選び、今日マージしてみてください。どんな感じがするか、確かめてみましょう。今夜までに出荷できる、現在の機能の一番小さな切り口は何でしょうか。

よくある質問

トランクベース開発と GitFlow の違いは何ですか?
GitFlow は develop、リリース、ホットフィックスなど長めのブランチを使う枠組みです。トランクベース開発は1本の共有ブランチへ小さな変更を毎日マージします。ウェブアプリを頻繁にデプロイするなら、後者のほうがシンプルになりやすいです。
1日1回マージするのは現実的ですか?
変更を1日で終わるサイズに切れば現実的です。大きな機能は、フィーチャーフラグとブランチ・バイ・アブストラクションで小さく分けて進めます。
フィーチャーフラグは必ず必要ですか?
未完成の作業を隠す必要がなければ不要です。ただし、完成していないコードを毎日マージするなら、ほぼ必要になります。
個人開発でもトランクベース開発を使うべきですか?
多くの場合は使えます。テストを信頼でき、ビルドが速ければ、main への直接コミットが最速の方法です。
必要な CI はどの程度ですか?
すべての push で走るビルドと、本当に壊れたときに失敗するテストがあれば十分です。最初は1つのワークフローファイルで始められます。
リリースブランチは使ってはいけませんか?
使ってもかまいません。ただし修正は先にトランクへ入れ、そのあとでリリースブランチにチェリーピックします。すでに配布済みのバージョンを直すモバイルアプリで特に役立ちます。