Claude Codeでモノリスを分割する順番をPlan modeで決める手順
モノリスの分割は、いきなり切り出さず、読み取り専用のサブエージェントで依存の向きとテーブル共有を調べ、Plan modeで切り出す順番を決めます。調査用サブエージェントの定義例とプロンプト例つきです。
モノリスの分割で失敗しやすいのは、切り出す機能を選ぶ前に手を動かしてしまうことです。Claude Codeでは、Plan modeで編集を止めたまま、読み取り専用のサブエージェントに依存の向きとテーブルの共有を調べさせ、その結果から切り出す順番を決められます。この記事は、その調査から計画までの手順をまとめたものです。
分割の前にPlan modeで編集を止める理由
Plan modeは、調査と提案だけを行うパーミッションモードです。Claudeはファイルを読み、シェルコマンドで探索し、計画を書きますが、ソースは編集しません。計画を承認するまで編集はブロックされたままです(インタラクティブな端末でbypass permissionsが使える状態では、この限りではありません)。
分割の初期段階に向いているのは、この「承認するまで何も変わらない」性質です。境界の候補を間違えても、失うのは調査の時間だけです。
入り方は3つあります。
Shift+Tabでモードを巡回する- 1回のプロンプトの先頭に
/planを付ける - 起動時に
claude --permission-mode planを指定する
分割プロジェクトでは、最初から計画モードで起動する運用が楽です。
claude --permission-mode planプロジェクトの既定にするなら、.claude/settings.json の permissions.defaultMode に plan を入れます。VS Code拡張はプロジェクト設定を読まないため、ユーザー設定の claudeCode.initialPermissionMode を plan にします。
{
"permissions": {
"defaultMode": "plan"
}
}Plan modeの基本操作はPlanモード完全ガイドに詳しくあります。ここからは、分割に特有の部分に絞ります。
調べることは「依存の向き」と「テーブル共有」の2つ
切り出す順番は、2種類の結合の強さで決まります。コード上の呼び出し関係と、データベース上のテーブル共有です。どちらも、読むだけで集められます。
| 調べる対象 | 見るもの | 切り出しやすさの目安 |
|---|---|---|
| 依存の向き | 見るものモジュールがどこをimportし、どこから呼ばれているか | 切り出しやすさの目安呼ばれる側が少ないほど切り出しやすい |
| テーブル共有 | 見るもの複数モジュールが読み書きするテーブル | 切り出しやすさの目安書き込み元が1つのテーブルほど分けやすい |
| 外部との接点 | 見るもの公開API、キュー、バッチ | 切り出しやすさの目安境界がすでに明文化されているほど楽 |
2つの調査は互いに独立しています。独立した調査を並列に走らせるのは、サブエージェントが得意とする使い方です。認証・データベース・APIの各モジュールを別々のサブエージェントで並列に調べさせる例が、サブエージェントの解説にもあります。ただし、返ってくる結果が多いとメインの会話のコンテキストを消費します。調べる粒度は欲張らないことです。
調査用の読み取り専用サブエージェントを2つ用意する
Plan mode中のコードベース調査は、組み込みのPlanサブエージェントがすでに担っていて、読み取り専用です。それでもカスタムの調査役を足す理由は3つあります。報告の型(表の列と根拠パスの添え方)を固定できること、調べる対象を依存とテーブル共有に絞れること、model: haiku で費用を抑えられることです。
サブエージェントは .claude/agents/ に置くMarkdownファイルで定義します。tools に Read, Grep, Glob だけを書けば、編集できない調査役になります。この制限は、プロンプトで頼むのではなくClaude Code側で強制されます。
1つ目は、依存の向きを調べる役です。
---
name: dependency-mapper
description: モジュール間のimportと呼び出しの向きを調べて報告する。編集はしない
tools: Read, Grep, Glob
model: haiku
---
指定されたディレクトリについて、次を調べて表で報告する。
- そのモジュールがimportしている他モジュール(出ていく依存)
- そのモジュールをimportしている他モジュール(入ってくる依存)
- 循環している組
推測で補わず、根拠にしたファイルパスを必ず添える。2つ目は、テーブル共有を調べる役です。
---
name: db-coupling-mapper
description: どのモジュールがどのテーブルを読み書きしているかを調べて報告する。編集はしない
tools: Read, Grep, Glob
model: haiku
---
ORMのモデル定義、SQLファイル、マイグレーションを手がかりに、
テーブルごとに次を表で報告する。
- 読み取っているモジュール
- 書き込んでいるモジュール
- JOINで他テーブルと結びついている箇所
根拠にしたファイルパスと行を必ず添える。model: haiku にしたのは、ファイルを集めて整理するだけの仕事だからです。ベストプラクティスにも、モジュールをimportしているファイルの収集のような単純な作業にはHaikuが向く、という趣旨の記述があります。設計上の判断が要る調査には、軽いモデルを当てないでください。モデルの決まり方はサブエージェントのモデル指定と配分にまとめています。
定義ファイルは、追加から数秒で検出されます。ただし、agents ディレクトリを新しく作った直後は、再起動が必要です。
落とし穴
Plan mode中は、サブエージェントの権限もPlan modeのまま
メインの会話がPlan modeのとき、サブエージェントの permissionMode に何を書いてもPlan modeのままです。dontAsk を書いた場合も、Plan modeなら確認が出る操作は拒否されます。調査役にあえて permissionMode を書く必要はありません。
調査を並列に走らせ、境界候補を出させる
Plan modeに入った状態で、調査と境界候補の提案をまとめて頼みます。次は、プロンプトの一例です。
このリポジトリのモノリスを分割したい。まだ何も編集しないこと。
1. dependency-mapper で、src/ 直下の各モジュールの依存の向きを調べる
2. db-coupling-mapper で、各テーブルの読み書きモジュールを調べる
(1 と 2 は並列に実行する)
3. 両方の結果から、独立したサービスに切り出せそうな境界の候補を
3 つまで挙げ、それぞれ根拠のファイルパスを添える返ってくる内容は、リポジトリによって変わります。ここで見るべきは、境界の名前ではなく、根拠として挙がったパスとテーブルが実在し、読み取りと書き込みの区別が合っているかどうかです。
サブエージェントの結果を鵜呑みにしない理由は、ベストプラクティスにもあります。サブエージェントは自分で新たに文脈を集めるため、メインの会話が知っている前提を持ちません。調査の前提(たとえば「legacy/ は廃止予定なので除外」)は、プロンプトに書いて渡します。
並列で動かせるサブエージェントの数には上限があります。既定では20個が同時に動いているとき、新しい起動が Concurrent subagent limit reached で失敗します。モジュールが数十あるなら、一度に全部を投げず、領域ごとに分けて回すほうが安全です。上限は環境変数 CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS で変更できます。
切り出す順番は、結合の弱い順に1機能ずつ決める
調査結果が揃ったら、同じPlan modeのまま順番を決めさせます。段階的に切り出す方式(ストラングラーパターン)では、機能を1つずつ新しいサービスへ移し、古い経路を少しずつ細らせます。計画はその単位で組ませます。
切り出し順を決めるときの流れ
- 1
結合の強さで候補を並べる
入ってくる依存が少なく、書き込むテーブルが他と共有されていない機能を先頭にします。
- 2
共有テーブルの扱いを決める
複数のモジュールが書き込むテーブルは、先に書き込み元を1つに寄せる手順を計画に入れます。
- 3
1機能ごとに「戻せる」状態を保つ
各ステップの終わりで、元の経路に戻せることを確認項目に入れます。
- 4
計画を編集して承認する
Ctrl+Gで計画をエディタで開き、順番と範囲を直接直してから承認します。
プロンプトの例は次のとおりです。
調査結果をもとに、切り出す順番を計画にして。
- 1 ステップにつき切り出す機能は 1 つだけ
- 各ステップに「完了の確認方法」と「元に戻す方法」を書く
- 共有テーブルを先に整理が必要な場合は、そのステップを独立させる
- 最初のステップは、リスクが最も低いものにする計画が出たら、承認の前にそのまま走らせず、前提が崩れていないかを確かめます。承認の選択肢は3つです。
- 自動モードで進める(自動モードが使えない環境では、編集を自動承認する選択肢になる)
- 編集を1つずつ手動で承認する
- 計画のまま、さらに練り直す
分割のように長く続く作業では、編集を1つずつ見られる手動承認から始める方法があります。慣れてきたら、リスクの低いステップだけ自動に切り替えられます。Plan mode中のシェルコマンドの審査はuseAutoModeDuringPlanの設定で変わります。自動モードで進めるなら、サブエージェントが返す最終報告の審査はSubagentHandbackの解説が参考になります。
計画を実行する前に、CLAUDE.mdに分割の約束事を書く
計画を承認すると、Claudeは編集を始めます。この段階で効くのは、分割中に守らせたいルールをCLAUDE.mdに置いておくことです。次は書き方の一例です。
## モノリス分割の約束事
- 1 回の変更で切り出すのは 1 機能だけ。他機能のコードに手を入れない
- 切り出した機能のテーブルは、元のモジュールから直接参照しない(API 経由にする)
- 各ステップの最後に、既存のテストを全件実行して結果を報告する
- 共有テーブルのスキーマ変更は、必ず別のステップとして計画に出す計画と実装を同じ会話で続けると、計画の前提が文脈から薄れることがあります。Plan modeのshowClearContextOnPlanAcceptを有効にしておくと、計画フェーズのコンテキストを消して実装に進む選択肢が出ます。そのかわり、計画ファイルとCLAUDE.mdに前提を残しておく必要があります。
実装の途中で前提が崩れたときは、Shift+Tab か /plan でPlan modeに戻り、残りのステップを組み直します。
調査の費用は、サブエージェントの数だけ増える
サブエージェントは独立したリクエストを送るため、メインの会話と同じ使用量から消費します。費用の中心はトークンです。並列調査は速い一方で、コストが軽いわけではありません。
節約の手は3つあります。
- 調査役は
model: haikuにする(判断の要る調査は除く) - 調査の範囲をモジュール単位で区切り、重複した調査を避ける
/usageでサブエージェントの種類ごとの消費を確かめる
サブエージェントのプロンプトキャッシュは、既定では5分です。調査を何度も打ち直すと、その分だけ読み直しの費用がかさみます。
技術的負債の棚卸しとの使い分け
負債の優先順位を付けたいだけなら、tech-debtスキルによる棚卸しのほうが目的に合います。この記事が扱うのは、分割すると決めたあとの、調査と順番決めです。負債の棚卸しで見つけた「触りたくない領域」を、ここでの調査対象に選ぶ使い方もできます。
分割計画づくりで踏みやすいつまずき
落とし穴
調査が大きすぎて、結果が読み切れない
リポジトリ全体を1回で調べさせると、報告が長くなりすぎます。領域を3〜5個に区切り、領域ごとに表で返させます。
落とし穴
テーブル共有が見えているのに、コード側の依存だけで順番を決める
コードの依存が少なくても、書き込みテーブルを他のモジュールと共有している機能は、切り出すとデータの整合が崩れます。2つの調査結果を並べて、先頭の候補を選びます。
落とし穴
動的な呼び出しはGrepで拾えない
設定やリフレクションで呼び出し先が決まるコードは、importの検索には出てきません。調査役の報告に「静的な検索では追えなかった箇所」を書かせ、その箇所は人が確かめます。
まとめ
モノリスの分割は、Plan modeで編集を止め、読み取り専用のサブエージェントで依存とテーブル共有を並列に調べ、結合の弱い順に1機能ずつの計画へ落とす流れで進められます。最初の1ステップが小さく、戻せる形で決まっていれば、残りの計画は走りながら組み直せます。まず1つ、依存の少ないモジュールで調査役を試し、報告の根拠パスが正しいかを確かめるところから始めると、この進め方が自分のリポジトリに合うかが分かります。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。