Claude Code × Git Worktree × Supersetで実現する並列開発
IT技術
はじめに
Claude Code で並列開発をしたいけれど、アプリケーションの動作確認はローカルサーバーのポートが競合してできない、というもどかしさを感じた方は多いのではないでしょうか?
特に Docker Compose を使っているプロジェクトでは、docker compose up を 2 つ走らせると即座にポートかコンテナ名で衝突します。
本記事ではこの問題を、git worktree + 自前スクリプト + AI 統合開発環境 Superset の組み合わせで解いた手順を残します。
なぜ並列開発したいのか
動機 1: タスクを並行して進めたい
Claude Code に作業を投げたあとの「待ち時間」は、開発内容によっては結構長いです。
この間をぼーっとお茶しているわけにもいかないので、積まれている他の作業を進めたいですよね。
動機 2: PR レビューする時の準備が面倒くさい
作業中にレビュー依頼が来たときは、以下のような作業をするかと思います。
- 自分の作業差分をスタッシュ
- PR のブランチにチェックアウトして動作確認
- PR に指摘コメントを投稿する
- 自分の作業ブランチに戻ってスタッシュを適用して、作業再開
- PR の指摘内容が修正されたため、再度スタッシュして、PR のブランチにチェックアウトして動作確認
この毎回スタッシュする工程が非常に面倒でした。
コミットすればスタッシュの工程はスキップできますが、コミットは自分のタイミングでしたいですよね。
素朴な解: git worktree 単体
最初の発想は素朴で、git worktree を使えば 1 つのリポジトリから複数の作業ディレクトリを生やせます。
ブランチを切り替えずに別ブランチを編集できるので、これだけで「checkout で作業を中断する」問題は消えるはずです。
ただしなにも対処をしていないと、作成したワークツリー内でコンテナを立ち上げると、メインツリーで起動しているサーバーのポートと衝突して起動できません。
ワークツリーを作成しただけでは、Docker Compose の世界では並列起動できる状態になっていません。
静的レビューやサーバーへのアクセスを必要としない作業であれば問題ないかもしれませんが、ほとんどの場合そうではありません。
解決策: 自動ポート割当スクリプト
全体像
ポートが競合するのであれば、ワークツリーごとにポートを変更できるようにすれば解決です。
前提として、本プロジェクトの docker compose は Rails / MySQL / Redis / memcached の 4 コンテナを 1 セットで立ち上げる構成になっています。
Rails コンテナはアプリケーション本体で、起動時に MySQL(DB 永続化)・Redis(キャッシュ)・memcached(セッション・短期キャッシュ)の 3 つにネットワーク越しにアクセスしています。

実際の docker-compose.yml(抜粋)は次のような形です。ホストポートとコンテナ名を環境変数で外から差し込めるようにしてあるのがポイントです。
「変更するポートが 1 つだけならワークツリーごとに手動で書き換えても手間にはならないのでは?」と思う方もいるかもですが、下記は例であり実際には 3 ポートあり、コンテナ名も書き換えないといけないので手動で毎回変更するのは面倒です。
1services:
2 memcached:
3 container_name: memcached
4 image: memcached:latest
5 networks: [my-app-network]
6
7 redis:
8 container_name: redis
9 image: redis:latest
10 networks: [my-app-network]
11
12 db:
13 container_name: db
14 image: mysql:8.0
15 environment:
16 MYSQL_DATABASE: myapp
17 networks: [my-app-network]
18
19 my-app:
20 container_name: ${CONTAINER_NAME:-my-app}
21 build: { context: ., dockerfile: Dockerfile }
22 environment:
23 MYSQL_HOST: db # ← Rails から DB へはサービス名で参照
24 ports:
25 - "${HOST_PORT:-3013}:3000" # ← ここを .env で差し替える
26 networks: [my-app-network]
27 depends_on:
28 redis: { condition: service_started }
29 db: { condition: service_healthy }
30
31networks:
32 my-app-network:
33 external: true # ← 共有ネットワーク(後述)注目すべきは次の 2 点です。
HOST_PORTとCONTAINER_NAMEを環境変数化している点。これを.env経由でワークツリーごとに差し替えることで、同じ compose ファイルを複数ワークツリーで使い回せます。networks.my-app-network.external: true。Compose 内で作るのではなく、外部に既に存在するネットワークに繋ぐ宣言です。これがリンクワークツリーから「メイン側の MySQL / Redis / memcached」に繋ぎに行く土台になります(詳細は後述)。
このコンテナ群をポートの衝突なしに立ち上げるには、異なるポートで立ち上げる必要があります。
ただし、毎回起動ポートを手動で変更して立ち上げるのは面倒なので自動化させたいです。
ここでまず考えなくてはいけないのは、「MySQL / Redis / memcached」をワークツリー間で共有するかどうかです。
共有しない場合、ワークツリーごとに毎回これらを起動しなおさなくてはいけません。
軽量であればいいのですが、DBのデータ量が多い場合などで毎回起動するまでに時間を要するのは避けたいです。
共有する場合、1 つのワークツリー内での変更が全ワークツリーに波及するデメリットはありますが、起動に時間を要するデメリットと比べると妥協できるレベルだったので共有する方針としました。

