見出し画像

Claude Code Webを10並列で回す!チームみらいの選挙支援ツールを作りながら構築した、超並列LLMコーディングを実現するためのハーネスエンジニアリング

こんにちは。チームみらい 永田町エンジニアチームの伊藤(エディ)です。
選挙も終わったので、最近の開発スタイルについて書いてみようと思います。

衆院選では、動画作成チームのために、各選挙区から大量にGoogleDriveにアップロードされてくる演説動画の文字起こし・切り抜きクリップ作成を自動化する内部ツールを作っておりました。(あくまで内部ツールなので詳細は割愛)

今回作ったツール「みらい動画スタジオ」

まるみえ政治資金のAI開発手法の記事を書いてから約4ヶ月、コーディングAIは超スピードで進化し、それに合わせ自分の開発スタイルもだいぶ変化しました。特に自分的に大きかったのは以下2点です。

  • Claude Code Webの登場で、LLM実装の並列化コストが劇的に下がったこと

  • Skills(スラッシュコマンド)の登場で、定型作業のクオリティを上げやすくなったこと

これらの使い方を突き詰めた結果、最近ではClaude Code (Opus 4.6) を、常時3~4並列、大きい機能の実装時には10並列で回すような超並列開発がメインのスタイルになってきました。

10並列でClaude Code Webを動かしているところ

ある程度自分の中でベストプラクティスが溜まったので、この記事では、並列開発を可能にしている工夫をまとめてご紹介しようと思います。

※ 奇しくも、AIエージェントが正しく力を発揮できるように情報やルールを整えることを「ハーネスエンジニアリング」と呼び、コードを書くこと以上にエンジニアの中心的な仕事になっていくだろう、というOpenAIの記事が、この記事の1日前に公開されていました。その一事例として読んでみても面白いと思います。


とても大事な注意事項

ここに書いてあることは「僕はこうやったら結構うまくいったよ」くらいのものであり、万能な方法論ではありません。

  • フルスクラッチの開発であること

  • TypeScript + Next + Express というLLMが得意な技術スタックであること

  • 1人で開発しているため、合意形成のコストが低いこと

  • 大まかに動けば有用で、エッジケースにおけるバグの存在が致命的な事象にならないタイプのプロダクトであること

これらの前提があってこその話なので、一つの事例としてお読みください。

なぜ並列で回したいのか

並列化する最大のメリットは、作業スピードの向上です。

コーディングAIはこの半年で大変思慮深くなり、たくさんの関連ファイルを読み込み、より複雑なコンテキストにも対応できるようになった一方で、一つ一つの作業には時間を要するようになり、普通に使っていると、LLMを待つ時間がボトルネックになりやすくなりました。

各作業の作業を高速化するには限界がある一方で、うまく並列化することで、2倍、3倍…10倍の開発速度を出すことが現実的に可能です。

Git Worktreeなどで、ローカルで並列化する手法も研究が進められている印象ですが、個人的にはブラウザUIでセッションが一覧化され、ローカルのgit 状態、ディレクトリ構成に注意を払わなくて済むClaude Code Webのほうが取り扱いやすいと感じ、こちら方面に最適化する方向で試行錯誤していました。

ということで、ここからは、どうやったら並列化でコーディングを回しつつコード品質を保てるのか、という工夫について話していきます。

並列化を可能にするプロジェクト構成

10並列で実装を回すとなると、一番気になるのは変更ファイルの衝突なのではないでしょうか。

この点については、DDDやレイヤードアーキテクチャといった設計パターンを参考に、ディレクトリや処理の記述箇所についてのルールを厳格に設計・適用することで、プランニング時に衝突を回避しています。

機能分離 × レイヤーで「触るファイル」を自明にする

まず大きな区分として、機能コンテキスト(BC)単位でディレクトリを完全に分離しています。

apps/backend/src/contexts/
├── clip-video/    # 動画クリップ切り出し機能
├── shorts-gen/    # ショート動画生成機能
└── shared/        # 機能間で共有するコード

