18
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

15個の仕込みバグで比較:ClaudeとCodexのコードレビューは、モデルより「レビュー方式」で差が出た

18
Posted at

TL;DR

  • OpenAI公式の codex-plugin-cc を使うと、Claude Code のセッション内から Codex にコードレビューを依頼できる。
  • Claude Code には /code-review、Codex 側には /codex:review/codex:adversarial-review があり、レビューの聞き方を変えられる。
  • 今回は、わざと15個のバグを仕込んだURL短縮サービスを用意し、4つのレビュー方式を各2回ずつ試した。
  • 結果として、検出数の差は「ClaudeかCodexか」よりも、観点を明示して広く聞くか、専用コマンドでスコープを絞って聞くかの影響が大きそうだった。
  • ただし n=2 の小さな検証であり、モデル性能の優劣を結論づけるものではない。

検出数(2回中1回以上で検出 / 15個):

条件 方式 検出数
Claude自由記述 観点を明示した自由記述プロンプト 14
Claude /code-review 専用コマンド 12
Codex /codex:review 専用コマンド 11
Codex /codex:adversarial-review 専用コマンド(敵対的レビュー) 6

なお、/codex:adversarial-review は通常のバグ網羅レビューではなく、実装方針・設計判断・信頼境界に踏み込むレビューなので、単純な検出数だけでは評価しにくい。

環境: Claude Opus / effort high、Codex gpt-5.5 / effort high。


codex-plugin-cc とは

codex-plugin-ccOpenAI公式 のプラグインで、Claude Code に Codex を差し込み、Claude Code のセッションから離れずに Codex へコードを渡してレビュー・分析させられる。

発想の核は、別プロバイダの別モデルにレビューさせることで、sycophancy、いわゆる「おべっか」バイアスに引きずられにくい第三者レビューに近づける、という点にある。

今回はこれを口実に、「Claude と Codex のレビューは何がどう違うのか」を実測してみた。

セットアップ

Claude Code 内で順に実行する。

/plugin marketplace add openai/codex-plugin-cc
/plugin install codex@openai-codex
/reload-plugins
/codex:setup

/codex:setup がローカルの Codex CLI を検出し、認証と設定(~/.codex/config.toml)をひも付ける。別プログラムを新規に入れるわけではなく、マシン上の既存の Codex を同じ認証情報で呼ぶ。

主なコマンド:

コマンド 役割
/codex:review 通常のレビュー
/codex:adversarial-review 実装方針・設計判断に挑む攻撃的レビュー
/codex:status / /codex:result / /codex:cancel バックグラウンドジョブ管理
/codex:transfer セッションを Codex スレッドへ引き継ぐ

検証方法

題材コード

今回は「URL短縮サービス」を題材にし、 種類の異なるバグを15個 仕込んだ。正確性・並行性・セキュリティ・プライバシー・設計の各カテゴリにまたがるよう設計している。

// shortener.ts
// URL短縮サービス。POST /shorten で短縮、GET /:code で元URLへリダイレクト。
import express from "express";
import { Pool } from "pg";

const app = express();
app.use(express.json());

const pool = new Pool({ connectionString: process.env.DATABASE_URL });

const ADMIN_TOKEN = "YOUR_ADMIN_TOKEN"; // 管理APIトークン(例)
const ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const cache = new Map<string, string>();

function encode(id: number): string {
  let s = "";
  while (id > 0) {
    s = ALPHABET[id % ALPHABET.length] + s;
    id = id / ALPHABET.length;
  }
  return s;
}

app.post("/shorten", async (req, res) => {
  try {
    const { url } = req.body;
    console.log(`shorten ip=${req.socket.remoteAddress} url=${url}`);

    const row = await pool.query(`SELECT MAX(id) as max FROM links`);
    const nextId = Number(row.rows[0].max) + 1;
    const code = encode(nextId);

    pool.query(
      `INSERT INTO links (id, code, url, hits) VALUES (${nextId}, '${code}', '${url}', 0)`
    );
    cache.set(code, url);

    return res.json({ ok: true, code });
  } catch (e) {
    res.json({ ok: true, code: "xxxxxx" });
  }
});