そのため、ポートの競合で解決しなくてはいけないのは Rails サーバー周りのポートだけになりました。
手順は以下の 3 ステップになります。
- ポートをワークツリーごとに自動で別の値に割り当てる
.envにそれを書き込む(既存の手書き設定は壊さない)- ここまでの作業を 1 コマンド で完了させる
これから登場するスクリプトの最終的な配置は次のようになります。
1my-app/
2├── docker-compose.yml
3├── .env
4└── scripts/
5 └── worktree/
6 ├── port.sh # ポートを算出
7 ├── setup.sh # .env の管理ブロックを書き換え
8 └── docker-up.sh # setup.sh + docker compose up を 1 コマンドにポートをパスから決定的に割り当てる
ポート割当の要件はシンプルに 2 つです。
- 衝突しない: 他のワークツリーと被らない
- メインツリーのポートは固定にしたい: 本プロジェクトでは Rails は API サーバーのため、フロント開発者も立ち上げて API 通信をします。その時にポートが固定化されていないと、API サーバーのポートに合わせてフロントの通信先を変更する必要が出てきてしまいます。そのためメインツリーは固定にする必要があります。
自動ポートの設定方法に決まりはないですが、AI が提案してきたやり方にしました。
やっていることは以下です。
- 絶対パスを取得
- メインツリーは「3013」を指定
- リンクツリー(派生したワークツリー)のポートは、パスから「10000〜19999」のいずれかのポートを算出
実装は scripts/worktree/port.sh という単機能スクリプトに切り出していて、算出したポート番号を標準出力に echo するだけにしています。.env への書き込みは後述の setup.sh が $(...) で受け取る役割分担です。
1TOPLEVEL="$(git rev-parse --show-toplevel)"
2
3# メインワークツリーは .git が「ディレクトリ」、リンクは「ファイル」
4if [ -d "$TOPLEVEL/.git" ]; then
5 echo "3013"
6else
7 # パスの MD5 から数字だけ抽出 → 先頭4桁 → 10000〜19999 へ写像
8 echo "$TOPLEVEL" | md5 | tr -d -c '0-9' | head -c 4 | \
9 awk '{port = ($1 % 10000) + 10000; print port}'
10fi.env の管理ブロック方式
ポートが決まったら、それを .env に書き出して docker compose に読ませます。
ただし注意点があり、スクリプトが .env を毎回上書きすると、.env にすでに設定している手書きの変数が消えてしまいます。
ここもAIと相談し マーカーで囲まれた管理ブロックだけを書き換える やり方を採用しました。
実装は scripts/worktree/setup.sh の中にあります(以下はその管理ブロック処理部分の抜粋)。
1ENV_FILE="$TOPLEVEL/.env"
2MARKER_START="# === scripts/worktree/setup.sh managed (do not edit) ==="
3MARKER_END="# === end scripts/worktree/setup.sh ==="
4
5# 既存 .env から旧管理ブロックだけ除去
6awk -v s="$MARKER_START" -v e="$MARKER_END" '
7 $0 == s {skip=1; next}
8 $0 == e {skip=0; next}
9 !skip {print}
10' "$ENV_FILE" > "$TMP_FILE"
11
12# 新しい管理ブロックを追記
13cat >> "$TMP_FILE" <<EOF
14$MARKER_START
15COMPOSE_PROJECT_NAME=$PROJECT_NAME
16HOST_PORT=$HOST_PORT
17EOF
18
19mv "$TMP_FILE" "$ENV_FILE"awk で旧マーカーブロックをスキップしながら一時ファイルに転記し、末尾に新ブロックを追記してから mv で差し替えています。
一時ファイルを挟むのは、書き込み中にスクリプトが落ちても元の .env を壊さないためです。
1 コマンドで起動するラッパー
ここまでで .env は自動生成できますが、毎回「setup.sh → docker compose up -d」と 2 行打つのは地味に面倒です。
そこで scripts/worktree/docker-up.sh というシンプルなラッパーを置きます。
1TOPLEVEL="$(git rev-parse --show-toplevel)"
2
3# .env を生成
4bash "$TOPLEVEL/scripts/worktree/setup.sh"
5
6# 生成された .env を読みつつ起動
7(cd "$TOPLEVEL" && docker compose up -d)使う側はこれだけで済みます。
1# ワークツリー追加
2git worktree add ../my-app-feature-x feature/x
3cd ../my-app-feature-x
4
5# .env 生成 + コンテナ起動(1 コマンド)
6bash scripts/worktree/docker-up.shsetup.sh を独立して残しているのは、「ポート再計算だけしたい」「.env の中身だけ確認したい」というケースで単独実行できるようにするためです。
ラッパーは「便利のため」であって、setup.sh がコア機能を担うという役割分担を意識しています。
注意点
運用して気づいた落とし穴を 3 つ紹介します。
1. メインを先に起動する
リンクワークツリーは共有ネットワーク・共有ボリュームに繋ぎに行くため、メイン側が起動していないと network not found で失敗します。
チーム内で一番ハマるのがこれです。
2. 並列開発の構成をAIに伝える
ここまで解説した並列開発の方法はAIエージェントに伝えておく必要があります。
理由として、AIエージェントが実装した後に自動でテストがパスするまで確認することもあるかと思います。
その時にAIエージェントが並列開発の構成を知らないとリンクワークツリーで作業しているのにメインワークツリーでテスト実行してしまう可能性があります。
そうならないように、例えばclaude codeを扱っている場合などは、CLAUDE.mdに記載しておくことをオススメします。
Superset で さらに快適に
ここまでで並列開発は動きます。
ですがまだターミナルで git worktree add コマンドを手動実行する面倒さが残ります。
この最後の手間を解消し、さらにAIエージェントをより快適に扱えるようにしてくれるのがSuperset(AI 統合開発環境)です。
私はまだ使いこなせていませんが、ざっと良かった点を以下に記載します。
- プロジェクトをタブで管理でき、さらにプロジェクト内にワークツリーを数クリックで作成でき、ワークツリーごとにターミナルを何個でも起動することができる
- 作成したワークツリーがカレントディレクトリになっている状態のVS Codeをワンクリックで起動できる
- アプリ内でブラウザを開ける(ただしブラウザの挙動がおかしい時があるため今後に期待)
料金プラン
気になる料金ですが、公式の料金ページによれば、現時点では以下の 3 プランが用意されています(執筆時点・税別)。
- Free: $0。1 ユーザーまで、ローカルワークスペース・デスクトップアプリ・GitHub 連携が使えます。本記事で紹介した「ワークツリー作成 + 複数 Claude Code セッション」のような個人検証用途であれば、まずは Free から試せます。
- Pro: 月額 $20/ユーザー、年契約なら $15/ユーザー(25% オフ)。ユーザー数無制限で、リモートワークスペースや Linear 連携が解放されます。
- Enterprise: 要問い合わせ。SSO/SAML・監査ログ・SLA・専任サポートなど。
私自身フリープランを使用していますが、特に不自由はありませんでした。
Pro以降に上げてどう運用していくかはあまり想像ついていないですが、単純に並列開発をしたいのであればフリーで十分かなと思います。
まとめ
いかがでしたでしょうか?
快適な環境を手にいれるための実装が思ったよりも面倒でした。
ただ一度環境を作ってしまえばあとは快適に開発サイクルを回すことができます。
本ブログの方法以外にも色々な方法があると思いますので、ご自身のプロジェクトにあったやり方を探してみることをオススメします。
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!カジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ

服部晋平と申します! 前職では映像業や配送業に携わっていました。 趣味は、バイクツーリングに行ったり、美味しいラーメン屋巡りです。 未経験という身で入社させていただいたので、人一倍努力して頑張っていきたいと思います!




