0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

公式の /code-review は「リーダブルコード観点」をどこまで見るのか — 可読性特化スキルを自作して比較した

0
Last updated at Posted at 2026-08-05

公式の /code-review は「リーダブルコード観点」をどこまで見るのか — 可読性特化スキルを自作して比較した

社内で「LLM 活用の課題」を洗い出したとき、コードレビューの品質が挙がりました。

Claude Code には Anthropic 公式の /code-review が最初から入っています。しかし、これはバグ・セキュリティ・パフォーマンスといった幅広い観点を扱う汎用性の高いレビューです。そのため、命名・コメント・制御フローといった可読性の観点をどこまで見ているのかは判然としません。

そこで、書籍『リーダブルコード』の観点に特化したレビュースキルを自作し、
同じコードに両方を当てて、どこまで差があるかを測りました。

「汎用レビューは可読性をほとんど見ないだろう」と予想していましたが、実際に比べてみると差はかなり小さいものでした。

流れを最初に置いておきます。

順 やったこと
1 社内の LLM 活用課題として「コードレビューの品質向上」が挙がる
2 可読性観点に特化したレビュースキルがあってもいいのでは、と思い自作
3 検証したいので、検証用サンプルコードと正解キーも LLM に作らせて採点
4 同じ検証セットを公式 /code-review にも当て、カバー範囲を比較 ← イマココ

すべて 1 日の中で実施した内容です。長期運用の知見ではないため、その点を踏まえて読んでください。


1. 作ったもの: readable-code-review

書籍『リーダブルコード』 の観点だけに絞ったスキルを作成しました。レビュー観点は、書籍の章立てに沿って定義しています。

  • 名前に情報を詰め込む / 誤解されない名前
  • コメントすべきことを知る(=コードを読めば分かることは書かない)
  • 制御フローを読みやすくする / 巨大な式を分割する
  • 変数のスコープと生存期間
  • 無関係の下位問題を抽出する
  • テストコードの読みやすさ

興味深かったのは、「何をレビューするか」よりも 「何をレビューしないか」 のほうが分量が多くなったことです。

やらないこと 理由
Lint で機械検出できる整形(インデント、セミコロン、import 順) 人間が読まないと分からないことに時間を使う
意図が読めない箇所を断定する 業務都合や外部システム制約の可能性がある。💬 質問に回す
性能・バグの指摘を可読性の指摘に混ぜる 「可読性の範囲外ですが」と別枠にする
点数をつける / 勝手に全体をリファクタする レビューの役割を超える
指摘を 10 件超並べる 上位に絞り、残りはまとめる

LLM に自由にレビューさせると、指摘の水増しと、知らない制約への断定が起きがちです。
例えば、「1 ファイルだけインデントが乱れています」や「DENNO は slipNumber にリネームしましょう」といった指摘は、技術的には間違っていません。しかし、レビューとしての価値は低く、場合によっては有害です。

出力フォーマットも固定し、各指摘に「場所・なぜ困るか・修正後のコード・重要度」の
4 点を揃えるようにしました。


2. 検証セットも LLM に作らせた

スキルを書いたら検証したくなります。ただ、手元に都合のいい「読みにくいコード」はありません。
そこで、検証用のサンプルプロジェクトと正解キーも LLM に作ってもらいました。

なお、ここでいう「正解キー」とは、サンプルコードに意図的に仕込んだ可読性上の問題点一覧です。後でレビュー結果を採点する際の基準として使用します。

2-1. サンプルプロジェクト

「受注同期バッチ」を模した TypeScript プロジェクト(461 行)。
EC の受注を基幹システムと WMS(倉庫管理)へ連携する、よくある業務バッチです。

sample-project/
├── src/order/       sync.ts / validator.ts / inventory.ts
│                    priceCalculator.ts / report.ts / types.ts
├── src/shared/      httpClient.ts / dateUtil.ts
├── src/legacy/      bizpoAdapter.ts
└── tests/           sync.test.ts

条件として重視したのは、ツールとしては正常に動く状態を保つことです。
npm run typecheck はエラー 0 件、npm test は 3 件すべて pass します。
「壊れているから読みにくい」のではなく、「動くけど読みにくい」を再現したかったからです。

2-2. 仕込んだ問題(正解キー)

区分 件数 内容
MUST 8 件 スキルが機能しているなら必ず拾ってほしい。再現率の分母
SHOULD 27 件 拾えれば加点。絞り込みルールがあるので全部拾う必要はない
トラップ 15 件 指摘したら減点。偽陽性の検出用

MUST の例を 2 つ。