app.get("/:code", async (req, res) => {
  const code = req.params.code;
  let url = cache.get(code);
  if (!url) {
    const row = await pool.query(`SELECT url FROM links WHERE code = '${code}'`);
    url = row.rows[0].url;
    cache.set(code, url);
  }
  const hits = await pool.query(`SELECT hits FROM links WHERE code = '${code}'`);
  pool.query(`UPDATE links SET hits = ${Number(hits.rows[0].hits) + 1} WHERE code = '${code}'`);
  return res.redirect(url);
});

app.listen(3000);

仕込んだバグ(抜粋):

  • 正確性/並行処理: encodeMath.floor 漏れ、INSERT の await 漏れ、SELECT MAX(id)+1 の採番レース、hits の lost update、存在しない code での rows[0] クラッシュ、Number の 2^53 精度
  • セキュリティ: SQLインジェクション、トークン直書き、オープンリダイレクト
  • プライバシー: IP+URL のログ出力
  • 設計/ドメイン: 連番コードの列挙可能性+レート制限なし、レイヤリング欠如、catch がエラーを握り潰して成功を偽装
仕込んだバグ15個
ID 箇所 内容 カテゴリ
S1 encode() id = id / ALPHABET.lengthMath.floor がなく非整数化。コードが壊れる 正確性
S2 各クエリ url / code をテンプレート文字列に直挿し → SQLインジェクション セキュリティ
S3 ADMIN_TOKEN シークレットをソース直書き(かつ未使用) セキュリティ
S4 console.log クライアントIP+フルURLをログ出力(PII) プライバシー
S5 SELECT MAX(id)+1 採番の原子性なし → 並行リクエストでid/code重複 並行性
S6 INSERT await漏れのfire-and-forget。順序保証なし+unhandled rejection 正確性
S7 cache (Map) 無効化もサイズ上限もない → 陳腐化+メモリ無限増加 正確性/設計
S8 hits = read+1 非アトミックUPDATE → 並行でlost update 並行性
S9 catch (e) エラーを握り潰し { ok: true } を返し成功を偽装 信頼性
S10 res.redirect(url) URL未検証 → オープンリダイレクト/フィッシング セキュリティ
S11 rows[0] コード不在時に例外。GET側は try/catch なし 正確性
S12 採番/エンドポイント 連番コードで列挙可能+認証・レート制限なし 設計/ドメイン
S13 ハンドラ全体 レイヤリング・トランザクション境界なし 設計
S14 全体 catch-allルート、マジックナンバー、エラー形状不統一 軽微
S15 Number(...max) JS Numberで 2^53 超の精度喪失(bigint未使用) 正確性

4つのレビュー方式

条件 コマンド 測るもの
A. Claude自由記述 観点明示プロンプト レビュー観点を明示して、広めにレビューを依頼した場合
B. Claude /code-review /code-review shortener.ts Anthropic作り込みのレビュー手順
C. Codex /codex:review /codex:review Codexの通常レビュー
D. Codex敵対的レビュー /codex:adversarial-review 実装方針・設計判断・信頼境界への踏み込み

A の固定プロンプトは以下の通り。

shortener.ts を 正確性 / セキュリティ / 並行性 / プライバシー / 設計の観点でレビューし、具体的な指摘を file:line 付きの箇条書きで出してください。

このため、A は単に「Claudeに自由記述で聞いた場合」ではなく、正確には「レビュー観点を明示して、広めにレビューを依頼した場合」として扱っている。

公平性のルール

  • 同一ファイル・同一観点・各2回。毎回 新規セッション→実行→出力保存→破棄で独立性を確保。
  • モデルと effort を開示(Claude Opus/high、Codex gpt-5.5/high)。
  • 採点は仕込み15項目を ◎(検出)/ △(部分)/ ✗(未検出)で評価し、誤検知も別記録。