またそれぞれのコンテキストの中には、レイヤードアーキテクチャに沿った4つのレイヤー、その中にサブディレクトリを作っています。

contexts/clip-video/
├── presentation/
│   ├── routes/        # APIエンドポイント定義
│   └── middleware/    # 認証・バリデーション等
├── application/
│   ├── usecases/      # ユースケースごとの処理フロー
│   └── services/      # ユースケースから切り出した複雑な処理など
├── domain/
│   ├── models/        # ドメインロジックを集約したドメインモデル
│   ├── services/      # 複数のドメインモデルをまたぐドメインロジックの実装
│   └── gateways/      # 外部依存の抽象インターフェース
└── infrastructure/
    ├── repositories/  # DB操作の実装
    └── clients/       # 外部API・FFmpeg等の実装

ここまで深いレイヤー分けは、人間にとっては面倒に感じるところですが、AIにとっては実装量は苦になりません。むしろ、このように設計を明確にすることで、各レイヤーに何を置くかという設計判断がほとんど発生しなくなります。

例えば「動画に字幕を付けられるようにする」という新機能を例にした場合、概ね誰が設計しても以下のようなファイルを触ることになるのではないでしょうか。

  • ドメイン層

    • models: 切り抜き(Clip)や字幕(Subtitle)のドメインモデル

    • gateways: 字幕合成処理の抽象インターフェースを定義

  • インフラ層

    • clients: FFmpegを使った字幕合成の実装

    • clients: 字幕合成済動画のファイル保存機能

    • repositories: RDBへの字幕動画URLの保存機能

  • アプリケーション層

    • usecases: 「クリップに字幕を付ける」処理フロー

  • プレゼンテーション層

    • routes: 字幕付与APIのエンドポイント

実装場所が自明かつ分割されていれば、タスクの並列化も自然にできます。

たとえば下記のように、最初のフェーズでAPIの形とgatewayのインターフェース(抽象)だけ決めてしまえば、その「契約」に基づいてフロントエンド・ドメインモデル・インフラ層をそれぞれ独立して実装できます。

  • Phase A: APIとgatewayのインターフェースを定義(契約を決める)

  • Phase B1: フロントエンドの実装

  • Phase B2: ドメインモデル、ドメインサービスの実装

  • Phase B3:  インフラ層: FFmpegの文字埋め込み機能の実装

  • Phase B4:  インフラ層: ファイル保存機能の実装 

  • Phase B5: インフラ層: Repositoryの機能実装

  • Phase C: プレゼンテーション・アプリケーション層を実装して統合

ここで、PhaseBの5つは触るファイルが完全に別なので、5セッション並列で同時に走らせられるというわけです。

超並列LLMコーディングの具体的なフロー

設計による境界の話をしたところで、実際のフローを紹介します。

Step 1: 設計ドキュメントを作る(/planコマンド)

Claude Code Skills として、/planというslash commandを用意しています。実行すると、Claude Codeが以下の手順で設計ドキュメントを生成します。

  1. 目的(Why)の明文化: 「なぜこの実装が必要か」を言語化

  2. 関連コード・ドキュメントの調査: 既存の設計ガイドを読み込み

  3. 設計ドキュメントの生成: docs/tasks/YYYYMMDD_HHMM_{作業内容}.md に保存

  4. レビュー待ち: 自分で実装には進まず、対話しながら人間のOKを待つ

例えばこんな感じで指示します。(指示はけっこう具体的に設計まで踏み込んで書きます)

/plan
カット済みのclipに、ffmpegで字幕を合成できる機能を作成したい。
字幕は、テキストと、開始と終了のタイムスタンプを持ち、1クリップが複数のリレーションを持つような感じ。
ひとまず文字スタイルは考慮せず、決まったフォントやサイズでレンダリングされればいい。

なるべくLLMの並列セッションで実行可能なように実装プランを考えてください。

Step 2: レビューしてOKを出す

設計ドキュメント(md)の中身を確認して、スコープと方針に問題がなければOKしてdevelopブランチにマージ。