// F-04: checkStock は「確認」の顔をして在庫を予約している
export async function checkStock(itemCode: string, qty: number): Promise<boolean> {
  const stock = await fetchStock(itemCode);
  const alreadyReserved = reserved.get(itemCode) ?? 0;
  if (stock - alreadyReserved < qty) return false;
  reserved.set(itemCode, alreadyReserved + qty);   // ← 名前にない副作用
  return true;
}
// F-02: 第 2 引数 f が何のフラグか呼び出し側から読めない
export async function doSync(d: Order[], f: boolean): Promise<SyncResult> {
  // ...55 行下...
  o.status = f ? '2' : '1';
}

2-3. トラップ側のほうが手が込んでいる

このスキルは「指摘しすぎない」ことも仕様なので、踏んだら減点の罠を仕込みました。
個人的には、ここが今回いちばん設計が楽しかった部分です。

  • 整形崩れ — 1 ファイルだけインデント・クォートが乱れており、ESLint が 14 件検出する。
    → 指摘したら減点(Lint の担当領域)
  • ローマ字の項目名 — DENNO TOKCD KINGAKU。ただし直上の JSDoc に
    「連携仕様書 v3.2 §4 の項目名にそのまま合わせている」と書いてある。
    → リネームを求めたら減点(外部仕様に合わせるのが正しい)
  • 理由コメントのある奇妙な処理 — 全角スペース埋めの固定長 20 文字。直前に
    「半角で埋めると受信側で桁ずれする(仕様書 v3.2 §4.1)」とある。
    → 指摘したら減点。良いコメントとして褒めたら加点
  • 理由コメントのない似た処理 — すぐ下に、理由が書かれていない 40 文字切り詰めを置いた。
    → 💬 質問で由来を聞くのが正解。断定したら減点
  • 十分に読みやすいファイル — httpClient.ts は例外クラスを 2 つに分け、
    JSDoc に @throws と実行例がある。
    → 指摘 0 件が正解
  • 性能問題 — ループ内 await の N+1、find の O(n²)。
    → 可読性の指摘に混ぜたら減点。別枠なら OK

3 つ目と 4 つ目をペアで置いたのがポイントです。ほぼ同じ見た目の処理で、
片方には理由コメントがあり、片方にはない。レビュアーが「コメントの有無」で
態度を変えられるかを見ています。


3. 検証方法

採点指標

指標 目標
MUST 再現率 7/8 以上
重要度一致率 検出した MUST の 75% 以上
偽陽性(トラップ命中数) 0 件
出力フォーマット準拠 12 項目中 10 以上

汚染を避ける

検証時のルールは次の 3 点です。

  • 採点はレビュー実行とは別セッションで行う

  • 同じセッションで正解キーを開くと、その情報が後続のやり取りに影響するためです。

  • レビュー時は sample-project/ のみを作業対象とし、正解キーが見えない状態にしました。

  • 依頼文に「リーダブルコード」という言葉は入れない

  • 「このプロジェクトのコードをレビューしてください」とだけ依頼します。

  • スキルの description が一般的なレビュー依頼でも発火するかを確認したかったためです。

  • 同条件で 3 回実行し、中央値を採用する

  • LLM の出力にはばらつきがあるためです。

この検証の限界(先に書きます)

  1. サンプルコードも正解キーも LLM 生成です。 「LLM が作った問題を LLM が解く」構図なので、
    出題側と解答側が同じ癖を共有している可能性は排除できません。
    (後述しますが、これは実際に問題になりました)
  2. 試行数が非対称です。 自作スキルは 6 回、公式 /code-review は各設定 1 回ずつ。
    公式側の数値は参考値として読んでください。
  3. 正解キーは自作スキル用に作ったものです。 「10 件に絞る」「質問に回す」は
    自作スキルの仕様であって、公式 /code-review はそんなルールを知りません。
    公式側の「フォーマット違反」は、その意味で不公平な減点です。

4. まず自作スキルを 6 本回した

3 回回して弱点が見えたので SKILL.md を直し、もう 3 回回しました。

指標 run1〜3(改善前) run4〜6(改善後)
MUST 再現率(中央値) 7/8 8/8
偽陽性 0 件 0 件
フォーマット準拠 11/12 12/12
SHOULD 検出数 — 22〜26 / 27

6 本すべてで偽陽性 0 件でした。整形崩れも、ローマ字項目名も、理由コメント付きの
全角パディングも、1 本も踏んでいません。むしろ 6 本全部が httpClient.ts を
「一番読みやすい」と冒頭で褒め、理由コメントを「維持してほしいパターン」として
引用していました。ペアで置いた仕掛けも効いていて、複数の run が
「TOKCD には理由が書いてあるのに BIKO には無いのはなぜか」と質問を返してきました。

性能問題も全本が「可読性の範囲外(参考)」として末尾に分離しています。