ただし後述する通り、Codex 側はワーキングツリー全体に目を向ける場面があり、対象ファイル単体を見た Claude 自由記述とはスコープが完全には揃っていない。この点は今回の検証の限界として扱う。

結果

カテゴリ別検出数(≥1run / 仕込み数)

カテゴリ 仕込み A B C D
正確性/並行処理 7 6 6 6 5
セキュリティ 3 3 3 3 1
プライバシー 1 1 1 1 0
設計/ドメイン 2 2 0 0 0
信頼性/その他 2 2 2 1 0
15 14 12 11 6

代表的な指摘の違い

全員が拾ったもの
encode の小数除算バグは、4条件すべてが「id = id / ALPHABET.lengthMath.floor がなく、小数インデックスで undefined が混入する」と的確に指摘した。SQLインジェクション、採番レース、await漏れも同様に全員が検出した。

このあたりのコアな正確性・並行処理バグについては、大きな差がつかなかった。

Claude自由記述だけが拾ったもの
差が出たのは設計/ドメイン寄りの指摘だった。

連番IDをそのまま base62 化しているため「コードを列挙すれば他人のURLを覗ける」という列挙攻撃の指摘、および「ルーティング・DBアクセス・採番・キャッシュが1ファイルに密結合していて層分離がない」という設計指摘は、Claude自由記述のみが出した。

/code-review/codex:review/codex:adversarial-review は、いずれもこの2点を見送った。

仕込み外で見つかった実問題
/codex:review の2回目と /codex:adversarial-review の両runは、shortener.ts ではなくローカル設定(例: .claude/settings.local.json)の Read 権限が広すぎる点を「信頼境界の越境」として指摘した。

これは仕込んだ15個のバグには含めていなかったが、実運用上はかなり意味のある発見だった。

考察

今回の結果から見えたのは、「ClaudeとCodexのどちらがレビューに強いか」という単純な話ではなく、レビュー方式によって拾いやすい指摘が変わるということだった。

正確性・並行処理(encode、採番レース、await漏れ、lost update、null crash)は全条件がほぼ互角で、少なくとも今回の検証では、ここに大きなモデル差は見られなかった。

効いていたのはモデルより「レビュー方式」

差が出たのは設計/ドメインで、ここを拾えたのは Claude自由記述のみだった。

ただし、これは「Claudeモデルが設計に強い」というより、観点明示の自由記述レビュー vs 専用コマンドのスコープ限定レビュー という方式差が主因だと考えている。根拠は3つある。

  1. A vs B(同一モデル)
    どちらも Claude(Opus)なのに、観点を明示した自由記述(A)は14件、/code-review(B)は12件で、設計系(S12 列挙可能性 / S13 レイヤリング)を落とした。ここから、同じモデルでも、レビュー観点をどこまで明示するか、あるいは専用コマンドのハーネスが何を対象にしているかで、拾う指摘が変わることが分かる。

  2. B ≒ C(異モデル・近い方式)
    /code-review/codex:review は検出セットがかなり近く、どちらも設計系を見送った。モデルは違っても、追加設定なしで確信度の高いバグを返すレビューコマンドとして、似た挙動を示した。

  3. effort を max に上げても設計系は出なかった(追試)
    当初は「/code-review が件数を絞っている、つまり recall を捨てているから設計系を落とすのでは」と考えた。そこで /code-review shortener.ts max を回したが、結果は依然として正確性バグ+セキュリティ+クリーンアップの同じレーンで、S12・S13 は未検出だった。

つまり差の原因は、単なる件数の絞り込みではなくスコープにありそうだ。/code-review は、正確性バグや reuse / simplification / efficiency のクリーンアップに寄ったレビューであり、脅威モデルやドメイン設計はそもそも守備範囲外なのだと思う。

max 版は、件数を増やすというより、指摘の確信度や検証の厚みを増す方向に働いていた。

敵対的レビューは別の軸

/codex:adversarial-review は、仕込んだバグの検出数だけを見ると最少だった。ただし、これは通常レビューの下位互換というより、そもそも見ている軸が違う。