ここにはあえて実装の詳細を書かせないようにしており、設計ドキュメントに書いてあるのは「目的」「影響範囲」「どのレイヤーにどういうファイルを置くか」が適切かチェックしています。

Step 3: Claude Code Webに投げて並列実行

実際のClaudeCode Webの並列実行の様子

OKを出した設計ドキュメントを、Claude Code Webの各セッションに渡します。先ほどの字幕機能の例なら、↓こんな感じのプロンプトを5並列で投げて動かします。

下記手順書内の、Phase B1を実装してください。
手順書: https://github.com/team-mirai-volunteer/video-processor/blob/develop/docs/xxx.md
並列で別のエージェントも別のタスクを作業しているので、他のタスクは着手しないでください。

(なお、これまでもDevinなどでも同様のことはできていましたが、Claude Code Opusシリーズは、Devinと比べ圧倒的に実装力が高い感じがあり、手戻りが相当減りました。)

Step 4: PRが上がってくるのでレビュー → マージ

各セッションが実装を終えるとPRを作ってくれるので、軽く動作確認したのち、CIが通っていることを確認してマージ。

基本的にコードを全行読むようなレビューはしていないです。

ここまでガチガチにアーキテクチャルールを決めて、後述のようなガードレールでそれが守られていることを担保していると、手元で動作確認してOKなら、コードの中身を逐一見なくても品質は破綻しにくいというのが実感です。
(これは、動画を大量にさばくという用途特有で、バグが致命的になるアプリケーションではそうはいかないと思います)

もちろん、設計判断に関わるPR(新しいドメインモデルやテーブルの追加、API設計の変更など)はコードをちゃんと読みますが、基本は動作確認だけですませても、それほどおかしい実装にはならない感覚があります。

並列で回しても品質が崩れないガードレール

このような大雑把な回し方をすると、セッションによっては低品質なコードがマージされてしまうのでは?と考えた方もいるかもしれません。ここは、ガードレールとなる仕組みを多重に用意することで、品質の低いコードはマージされないようにしています。

ガードレール①: CLAUDE.md + slash commandで、AIに読ませる情報を構造化する

全セッションで読み込まれる CLAUDE.md にはどんな作業でも共通で参照すべき情報(コマンド一覧、大まかなディレクトリ構成、コーディング規約)だけを記載し、個別の実装においては、機能 × レイヤー単位のslash commandを用意しています。

.claude/commands/
├── docs/backend-architecture-guide.md # バックエンド設計の原則
├── docs/backend-testing-guide.md # バックエンドテスト設計の原則
├── backend-clip-video.md    # clip-videoのバックエンド修正
├── backend-shorts-gen.md    # shorts-genのバックエンド修正
├── front-clip-video.md      # clip-videoのフロントエンド修正
├── front-shorts-gen.md      # shorts-genのフロントエンド修正
├── plan.md                  # 設計ドキュメント作成
├── review.md                # 設計レビュー
└── ...

たとえば /backend-clip-video を実行すると、Claude Codeが以下の2ファイルを読み込んで実装してくれます。

CLAUDE.md(常に読む共通情報)は薄くしつつ、slash command(タスクに応じて読む専門情報)で必要な情報を参照させることで、コンテキストを消費しすぎることを防ぎながら、どのセッションにも同じルールが注入されるようにしています。

ガードレール②: dependency-cruiserによる依存方向違反の機械的な検出

特に並列で複数のAIが同時にコードを書いている状況では、人間が全部目で見るのは無理なので、dependency-cruiser導入しています。

これは、プロジェクトのimportを静的解析して、「このレイヤーのモジュールはあのモジュールに依存してはいけない」といったルールへの違反を検出してくれるツールです。CIに組み込んで、PRごとに依存関係の方向を自動で検証しています。

バックエンド側のルール(一部):

  • Domain層は他のどの層にも依存してはならない

  • Application層はInfrastructure層に依存してはならない

  • BC間の直接依存は禁止(shared経由のみ許可)

