1
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?

AIコードレビューの指摘、76%は繰り返しだった。過去355件をマイニングして開発ルールを鍛える

1
Last updated at Posted at 2026-07-29

何をしたか

PRを作成すると、GitHub Actions上のClaude Codeが自動でコードレビューを投稿する運用をしている。
指摘はだいたい当たっている。
当たっているからこそ、毎回のように修正コミットとやり取りが発生して、それなりに面倒だった。
そこで、過去のマージ済みPRに付いたレビュー指摘をすべて収集して分類し、繰り返し指摘されているパターンを開発ルール(CLAUDE.mdと.claude/rules/)へ反映した。

結果は次のとおり。

  • マージ済み176件のPRのレビューを走査し、355件の指摘を抽出した
  • そのうち76%は、事前にルール化できる繰り返し型だった
  • ただし、ルールを文書に書くだけでは防げない。すでに明文化されていたのに26件のPRで指摘され続けた項目があった

以下、収集から反映先の設計までを、そのまま再現できる手順で書く。
対象はLaravel + Livewireの業務システムだが、GitHubで自動レビューを運用しているリポジトリなら手順はそのまま使える。

収集、並列分類、重み付き集計、3層へ反映の4ステップ

レビューコメントの全件収集

コメントはPRごとに取得しない。
PRごとに叩くと数百リクエストになるが、リポジトリ単位のエンドポイント2本なら、ページネーション込みで数回のリクエストで全件取れる。

# トップレベルのレビューコメント(PRの会話欄)
gh api "repos/{owner}/{repo}/issues/comments?per_page=100" --paginate \
  --jq '.[] | select(.user.login | test("claude"; "i")) |
        {pr: (.issue_url | split("/") | last), body: .body}' > toplevel.jsonl

# inlineコメント(コード行に付くもの)
gh api "repos/{owner}/{repo}/pulls/comments?per_page=100" --paginate \
  --jq '.[] | select(.user.login | test("claude"; "i")) |
        {pr: (.pull_request_url | split("/") | last), path: .path, body: .body}' > inline.jsonl

# マージ済みPRのリスト(絞り込み用)
gh pr list --state merged --limit 500 --json number,title > merged-prs.json

botのlogin名は環境によって違う(github-actions[bot]名義で投稿される構成もある)ので、test("claude"; "i")の部分は自分の環境に合わせる。

取得後、マージ済みPRの番号で絞り、「指摘事項」「結論」などのマーカーを含むコメントだけをレビューとして残す。
今回はこのフィルタ後で176PR分、トップレベルとinline合わせて約65万字が残った。

サブエージェントによる並列分類

65万字は1つのコンテキストに収まらない。
PRごとにまとめたテキストを約8万字ずつ8チャンクに分割し、Claude Codeのサブエージェント8本へ並列で投げた。
1つのPRのレビューが2チャンクに跨がると分類の重複や欠落を生むため、分割は必ずPRの境界で行う。

各エージェントへの指示の骨子は次のとおり。

  • 「指摘事項」だけを抽出する。「良い点」のセクションは無視する
  • 各指摘を次の形式のJSONにする
    • severity(指摘の深刻度):blocker / conditional(条件付きマージ可)/ minor / info の4段階
    • category:提示した17カテゴリから選ぶ。合わなければ新設してよい
    • summary:80字以内の要約
    • generalizable:ルール化できる繰り返し型か(true / false)
    • rule:trueの場合、一般化したルール文を1文で書く
  • 結果をJSONファイルに書き出す(8本分のJSONは最後に1ファイルへ結合して集計に回す)

実際に使ったプロンプトを載せておく。

分類プロンプト(カテゴリ一覧つき)
あなたはPRレビューコメントの分析担当です。
chunk-N.md を読んでください。これはマージ済みPRに対するAI自動レビューのコメント集です。

タスク: 「指摘事項」(修正を求められた/推奨された内容)をすべて抽出し、構造化してください。
「良い点」セクションは無視。「参考情報」はseverity=infoとして含める。

各指摘を以下のJSONオブジェクトにする:
- pr: PR番号(数値)
- severity: "blocker" | "conditional"(条件付きマージ可) | "minor" | "info"
- category: 以下から最も近いものを1つ。当てはまらなければ新カテゴリを短い日本語で作ってよい:
  "兄弟・類似箇所への修正漏れ", "テスト観点漏れ・テスト不備", "用語集との齟齬",
  "ORM操作規約違反", "N+1・パフォーマンス", "バリデーション不備", "権限・認可漏れ",
  "エッジケース・null安全", "ブランチ運用・リリースノート", "命名・コード規約",
  "フレームワーク固有の罠", "トランザクション・整合性", "UI・表示不具合",
  "ドキュメント・コメント不備", "既存バグの指摘(PR範囲外)", "設計・アーキテクチャ",
  "デッドコード・不要コード"
- summary: 指摘内容の1文要約(80字以内)
- generalizable: true/false — 「事前のルール・チェックリストで防げた繰り返し型」か、
  そのPR固有の文脈がないと気づけないものか