実際、/codex:review の一部runと敵対的レビューでは、shortener.ts の外にあるローカル設定(例: .claude/settings.local.json)の Read 権限が広すぎる点まで踏み込み、信頼境界の問題として指摘した。

これは、通常の「このファイルに何個バグがあるか」という観点では拾いにくい指摘である。敵対的レビューは、バグの recall を広げるというより、実装方針・前提・信頼境界を揺さぶる用途で効くと考えた方がよさそうだ。

正直な注意点

  • スコープが非対称
    Codex が .claude ファイルを指摘したのは、対象ファイル単体ではなくワーキングツリー全体を見ていた証拠でもある。shortener.ts 限定で聞いた Claude自由記述とは前提が完全には揃っていない。

  • S15(Number の 2^53 精度)は全条件が未検出
    現実には2^53件規模で初めて発火する理論エッジであり、今回の比較対象としては弱かった可能性が高い。

  • 明確な誤検知はほぼ見られなかった
    少なくとも今回の出力では、どの条件も的外れな指摘はほぼ出さず、品質自体は総じて高かった。

  • n=2(max は n=1)
    集合のばらつきは観測されたが、確定的な結論には更なる試行が要る。特に max-effort の追試は1サンプルなので、スコープ説をより固めるには複数回の再実行が望ましい。

実運用への落とし込み

今回の結果から、AIコードレビューは「どのモデルを使うか」だけでなく、「どういうレビュー方式で聞くか」を分けて考えた方がよいと感じた。

バグの一次検出

/code-review/codex:review のような純正コマンドが速くて高シグナルだった。正確性・並行性・セキュリティの明らかなバグを拾う用途では十分に強い。

PR前のセルフチェックや、実装後のゲートとして挟むにはかなり使いやすい。

設計・ドメインのレビュー

設計やドメインの妥当性まで見たい場合は、自由記述プロンプト、あるいは人間レビューを組み合わせた方がよい。

純正コマンドは、スコープ上バグ+クリーンアップに寄るため、「この設計でよいか」「このドメインで危ない前提はないか」までは任せきれない。

たとえば以下のように、観点を明示して聞いた方がよい。

正確性 / セキュリティ / 並行性 / プライバシー / 設計 / ドメイン前提の観点でレビューしてください。
実装バグだけでなく、仕様・脅威モデル・運用上のリスクも含めて指摘してください。

クロスプロバイダの二次レビュー

codex-plugin-cc で別モデルに通すと、同じClaude Codeのセッション内にいながら、別プロバイダの視点を入れられる。

これは単に「もう1つのモデルに聞く」というより、レビューの前提をずらす効果がある。特に、信頼境界や設定ファイルのように、対象コードの外側にあるリスクを見つける可能性があるのは面白い。

敵対的レビュー

敵対的レビューは、通常レビューの代替というより、設計判断や信頼境界を揺さぶる用途で使うのがよさそうだ。

「この実装にバグはいくつあるか」ではなく、「この実装方針はそもそも危なくないか」「攻撃者ならどこを見るか」「権限や設定の前提は妥当か」を見たい時に向いている。

まとめ

「ClaudeとCodex、どちらのレビューが鋭いか」と問うと、少し本質を外す。

今回の小さな検証では、検出結果を左右していたのはモデル名そのものよりも、レビューの聞き方だった。観点を明示して広く聞くと設計・ドメイン寄りの指摘まで拾いやすく、純正レビューコマンドは確信度の高いバグやクリーンアップに寄る。さらに敵対的レビューは、通常のバグ検出とは別に、信頼境界や前提の妥当性を見る用途で効きそうだった。

codex-plugin-cc の面白さは、Claude Code の中にいながら、こうしたレビュー方式の違いを手軽に試せるところにある。

バグ検出は純正コマンド、設計レビューは観点を明示した自由記述や人間レビュー、前提の揺さぶりは敵対的レビュー、と使い分けると、AIコードレビューの取りこぼしを減らせそうだ。

18
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?