フロントエンド側のルール(一部):

  • UIコンポーネントはfeaturesに依存してはならない

  • features間の直接依存は禁止

このようにすることで、設計違反となるコードがマージされることを防いでいます。

dependency cruiser の設定例

ガードレール③: レイヤーごとに異なるテスト原則

また、レイヤーを分けることでテスト戦略も明快にすることができています。具体的には以下のようなルールを導入しています。

  • Domain層・Application層: 極力すべての処理にユニットテストを書くことを原則とし、デグレを極力発生させないようにする

  • Infrastructure層: FFmpegの動画処理やLLMクライアントなど、外部依存のあるコードにも原則としてインテグレーションテストを書かせる。いつでも手元で動作確認できる状態を保つ。

slash commandでテストガイドラインも読み込ませているので、10並列で書いてもテストの粒度と書き方が揃います。

ガードレール④: Git Push をフックした品質チェック

コード品質のチェックは3段構えにしています。

  1. pre-commit: git commit時に lint-staged で変更ファイルだけBiome check

  2. pre-push: git push時に lint && typecheck で全体チェック

  3. CI: Biome + Knip(デッドコード検出)+ dep-cruiser + テスト

個人的に一番効いていると感じるのがgit pushにフックさせて品質チェックをする設定です。

人間が手で開発しているときは、pushのたびにlint + typecheckが走るのは正直うっとうしいですが、Claude Code Webの並列開発においては、以下の理由でとても有用です。

  • 10セッションが個別に実装→pushしてくるので、1セッションのpushに時間がかかっても人間は気にならない

  • 逆にpush時点で弾かなかった場合、CIで失敗してからセッションに差し戻すのはだいぶ面倒。

また、CLAUDE.mdに --no-verify は原則禁止と明記しています。AIはGit hookで怒られると --no-verify でスキップしようとすることがあるのですが、ルールとして禁止しています。

ガードレール⑤:  reviewコマンドによるガイドライン違反のチェック

また、/reviewコマンドで、設計ガイドラインに違反している部分をチェックできるようにしています。これをたまに実行することで、dep-cruiserのルールでは拾いきれない意味的な設計違反をカバーできることがあります。

それでも起きる問題と対処

ここまで仕組みを整えていても、問題がゼロになるわけではありません。

並列セッション間のコンフリクトはたまに起きる

「衝突はほとんど起きない」と書きましたがゼロではないです。ですが、コンフリクトが起きたらそれもClaude Code Webに解消させれば良いのであまり問題にはなりません。

仕様起因のエラーは普通に起きる

ここまで書いてきたガードレールは、コード品質に関するものなので、動かしてみると「そういう仕様ではないんだよなあ」となることはちょくちょく起きます。そういうときは並列化で回すのを一旦止め、個別対応で想定どおりになるまでローカルで試行錯誤したりしています。

まとめ

ということで、Claude Code Webでの超並列化を成立させるためのハーネスエンジニアリングとして、以下を紹介してきました。

  1. 設計: かっちりめのレイヤー設計による、実装の配置箇所の明確化

  2. ガードレール: 品質チェックの自動化による設計品質の担保

個人的には、LLMコーディング全盛の今こそソフトウェア設計の勉強はとてもおすすめだと思っています。

なぜなら、DDDやクリーンアーキテクチャといった確立された設計パターンの用語は、LLMにとっても共通言語として機能するからです。「Bounded Context」「ドメインモデル貧血症」といった短い用語をドキュメントや指示プロンプトに入れるだけで、詳しく説明せずとも品質の高いアウトプットが出やすくなります

また、選挙期間中は自分がオーケストレーターをやっていたのですが、このような構成とガードレールを前提にすれば、オーケストレーターも十分AIに任せて、マルチエージェント化も十分可能と思っています。次のトライとして試してみようと思います。

もっとAIが賢くなったら、こういった設計すら不要になる未来はありそうですが、現時点でうまくワークしている自分なりのベストプラクティスを紹介してみました!よければ参考にしてみてください〜!