あなたのプロジェクトはAI にコードレビューさせてますか?
AIに書かせたコードでも人間に書いたコードでも、
AIにコードレビューさせてみるとシニアエンジニアが指摘したい内容が全然指摘されない事に気付きます。
せっかくプロジェクトの方針をSkillとしてまとめたのに、
せっかくPullRequestをトリガーに自動でAIにレビューさせるようにしたのに、
放って置くと「これなんでAIは指摘してくれないんだ?」という不満と不信感が溜まっていきます。
タイポは拾う。null チェック漏れも拾う。テストの不足も指摘する。
でも「そもそもこの作り方でよかったのか?」が出てこない。
それを解決するために行った工夫がドンピシャだったのでメモがてら記事に残します。
3行要約
- 通常のAIコードレビュワーとは別に、「階層主義者」「分離主義者」「拡張主義者」というジョーカー(トリックスター的な役割)となる3人のレビュワーを追加した
- このレビュワーは あえてPRの内容とは別の設計の提案を行う 。ただし規定は「不採用」とし、PRの持ち主はこの指摘に対して従っても従わなくてもよい
- PRの内容とあえて違う代案を複数提示することで、視野狭窄に陥らない状態に仕組みと構造で解決することが出来る
(ただし、自身の書いたコードを常に否定されるため、人間向けではない)
前置き: なぜこれが実行できたのか
前提として、
- Issue を書くと AI が PR を出してくるところまで自動化
- PRが出ると自動でClaudeCode/Codexが差分をレビューしコメントをしてくれる所まで自動化
の2つの環境を構築済みです。
「別案を提案するAI」は、1つの PR に対して1万文字を超える提案を出してきます。
人間が受け止めるには重すぎる量です。
しかし**受け取る側も AI なら、量は問題になりません。
** 提案を読んで、採用するか不採用にするかを根拠付きで返し、採用したぶんを実装して push する——ここまで自動で回ります。人間が読むのは「3人がこう言って、AI はこう判断した」という要約だけです。
「AI だけでプログラミングが完結している」ことが、この重さを許容できる理由になっています。

(作業者。渡されたタスクに沿ってプログラムを書き、PRを作るAI)
PRのコード差分をコードレビューAIに渡すと、差分の中で整合しているかしか見ない
レビューAI に PR を渡すと、
この差分は正しいか
という観点でレビューします。
これはこれで、有益なレビューが帰ってきます。
関数のインターフェースが正しいかとか、計算ロジックのミスがないかとか、nullチェックやパターンが漏れていないかなどバンバン拾ってくれます。
しかし、「そもそも別の作り方があったのでは」という観点でのレビューはされません。
AIは既存の実装に合わせたりロジックのミスを指摘するのは得意ですが、
そのプロジェクトの概要や未来を踏まえたレビューというのは中々出来ません。
これは「正解のある答え合わせ」と「正解がわからない技術的負債の解消」という性質の違いがあるからです。
この問題を通常のレビュワーAIのプロンプトをいくら調整してもバイアスが掛かっている以上は難しいでしょう。

(レビュワー。PRの内容を確認してミスがあればわかりやすく指摘する)
根本的な設計・考え方を指摘する役を別に置く
レビューAIとは別に、3人のジョーカーとなるレビュワーを独立したセッションで起動します。
プロンプトの1行目からコードレビュワーとは違います。
あなたは欠陥を探すレビュアーではありません。 極端な思想を持ち、別の作り方を提案する専門家です。
この悪役となるレビュワーは3人居て、 それぞれ自身の大事な思想を1つ持ち、その信念に従ってレビューします。
| こだわり | よくある指摘 | |
|---|---|---|
| 階層主義者 | 依存の方向 | 「この情報は外側が持つべきです。内側には渡さないでください」 |
| 分離主義者 | 責任の分離と疎結合 | 「この情報はDIによるInject経由で自身で解決出来るためインターフェースに追加しないでください」 |
| 拡張主義者 | 将来の拡張性 | 「もし将来3つ目を足すとき、何箇所直しますか」 |
とにかく極端であることが仕事です。 バランスの取れた意見は既にレビュー AI が出しています。
ここで欲しいのはアイデアの種類です。
「一部は採用しても良さそう」となるアイデア・発想をAI同士でやり取りだけで発案させることが大事です。
そして既定は不採用です。ジョーカーの提案の多くは採用されませんが、残りは一考の余地があり、視野狭窄状態の作業者AIを救っているということです。