ここまでで「スキルとしては機能している」ことは確認できました。 残る問題は、
これが公式の汎用レビューに対してどれだけ上乗せになっているのか、です。


5. 同じ検証セットを公式 /code-review に当てる

Anthropic 公式の /code-review は Claude Code に最初から同梱されている組み込み機能です。
自作でもコミュニティ製でもありません。可読性に限定せず、
バグ・セキュリティ・パフォーマンスまで含めて見る汎用のレビューです。

汎用である以上、可読性観点の指摘が出ないわけではないはずです。
問題は「どの程度の粒度で、どの重要度で出るのか」。
特化スキルを維持する価値があるかは、そこを測らないと判断できません。

正解キーはすでにあるので、同じ土俵に乗せられます。2 パターン試しました。

  • 通常版 — そのまま /code-review を実行
  • max effort 版 — 出力上限を引き上げ、recall 重視の設定で実行

通常版の結果

指標 結果
MUST 再現率 3/8
偽陽性 0 件
修正コード 0 件(1 つも書かれていない)
質問 0 件

指摘 15 件はすべて「実行時にどう壊れるか」でした。可読性の指摘も混ざってはいますが
Medium 帯に降格しており、filterOrders の多義性や RATE = 0.1 の意味不明さといった
命名系はまるごと落ちています。

この結果から、デフォルトの /code-review だけでは可読性観点を十分に補えないことが分かりました。

max effort 版の結果

ところが、max effort 版では結果が大きく変わりました。

指標 結果
MUST 再現率 5/8 検出 + 2 件部分検出
SHOULD 検出数 約 22〜24 / 27(自作スキルとほぼ同水準)
偽陽性 2 件(性能話を可読性セクションに混入 / 17 件を絞らず列挙)
出力量 531 行・44KB(通常版の 4.8 倍)

max effort 版は、出力の中に 「Medium(可読性・構造 — リーダブルコード観点)」という
セクションを自分で作って
きました。三重否定の条件式、test1 というテスト名、
doSync(d, f)、flag の 4 段ネスト、const tmp = calc(o); const total = tmp; ——
自作スキルが指摘したのとほぼ同じ箇所を、同じ観点で指しています。

そのうえで、自作スキル 6 本が全部落とした欠陥を 11 件上乗せしてきました。

ID 内容
C-01 as { stock: number } のキャストで NaN < qty が false になり、在庫ゼロでも「在庫あり」を返す
C-02 基幹が 204 を返すと response.json() が例外 → catch {} が飲む → 同一伝票を 3 件登録して「失敗」と報告
H-02 在庫確認・引当の例外が try/catch の外。1 件の WMS 500 でバッチ全体が落ち、結果が消える
H-06 タイムアウトがヘッダ受信までしか効かず、ボディ停止でバッチが永久停止
H-08 備考の 40 文字切りが UTF-16 単位。絵文字でサロゲートペアが分断される
…他 6 件

しかも 「検証済み:」として実測値が付いています。モックの WMS と基幹サーバを立てて
実際に doSync を走らせ、ng に入ったのに WMS には引当が残っていることを
確認したうえで書かれていました。

念のため手元でも 4 件を実行確認しましたが、いずれも実在しました。

new Date('20260305')                       => Invalid Date
formatYmd(new Date('...T20:00:00Z')) の日  => 6(TZ=Asia/Tokyo、UTC なら 5)
('あ'×39 + '👍').slice(0, 40) の末尾       => "\ud83d"(孤立サロゲート)
calcLine 合計 1104  vs  calc 1105          => ¥335 × 3 行で 1 円ずれ

6. 何が違ったのか

数字を並べたので、実務に効く差だけ 3 つに絞ります。

差 1: 断定するか、質問を返すか

max effort 版は 質問が 0 件でした。すべて断定して「対処:」を書きます。

たとえば order.customerCode.startsWith('A') という条件について、
max effort 版は isPriorityCustomer(優待得意先)と命名して修正案を出しました。
一方、自作スキル 6 本は全部こう聞き返しています。

💬 customerCode.startsWith('A') の 'A' は業務上どういう区分ですか。
得意先区分か、契約種別か。仮に「優待得意先」と置きましたが、正しい呼び名に差し替えたいです。

どちらが正しいという話ではありません。ただ業務システムのレビューで
「勝手に決めた名前」入りの修正案が回ってくると、業務側の確認が一往復増えます
。

差 2: 指摘の単位と、着手のしやすさ

  • max effort — 1 件 = 1 つの故障経路。指摘同士を因果で連鎖させて書く。影響範囲の把握に強い
  • 自作スキル — 1 件 = 1 つの可読性原則。同じ sync.ts を「引数名」「flag」「日付の再実装」など
    6 件に分解し、それぞれに修正後のコードを付ける

