はじめに
AIにコードを書いてもらうようになって、困ったのは書いたコードの読む量でした。
「この機能を作って」と頼むと、DBもAPIも画面も、依存する先までまとめて作ってくれます。ありがたい反面、変更されるファイルが増えれば増えるほど、人の目でレビューするのが難しくなっていきました。
そこで今は、先に「レビューできる大きさ」で作業を区切っています。区切りごとにタスクを分けてissueを作り、git worktreeで作業フォルダも分けています。この記事では、worktreeの仕組みと、私が実際にやっている使い方をまとめます。
そもそもworktreeって何?
git worktreeは、1つのリポジトリから作業フォルダを何本も出せる機能です。
普通はリポジトリ1つに作業フォルダも1つで、別のブランチを見たいときはgit switchで中身を入れ替えます。worktreeを使うと、ブランチごとに別のフォルダを用意して、並べて開いておけます。
~/Documents/
├── myapp/ ← 本体
└── myapp-wt/
├── feature-db/ ← worktree(ブランチ feature/xxx-db)
└── feature-api/ ← worktree(ブランチ feature/xxx-api)
ここで大事なのは、履歴は本体と共有していることです。cloneを何回もするのと違い、コミットもブランチも1か所にあるので、worktreeでコミットしたものは本体からもそのまま見えます。
使うコマンドはほぼこれだけです。
# リモートの最新のデフォルトブランチから、ブランチを切って別フォルダに出す
git fetch origin
git worktree add ../myapp-wt/feature-db -b feature/xxx-db origin/main
# 一覧
git worktree list
# 片付け
git worktree remove ../myapp-wt/feature-db
起点は、git fetchで取ってきたばかりのリモートのデフォルトブランチにしています(例ではorigin/main)。手元のブランチは、更新し忘れて古いことがあるからです。
なぜ使うのか:レビューが追いつかないから
きっかけは失敗でした。
ある機能をAIに作らせたとき、変更がまるごと1つのコミットに入ってしまいました。チケットは「管理画面」と「API」で分かれていて、「これは範囲外」とも書いてあったのに、ブランチとMRを1本で進めていたのが原因です。
次は反省して、機能を先に細かいチケットに分けてから、AIに順番に作らせました。このとき「コミットはしないで」とだけ伝えていて、チケットごとにどこへ置くかは決めていませんでした。その結果、すべてのチケットの変更が同じ作業フォルダに積み上がり、どの変更がどのチケットの分なのか分けて見られなくなりました。せっかく分けたのに、レビューする量は分ける前と変わりませんでした。
この2つで分かったのは、詰まるのは作る速さではなく受け取る速さだということです。分けるだけでなく、分けた単位ごとに置き場所(ブランチと作業フォルダ)まで分けておかないと、結局混ざります。
| やること | 目的 |
|---|---|
| ブランチを分ける | レビューできる量にする(こちらが本題) |
| worktreeで並べて進める | 分けた単位を混ぜずに、同時に進める(補助) |
「ブランチを分けるだけならgit switchでいいのでは」と思うかもしれません。ただ切り替えだと、次のような場面で困りました。
- AIを2つ同時に動かすと、同じフォルダでは互いの変更が混ざる
- 未コミットの変更が、切り替え先のブランチに持ち越されて混ざる
- レビュー待ちのブランチを開いたまま、次の単位に進めない
- 2つのブランチを同時に動かして見比べられない
私の使い方
1. 先に分けて、表にする
着手する前に、AIに分け方の案を出してもらいます。単位ごとに、ブランチ名と依存と、同時に進められるかを書かせます。DBから新しく作る機能なら、たとえばこんな表です。
| 単位 | ブランチ | 依存 | 進め方 |
|---|---|---|---|
| ①土台(DB・モデル) | feature/xxx-db | なし | 先に単独で |
| ②商品の登録画面 | feature/xxx-products | ① | ③と同時に |
| ③注文のAPI | feature/xxx-api | ① | ②と同時に |
| ④注文の管理画面 | feature/xxx-admin | ①③ | 単独で |
土台を作るかどうかは作業によって変わります。この例のようにDBから新しく作る機能なら、ほかの単位が乗るDBとモデルを土台として先に出します。土台という考え方が当てはまらない作業なら、土台は作らず、最初から機能ごとにブランチとMRを分けます。
土台を作るときは、それだけを先に単独で出します。そこだけに集中してレビューできますし、出した時点でDraft MRを作っておけば、出口が先にできます。
機能の単位は、フロント・バック・テストをひとまとまりにして切ると、レビューしやすくなります。大きさは、ファイル数よりも変更行数で見るのがよさそうです。ファイル数が少なくても、行数が多ければ分けたほうが読みやすくなります。
2. チケットや要件をもとにissueを作る
分けた単位ごとに、GitLab,Githabでissueを作ります。
これは普段タスク管理しているところでOK、Notion、Linear、Obsidianなど
元にするのは、チケットや要件です。
チケットには要望が2、3行しか書かれていないこともあるので、AIと問答しながら、完了の条件・壊しそうな箇所・まだ決まっていないことを詰めてからissueに起こします。
大きな機能なら親のissueを1つ作って、全体の順番と、取り合いになりそうなファイルを書きます。単位ごとのissueには「やること」と「やらないこと」を書きます。
ブランチとMRはissue 1つにつき1本です。AIにはissueの番号を渡して、本文とコメントを読んでから作らせます。指示を毎回会話に書かずに済み、何をどこまでやったかもあとからissueで追えます。
3. 同時に進めていい組だけworktreeに出す
同時に進める組だけ、worktreeを作ってそれぞれAIに渡します。1つのworktreeに1issue、1ブランチ、1MR、1セッションです。
マイグレーションと共通部品に触る単位は並べません。マイグレーションは同時に作ると実行順が入れ替わり、共通部品はほかの全部と衝突するからです。
4. 本数は作業に合わせて決める
同時に作らせる本数は、作業の重さとパソコンの余裕を見て決めます。2本のことが多いですが、3本並べることもあります。worktreeの中でテストまで動かすときは、そのworktree用の開発環境(Docker)も立てるので、その分メモリを使います。
気をつけているのは、レビュー待ちを溜めすぎないことです。作るほうが速いので、溜めると結局まとめて読むことになります。
5. 普段は本体のフォルダで作業する
worktreeはいつも使うわけではありません。普段は本体のフォルダで、最新のデフォルトブランチから切ったブランチで作業します。worktreeを出すのは、並行させたいときと、本体で別の作業をしている最中にレビュー用のブランチを開きたいときだけ。使い終わったら消します。
置き場所はリポジトリの外(myapp-wt/)にしています。
6. AIには担当をはっきり渡す
並べて動かして驚いたのは、AIが自分の担当を推測して、全員が同じ答えを出したことです。
当時はAIのセッションをすべて本体のフォルダで立ち上げていたので、今いる場所も、今のブランチも、未コミットの変更も全部同じに見えていました。そこから「自分の担当はどれか」を当てさせると、全員が同じチケットを名乗りました。今はworktreeごとにその中でAIを立ち上げ、どのworktreeで、どのブランチで、何をするかを最初の指示に必ず書いています。
AIの作業を回しやすくする道具
worktreeを何本も出してAIを並べると、どこで何が動いているのかが見えにくくなります。そこを補うために、次の道具を組み合わせています。
Orca
worktreeとAIのターミナルをまとめて扱えるアプリです。worktreeを作るとその作業場所のタブが開き、そこでClaude CodeやCodexを動かせます。どのworktreeでどのAIが動いているかが1画面で見えるので、並べても迷子になりません。
実装はClaude Code、レビューはCodexのように、タブごとに役割を分けることもあります。worktreeの作成・削除のときにスクリプトを呼べるので、worktreeを作ると開発環境の準備が、消すと片付けが、自動で走るようにしています。
ホワイトボード
Whiteboard Desktopというアプリに、AIが書いたコードの流れやレビューの結果を描かせています。入口から出口までの流れが枠でつながり、枠を押すとその行のコードが開きます。差分を頭から読む前に全体の形をつかめるので、レビューの入口として使っています。
このほか、よくやる作業はClaude Codeのスキル(AI向けの手順書)として自分で作っています。
使わない場面
何でもworktreeにすればいいわけではありません。次のようなときは使いません。
- 1、2件の小さな修正。普通に1ブランチでやるほうが早い
- 仕様が固まっていないとき。先に1本で試して、固まってから分ける
- 依存が強くて、順番にしか進められないとき
おわりに
worktreeは「並行で速く作るための道具」として紹介されることが多いと思います。私にとっては並行して早く実装できるのもそうですが、レビューをしやすくするなどで使っています。
(まだ、人間の確認と修正が必要だと思うから)
本題はあくまで、レビューできる大きさに分けること。worktreeはそれを支える補助です。AIがいくらでも書いてくれるようになった今だからこそ、読む側の都合で区切る仕組みが要るのだと思っています。