少人数で複数のプロダクトを開発していると、ある地味な問題に毎日削られます。ブランチを切り替えるたびに、手が止まるのです。片方の作業を止めてもう片方を見る、レビュー指摘に対応するために今の作業を退避する——その一つひとつは数分でも、1日に何度も起きると、思考の連続性ごと奪われます。

この記事では、その「ブランチ切り替えコスト」を git worktree で物理的に消した運用を、実際に十数本の並行ワークツリーで複数プロダクトを開発している立場から整理します。git worktreeというコマンドの使い方の解説ではありません。少人数で並行作業を止めないための、開発体制の判断として書きます。

筆者(馬込)は、複数の自社プロダクト(TechMate・ExcelMate・SnapBuild)を少人数で開発・運用しています。人手が限られるほど、「手が止まる時間」は事業に直接効いてきます。増員でパワーを足す前に、今いる手を止めない仕組みで捌ける範囲は、思っている以上に広いというのが実感です。

なぜ「1つの作業ディレクトリ」だと手が止まるのか

多くのチームは、1つのクローン(作業ディレクトリ)の中で、git switch でブランチを切り替えながら開発します。プロダクトもタスクも少ないうちは、これで何の問題もありません。

問題は、並行するタスクが増えたときに起きます。

  • 機能Aを実装中に、別プロダクトのバグ修正が割り込む。今の変更を git stash で退避してから切り替える。
  • レビューで指摘が来た。指摘に対応するには、レビュー対象のブランチに戻る必要がある。今の作業をまた退避する。
  • ブランチを切り替えると、ビルドキャッシュや node_modules の状態が変わり、切り替えるたびに再ビルドが走る。

一つひとつは小さな操作です。でも、stash の積み重ねはいつか取り違えを生みますし、切り替えのたびの再ビルド待ちは、細切れの中断として1日を蝕みます。「手を動かす時間」より「文脈を切り替える時間」のほうが増えていく——少人数で並行タスクを抱えると、ここが最初のボトルネックになりました。

判断:切り替えるのをやめ、「並行を物理的に持つ」

そこで採った判断はシンプルです。ブランチを切り替えるのをやめ、タスクごとに独立した作業ディレクトリを物理的に持つ——これが git worktree です。

git worktree は、1つのリポジトリから複数の作業ディレクトリを同時に展開できる仕組みです。タスクAはAのディレクトリ、タスクBはBのディレクトリ。それぞれが別のブランチをチェックアウトした状態で同時に存在します。切り替えるのではなく、並べて持つ。作業を止めて退避する必要がそもそもなくなります。

# タスクごとに独立した作業ディレクトリを、専用ブランチで展開する
git worktree add ../snap-build.worktrees/<task-name> -b work/<task-name>

レビュー指摘が来ても、対象のワークツリーはそのまま残っているので、stash せずにそのディレクトリを開くだけ。割り込みが入っても、今のディレクトリはそのまま置いておける。中断が「退避と復元」ではなく「ウィンドウの切り替え」で済むようになりました。実際、常時十数本のワークツリーが並んでいる状態で、複数プロダクトの開発を並行させています。

この十数本という規模を現実的に回せているのは、実装の多くを AIコーディングエージェント(Claude Code など)と並走しているからでもあります。エージェントごとに独立したワークツリーで作業させれば、複数の実装を物理的に衝突させずに同時進行できます。つまり、ここで整理しているworktreeの運用ルールは、人間だけの並行作業ではなく、AIエージェントとの並行作業を前提に設計しているものです。手が人かAIかに関わらず、「並行を物理的に分ける」という原則は同じでした。

この構成は、複数プロダクトを1つのリポジトリ(モノレポ)にまとめている前提とも噛み合っています。モノレポにした判断そのものは複数プロダクトを1モノレポで運営する構成の判断にまとめています。

運用ルールを先に決めておく

worktree は便利ですが、無秩序に増やすと別の混乱を生みます。並行を止めないために、いくつかの運用ルールを先に決めておきました。

① mainの作業ディレクトリでは直接いじらない。 すべての改修は専用のワークツリーで行い、main は常にクリーンに保ちます。こうすると「今 main がどのタスクの途中なのか分からない」という事故が起きません。main はいつでも安心して参照・分岐できる基準点になります。

② ワークツリーの置き場所を、リポジトリの外(親ディレクトリ)に分離する。 一見、リポジトリ配下にまとめたくなりますが、あえて外に出しました。理由は、リポジトリの中に入れ子にすると、ripgrep や find、IDEのインデクサが main とワークツリーの両方を走査してしまい、検索結果が重複したり負荷が増えたりするからです。snap-build/ の隣に snap-build.worktrees/ を置く、という一段の分離が効きました。

③ マージしたら、ワークツリーとブランチを必ず片付ける。 これを怠ると、マージ済みのゴミが十数本たまり、「どれが生きているブランチか」が分からなくなります。片付けは、安全弁つきで機械的に回します。

# ブランチ先端が main の祖先=取り込み済みかを必ず確認してから消す
git merge-base --is-ancestor work/<task-name> main && echo MERGED
git worktree remove ../snap-build.worktrees/<task-name>
git branch -d work/<task-name>   # -d はマージ済みのみ削除する安全弁