前者は「何がどう壊れるか」の理解に向き、後者はそのまま実装者に渡せる差し戻し票になります。

差 3: 出力の重さ

出力量 実行コスト
/code-review 通常版 9KB 軽い
自作スキル 20〜28KB 軽い
/code-review max effort 44KB モックサーバを立てて実行検証。重い

max effort を毎 PR に回すのは現実的ではありません。


7. まとめ — 自作する意味はあったのか

「/code-review で賄えるのか」への答え

条件 賄えるか
デフォルトの /code-review No. MUST 3/8。命名系がまるごと落ちる
max effort の /code-review ほぼ Yes. MUST 5/8 + 部分 2、SHOULD は同水準

「可読性の指摘が欲しいだけ」なら、max effort の /code-review でかなり足ります。
自作スキルだけが拾えたのは最終的に 3 件で、しかもすべて
「実行しても壊れないが、読み間違える」タイプでした
(filterOrders の多義性、冗長コメント、sum/amount/total の使い分け)。

正直に言うと、想定よりずっと差は小さかったです。
「汎用レビューは可読性を浅くしか見ないだろう」という見込みで作り始めたので、
そこは測ってみないと分からなかった部分でした。

それでも消さないことにした理由

現時点の判断としては、残します。理由は 3 つです。

① コストと運用頻度が違う
max effort は出力が 4.8 倍で、実行検証まで走ります。リリース前の重要バッチには回せますが、
日常の PR には重すぎます。「毎回 44KB のレポートが飛んでくるレビュー」は、
結局読まれなくなります。

② 成果物の性格が違う
/code-review の出力は欠陥リストで、自作スキルの出力は差し戻し票です。
10 件に絞られ、各件に修正コードが付き、意図が不明な箇所は質問になっている——
これは「レビュアーの成果物」ではなく「実装者の作業指示」として設計しています。

③ 業務ルールを断定しない
業務システムでは、奇妙に見えるコードの相当数に理由があります。
理由コメント付きの全角パディングを 6 本すべてが指摘せず、逆に良い例として
引用したのは、その規律が効いた結果でした。

場面別の落とし所

場面 使うもの
日常の PR レビュー、実装者への差し戻し 自作スキル
リリース前、決済・在庫など壊れると痛い箇所 /code-review(max effort)
とりあえず何か見てほしい /code-review 通常版で十分

そして重要なのは、自作スキルを「バグも見るように」拡張しないことだと考えています。
可読性に特化しているからこそ偽陽性 0 件を 6 本連続で維持できたのであって、
役割を広げた瞬間にその強みは消えます。

1 日やってみての所感

  • 「公式にもある機能」の実力は、設定次第で大きく変わる。 同じ /code-review で
    MUST 3/8 と 5/8+2 に分かれました。デフォルトの出力だけ見て
    「汎用レビューはこの程度か」と判断していたら、間違った結論を出していました
  • 自作したこと自体は無駄ではなかったと思っています。「やらないこと」を明文化する過程で、
    自分たちがレビューに何を期待しているのかが言語化できました。
    そもそも比較の物差しになったのも、正解キーを先に作っていたからです
  • 検証セットは、スキルより長く使えます。 スキルを直すたびに同じ 3 回試行を回せますし、
    今回のように別のツールを当てることもできます

おまけ: 検証セットのほうが検証されてしまった話

冒頭で「サンプルも正解キーも LLM 生成」という限界を書きました。これが実際に問題になりました。
公式 /code-review を当てた結果、正解キー側のバグが 2 件見つかったのです。

1. 「dateUtil.ts は指摘 0 件が正解」が成立していなかった

模範として置いたはずの formatYmd が、ローカルタイムゾーンのゲッタを使っていました。
JSDoc に書いた実行例が TZ 次第で偽になります。つまり「ここは綺麗だから指摘するな」と
指定したファイルに、実際にはバグがありました。

2. 想定していなかった第三の反応が出た

slice(0, 40) について、正解キーは「40 の根拠を質問するのが正解、定数化を断定したら誤り」と
定めていました。ところが max effort 版は、数字ではなく 「slice が UTF-16 コード単位で数えるので絵文字が壊れる」 点を指摘してきました。
どちらの判定にも当たらず、採点表に行を足すことになりました。

出題側と解答側が同じ LLM だと、出題側の見落としがそのまま「正解」として固定される。
今回それが表面化したのは、別系統のレビュアー(公式 /code-review)を当てたからでした。

自作スキルを検証するつもりが、検証セットのほうが検証された——というオチです。
LLM に正解キーを作らせるなら、別のツールを当ててみるのは
それ自体が正解キーの健全性チェックになります。おすすめです。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?