(ジョーカー。作業者が頑張って作ったPRを根本的に否定したり別のアイデアを提示する)
「注文に納品して報酬と交換する」機能
Unityゲーム開発の文脈で一部例を出します。
ゲームの機能なので、若干わかりにくいかもしれません。
題材
こういう機能を追加する PR でした。(RPGゲームの文脈)
- アイテムを納品するクエストに対して、アイテムを一部だけ納品する機能
- 納品数を達成したら、報酬と交換することができ、クエストは完了
- クエストには常設で再利用型のものと、特別な期間限定の一度きりのものの2種類ある
- クエストの期間が消えると一部納品されていた場合はアイテムを返却する
この機能を実装するために作られたコードでは、以下のようなクラスを1つ新設する形になりました。
// 納品にまつわること全部を持つ窓口
class DeliveryService
{
void Deposit(Target target, Items items); // 預ける
bool TryExchange(Target target); // 交換する
void ReturnDeposits(Target target); // 返す
bool IsAutoDeliveryEnabled(Target target); // 自動納品の設定を読む
Reason GetExchangeWaitingReason(Target t); // なぜ交換できないかを読む
}
このServiceの各メソッドを呼び出すことで納品関連の操作が出来るというものです。
差分として見れば、何も悪くありません。
1つの機能に関わる処理が1つのクラスにまとまっていて、テストも通り、動きます。
レビュー AI も、この構造自体は問題にしませんでした。
ジョーカー3人が、同じクラスを別の角度から指摘した
ここが面白いところでした。3人とも同じクラスを指しているのに、言っていることが違います。
階層主義者 —「汎用の窓口が、両方の種別の中身を知っている」
DeliveryServiceは「納品の共通の口」のはずなのに、常設と期間限定の中身を両方知っている。
種別ごとの違いは、種別側が自分で持つべきでは。代案: 汎用側がインターフェースを定義し、種別ごとの実装がそれを満たす形に向きを逆転する。
失うもの:
- インターフェース2枚と実装2つが増える
- いま1ファイル340行で「2種類で何が違うか」を並べて読めていたものが、3〜4ファイルに分かれる。 読む人は1回余計に飛ぶ
- 種別は現在2つで、増える予定はタスクに書かれていない。3つ目が来なければ、得るのは依存の向きだけになる
いま入れるべきか: いま — このクラスはこの PR で新設されたもので、呼び出し元は UseCase 4本と Query 1本に限られる。ここで向きを決めれば変更はこのフォルダ内で閉じる。後から種別を足す段で分岐を剥がすと、セーブ形式まで巻き込む。
分離主義者 —「読むだけの画面が、書き込みできるサービスを丸ごと受け取っている」
IsAutoDeliveryEnabledとGetExchangeWaitingReasonは読むだけのメソッドなのに、預ける・交換する・返すと同じクラスにある。そのため、一覧を表示するだけの
GetOffersQueryが、値1つを読むために、在庫と台帳を書き換えられるサービス全体を注入している。代案: 読み取りを
TargetResolverとして切り出し、書き込みサービスから外す。失うもの: クラスが2つ増える(100行ほど)。DI 登録が1件増える。
いま入れるべきか: いま — 呼び出し元がまだ5つ。表示が書き込みサービスに依存する形が定着する前に分けるほうが安い。
拡張主義者 —「交換の条件が2箇所に分かれている」
交換してよいかの関門(数がそろったか・クールダウン・報酬の置き場があるか)が、
TryExchangeとGetExchangeWaitingReasonの2箇所に別々に書かれている。関門を1つ足すとき、2箇所直すことになる。片方だけ直せば、「ボタンは押せるのに何も起きない」という形でズレが出る。
代案: 判定を「理由を返す1つの関数」にまとめ、実行側はその結果が
なしかを見るだけにする。
3人とも、差分の中のバグは一言も指摘していません。 そして3人とも、レビュー AI が問題にしなかった構造を指しています。
裁定は4段階
提案を受けた AI は、各件に 採用 / 一部採用 / 別Issueとして記録 / 不採用 を根拠付きで返します。
この例での実際の裁定はこうでした。
| 提案 | 判断 | 根拠 |
|---|---|---|
| 分離主義者「読み取りを書き込みサービスから外す」 | 採用 | 読み取り専用の口に分ける |
| 階層主義者「依存の向きを逆転」 | 一部採用 | 読み取りの点だけ直す(分離主義者と同じ)。種別の抽象化は、種別が2つしか無く3つ目の予定も無いので見送る |
| 分離主義者「差し替え処理を外に出す」 | 一部採用 | 判定の二重実行だけ消す。クラスの分割は提案どおり将来でよい |
| 階層主義者「状態を内側の層に下ろす」 | 別Issueとして記録 | 提案どおり将来でよい。別の機能でも使う話が出たときに |
| 拡張主義者「関門を1判定に」 | 不採用(前提が消えた) | **修正ラウンド2で、その状態そのものを撤去していた。**関門は既に1判定になっている |
「採用」は1件だけで、残りは一部採用・別Issue・不採用です。
そして内容を読むと中々マシなレビュー内容です。
これは 「規定は不採用、だから極端なアイデアをAIに出させてもよい」 というスタンスだから生まれています。
こういったことをレビューAIは指摘出来ませんでした。
多少レビューAIのプロンプトを改善した所で、おそらくこのような人間が安心出来る強度の指摘は生まれないと思います。
見送った提案は捨てない
不採用 以外で入れなかったものは、将来タスクのメモに出典付きで記録されます。
- **預かりを Domain へ下ろす**(ジョーカー hierarchalist 2): 納品の状態はいま Application にある。…
「いま入れない」と「忘れる」は別物なのでそこも区分けしています。
実際に何が変わったか
採用ぶんの修正で、こうなりました。
変更前 GetOffersQuery(読み取り専用) → StateService(書き込みできる)
変更後 GetOffersQuery(読み取り専用) → TargetResolver(読み取り専用)
| ファイル | 増減 |
|---|---|
| 窓口クラス | +29 / −130(正味 101 行減) |
| 読み取り役(新設) | +92 |
| 一覧の Query | +48 / −70 |
この変更は、レビューAIからは一度も指摘されていません。
そしてそれは「レビューAI」の本分としては何も間違っていない、というのが今までの悩みポイントでした。
効いている設計判断
作ってる最中に工夫した部分を挙げます。
1. 「失うもの」を必ず書かせる
提案には3点を必ず書かせます。
| 内容 | |
|---|---|
| 何が良くなるか | 具体的に。「きれいになる」ではなく、何がどう楽になるか |
| 何を失うか | 必ず書く。 複雑さ、行数、間接参照、学習コスト。無いと思うなら「無い」と断言する |
| いま入れるべきか |
いま / 将来でよい のどちらか。判断の理由も |
これを入れる前は、提案が全部「入れたほうがよい」に見えました。
抽象化はだいたい何かを増やします。それを書かせないと、判断材料が揃いません。
前掲の階層主義者の提案が良い例です。自分の提案の価値を自分で削っています。
種別は現在2つで、増える予定はタスクに書かれていない。
3つ目が来なければ、得るのは依存の向きだけになる
これを書かせているおかげで、裁定した AI は「種別の抽象化は見送る。読み取りの点だけ採用する」という一部採用の判断ができました。
書かせていなければ、全部入れるか全部捨てるかの二択になっていたはずです。
2. 「いま入れるべきか」を二択で答えさせる
これも実際に運用してみると良い項目でした。
良い提案でも、その PRの範囲をあまりにも超えるなら「将来でよい」と書かせます。
書かせないと、1つの PR にあらゆる改善提案が流れ込んで収束しません。
特にプロジェクトの自動化を目指している場合は一定の単位に分割しないとPRの規模がとんでもないことになりがちです。
逆に「いま」と判断する理由も似た考え方で、例で言うと
このクラスはこの PR で新設されたもので、呼び出し元はまだ5つ。定着する前に分けるほうが安い。
「いまなら安い」は、構造の変更において本質的な判断軸です。
人間が書いたコードでも同じはずです。
呼び出し元が5つのうちに向きを変えるのと、30 になってから変えるのとでは、コストが桁で違います。
技術的負債っていうのはそういう話です。
3. 既定は不採用
コメントの冒頭に書いてあるように、規定を不採用としています。
迷ったら不採用にしろということです。
これは欠陥の指摘ではありません。 3人の専門家が、それぞれのこだわりに極端に寄せた代案を出しています。既定は不採用で、多くは採用されません。
何度も言いますが、採用を前提にすると、極端な意見が出せません。
不採用が普通だと決めておくと、AIも遠慮なく極端に振れます。
実質ブレインストーミングに近いのかもしれません。
4. 3人は互いの提案を読まない
別々のセッションで起動しています。 同じセッションで3人分を書かせると、先に書いた視点に引きずられて、残り2人が薄くなります。
実際のところ、今回3人が「同じクラス」を指したのは独立に到達した結果です。
互いを読んでいたら「1人目が言ったので別のところを見よう」になりがちです。
3人が同じ場所を指したこと自体が、そこが本当に怪しいという信号になります。
5. 最大2件。「提案なし」でもよい
多く出すほど良いわけではありません。 一番効く1件に絞れるなら1件でよく、無ければ 提案なし でもよいとしました。
実際に「3人とも提案なし」で終わる PR もあります。
レビューAIとの違い
整理すると
| レビュー AI | 3人の主義者 | |
|---|---|---|
| 問い | この差分は正しいか | 別の作り方があったのでは |
| 見つけるもの | バグ、規約違反、テスト不足、事実の誤り | 構造のズレ、責任の混在、将来の拡張点 |
| 根拠 | ガイドラインの条項 | その思想から見てどうか |
| 既定 | 指摘されたら直す | 不採用 |
| PR を止めるか | 止める(要修正) |
止めない(助言であって関門ではない) |
この2つは代替ではなく、別のものを見ています。 片方だけでは足りません。
なお実装コストはほぼ同じです。プロンプトとペルソナの定義ファイルを置いて、レビュー AI と同じ仕組みで並列に起動しているだけです。
使ってみた実感
当たりを引く確率は高くありません。 半分以上の PR は「提案なし」か「将来でよい」で終わります。
しかし当たったときに拾えるものが、他の手段では拾えません。
今回の例で言えば、
- 差分としては正しい
- テストも通る
- レビュー AI も問題にしない
- でも、読み取り専用の画面が書き込みサービスを掴んでいる
これを「呼び出し元が5つのうちに」直せたのは、極端な思想を持つ役を置いていたからです。
そして冒頭に書いたとおり、この重さを許容できるのは、受け取る側も AI だからです。1万文字の提案を読んで裁定し、採用ぶんを実装して push するところまで自動で回る前提があって初めて、「当たりは少ないが当たると大きい」役を常設できます。