- rule: generalizable=true の場合のみ、一般化したルール文を1文で

結果を JSON 配列として findings-N.json に書き出してください。

分類軸の中ではルール化できる繰り返し型かが要になる。
「そのPR固有の文脈がないと気づけない指摘」と「毎回同じことを言われている指摘」を分けることで、ルール化に値する対象だけが残る。

355件のうち76%がルール化できる繰り返し型、24%がPR固有の文脈依存

重み付き集計とランキング

指摘の深刻度(severity)に重みを付けてカテゴリ別に集計した。
重み付きスコアの降順で、上位は次のようになった。

指摘カテゴリ上位6件の重み付きスコア。1位はテスト観点の漏れで65.0

表で見る
カテゴリ 件数 出現PR数 ルール化可能率 スコア
テスト観点の漏れ 60 56 92% 65.0
ドキュメントとコメントの同期漏れ 41 36 78% 41.7
命名とコード規約 32 32 56% 31.2
エッジケースとnull安全 25 23 68% 28.5
兄弟箇所への修正漏れ 23 22 96% 27.4
ブランチ運用とリリースノート 26 26 69% 26.6

個別に見ると、指摘には型があった。
たとえばテスト観点の指摘は、次の数パターンで大半を占める。

  • 値なし時のフォールバック表示(「-」など)は、エラーが出ないことだけでなく表示自体をアサートする
  • 権限のテストは、許可される側だけでなく拒否される側も書く
  • バリデーション失敗のテストは、データが保存されていないことも確認する
  • 同じ修正を複数箇所に入れたら、テストも全箇所に対称に揃える

ルール化可能率96%の「兄弟箇所への修正漏れ」は、不具合をパターンとして修正したのに、同じ構造を持つ別のメソッドや画面への展開を忘れる、という指摘で、「修正後に同構造をgrepで洗い出す」という1行にルール化できる。

断っておくと、76%はサブエージェントが付けた分類フラグをそのまま集計した値で、355件すべての目視検証はしていない。
ただし反映対象にした上位カテゴリのルール文200行あまりには目を通しており、明らかな誤分類は見当たらなかった。

反映先の3層への振り分け

集計結果をそのままCLAUDE.mdに追記したくなるが、そうするわけにはいかない。
CLAUDE.mdは毎セッション全文がコンテキストに載るため、1行増やすごとにすべての作業のコストが上がる。
今回は3層に振り分けた。

  1. PR作成前チェックリスト(新設、常時ロード):横展開、テスト観点、ドキュメント同期、ブランチ運用、後片付けの確認項目を1ファイルに集約する。CLAUDE.mdには「PR作成直前に必ず通す」という実行指示を1行だけ書く
  2. paths付きルールファイル(新設):テスト観点の詳細は.claude/rules/testing-guidelines.mdに置き、frontmatterのpaths: tests/**/*.phpでテスト編集時だけロードさせる
  3. 既存ルールファイルへの追記:フレームワーク固有の罠(Livewireの#[Computed]をメソッド呼び出しで参照するとメモ化が効かない、など)は、既存のコーディング規約ファイルへ数行ずつ追記する

3層の振り分け。チェックリストは常時ロード、ルールファイルは対象ファイル編集時のみロード

チェックリストに実行タイミングを付けた理由

集計の途中で気づいたことがある。
ランキングそのものより、こちらの方が役に立った。

ブランチ運用とリリースノートに関する指摘は、運用ルールがCLAUDE.mdに明文化されているにもかかわらず26件のPRで出ており、直近のPRでもまだ出ていた。

ルールが読まれていないのではない。
CLAUDE.mdは毎セッション必ずコンテキストに載るため、届いていないという説明は成り立たない。
実装中の判断には反映されているのに、PRを作成する瞬間に差分と照合されていない、と見るのが自然だ。

だからチェックリストには、「PR作成直前に差分と突き合わせる」という実行タイミングを明記した。
LLMに向けたルールは内容と同じくらい適用タイミングの指定が効く、というのがこの26件から引き出した結論だ。

限界と効果測定

残り24%の指摘は、そのPR固有の文脈がないと気づけないものだった。
仕様理解の誤りや設計判断への指摘はルールでは防げないため、そこは自動レビューの仕事として残る。

info(参考情報)レベルの指摘までルール化するのも避けた。
ブロッカーにならない指摘を規則にしても、先に述べた「1行増やすごとに全作業のコストが上がる」問題が積み上がるだけになる。
今回は深刻度で足切りし、繰り返し性の高いものだけを反映した。

効果はこれから測る。
反映後のPRでカテゴリ別の指摘件数を追えば、どのルールが効いてどれが空振りかの見当がつく。
減らないカテゴリがあれば、次はルールの文面ではなくレビュー側のプロンプトを疑う。

まとめ

同じことを試すなら、最初の一歩は収集コマンド1本でいい。
分類まで自動化しなくても、自分のリポジトリの指摘を眺めるだけで、何が繰り返されているかはだいたい見える。
反映の効果はこれから数十PR分の指摘件数で測り、動きが見えたら続報を書く。

1
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
1
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?