git branch -d は、未マージのブランチを誤って消そうとすると拒否してくれます。-D(強制削除)は使わない、と決めておくだけで、「まだマージしていない作業を消してしまった」という最悪の事故を構造的に防げます。

ただし、この安全弁はsquashマージだと効きません。squashはコミットを作り直すため、元のブランチ先端が main の祖先になりません。--is-ancestorgit branch -d も「未マージ」と判定するので、結局 -D を使うことになります。squashで運用しているなら、コミットの系譜ではなく中身が main に入ったかで確認します。

# squash運用では、系譜ではなく「中身が入ったか」で確認する
files=$(git diff --name-only origin/main...HEAD)      # この枝で触ったファイル
git diff --quiet origin/main HEAD -- $files && echo MERGED

どちらの確認をするにせよ、消す前に必ず一度確認するという手順そのものは変わりません。変わるのは、マージの取り込み方に合わせて確認の方法を選ぶ、という点です。

つまずき——worktreeは「増やして終わり」ではない

正直に書くと、この運用にも代償があります。

ひとつは、ワークツリーごとに node_modules が重複すること。各ディレクトリが独立している以上、依存を別々に持てば、ディスクとインストール時間はそのぶんかかります。

ただ、これは運用で減らせました。依存を入れるのは、実際にビルドや動作確認をするワークツリーだけにしています。記事の執筆や設定の修正だけで完結する作業には、そもそも node_modules が要りません。実際、常時並んでいる十数本のうち、依存を持っているのは数本だけです。「並行を物理的に持つ」ことと「全部を動かせる状態にしておく」ことは別問題で、そこを分けてからディスクの心配はほとんど無くなりました。

もうひとつが本質的で、片付けを怠るとすぐ散らかること。並行を物理的に持てる自由は、そのまま「放置すると無限に増える」リスクでもあります。だから前述の「マージしたら必ず片付ける」を、感覚ではなくルールとして回す必要がありました。worktree は仕組みですが、その仕組みを回す規律まで含めて、初めて並行開発の武器になります。

これは「人を増やす前」の体制の工夫

大事なのは、これが増員ではなく、今いる手を止めないための工夫だという点です。少人数で複数プロダクト・複数タスクを抱えると、まず削られるのは「手が止まる時間」です。そこを仕組みで消せば、同じ人数で捌ける並行度は確実に上がります。

ただし、これにも限界はあります。切り替えコストを消しても、そもそもの実装量が人数の限界を超えれば、並行を止めない工夫だけでは追いつきません。そのときは、体制そのものを外部の手で広げる判断が必要になります。開発チームをどう外部で拡張するかは開発チームを外部で拡張する方法、キャパを超えたときの動き方は開発キャパが限界を超えたときの選択肢で整理しています。仕組みで伸ばせるところまで伸ばし、それでも足りない分を体制で補う——この順序が、少人数開発では効きました。

TechMateの開発体制づくり支援

私たちTechMateは、こうした「少人数で開発を止めない体制づくり」から、実装の伴走までを、月単位の準委任でまとめて支援しています。並行開発の運用設計だけでなく、手が足りない部分にはチームとして入って一緒に手を動かします。開発体制や、増員すべきか仕組みで捌くかの判断に迷う段階でも構いません。こちらからご相談ください。

よくある質問

Q. git worktreeは、少人数のチームでも導入する意味がありますか? A. むしろ少人数ほど効きます。人数が少ないと、一人が複数のタスク・複数のプロダクトを並行で持つ場面が増えるためです。切り替えコストは人数が少ないほど一人に集中するので、それを消せる恩恵も大きくなります。大人数で1人1タスクに専念できる体制なら、恩恵は相対的に小さくなります。

Q. ワークツリーは何本くらいまで持てますか? A. 技術的な上限より、片付けの規律が追いつくかが実質的な上限です。私たちは常時十数本を並行させていますが、これは「マージしたら必ず消す」運用があって初めて成立しています。片付けをしなければ、数本でもすぐ「どれが生きているか分からない」状態になります。本数より運用が先です。

Q. worktreeを使えば、チームの並行開発の問題はすべて解決しますか? A. 解決するのは「一人が並行作業を切り替えるコスト」です。複数人でのコンフリクトや、タスクの割り振り・進捗の可視化といった問題は別の仕組みが要ります。タスクとコミットを結びつけて開発を可視化する運用はタスク駆動開発の運用にまとめています。worktreeは「手を止めない」ための道具で、「チームを動かす」ための道具とは役割が違います。


複数プロダクトの並行開発で手が止まる原因は、実装力そのものではなく、ブランチを切り替えるたびに発生する文脈の退避と復元にありました。git worktreeで並行を物理的に持ち、置き場所と片付けのルールを決める。それだけで、同じ人数で止まらずに回せる並行度が上がります。そして、仕組みで伸ばしきれない分は、体制で補う——この順序が肝心でした。

少人数で複数プロダクトの開発をどう止めずに回すか迷う段階でも構いません。開発体制のご相談から、お気軽にお問い合わせください。