AIコードレビューの増分レビュー ─ 再pushのたびにPR全体を読み直していたのを、前回SHAからの差分に絞った話

当ページのリンクには広告が含まれています。
この記事の結論
AIコードレビューの再実行を軽くするなら、「前回どこまで見たか」を記録し、そこからの差分と前回の指摘だけを渡すのが効くと考えた話です。ただし、差分に絞れる条件を少しでも満たさないときは、迷わずPR全体のレビューへ戻す作りにしました。同じコミットの再実行で結果を使い回す仕組みも、検証できた「指摘なし」に限っています。

お疲れ様です!IT業界で働くアライグマです!

個人開発している開発関連ニュースのキュレーションサービス「DevPick」では、PRを出すたびにAIが自動でコードレビューをしています。2026年10月3日、このレビューが指摘を直して再pushするたびにPR全体を最初から読み直していた問題に手を入れました。今回は、前回の続きからレビューさせる「増分レビュー」と、同じコミットの再実行で結果を使い回す仕組みを入れた経緯と、導入後2日間の実際の動きをまとめます。

目次

指摘を1行直しただけで、PR全体を読み直していた

DevPickのCIでは、自動レビューを一番前に置いています。レビューで指摘が1件でも出たら、後ろのテストやE2Eは走らせません(この順序に組み替えた経緯はレビューで落ちるPRにテストもE2Eも流していたCIを組み替えた話に書きました)。つまり、指摘を受けたら直して再pushし、もう一度レビューを通すまでPRは先に進みません。

問題は、その2回目以降のレビューの中身でした。レビューを実行するスクリプトは、push のたびに同じプロンプトで最初からAIを起動していました。プロンプトには「毎回プロジェクトのルールファイル(CLAUDE.md)を読む」「PR全体の差分を取得する」「ロジックの変更があるファイルは丸ごと読む」と書かれています。そして、前回どのコミットまでレビューしたのか、前回どんな指摘を出したのかを渡す仕組みがありませんでした。

その結果、指摘を1行直して再pushしただけでも、AIはPR全体を一から読み直します。前回すでに問題なしと判断した箇所も、もう一度同じように読むわけです。

どのくらいの頻度で起きていたのかを数えてみました。2026年9月19日以降のPR 156件では、1件あたりのCI実行は平均1.9回でした。2回以上実行されたPRが70件、3回以上が31件あり、最も多いものは11回です。2回目以降の実行はすべて、PR全体のレビューになっていました。

項目 件数
2026年9月19日以降のPR 156件
1件あたりのCI実行 平均1.9回
2回以上実行されたPR 70件
3回以上実行されたPR 31件
最も多いPR 11回

レビュー前に「ドキュメントだけの変更」「軽微なスタイル修正だけの変更」を見分けて、AIレビューそのものを省く仕組みは別に入れていました。ただ、指摘を受けて直す再pushは大半がロジックの変更なので、その仕組みでは省けません。何度も繰り返される「指摘対応の再push」が、毎回PR全体の重いレビューを受けていたことになります。

IT女子 アラ美
1行直しただけで全部読み直しって、毎回答案を最初から採点し直してるのと同じよね。

ITアライグマ
まさにそうです。前回どこまで見たかを渡していなかったので、毎回初見になっていました。

前回レビューしたSHAを、ボットのサマリにだけ残す

増分レビューの前提は、「前回はどのコミットまでレビューしたか」が分かることです。Gitでは、コミットごとにSHAと呼ばれる一意の識別子(40桁の英数字)が付きます。前回レビューしたコミットのSHAが分かれば、git diff 前回のSHA..HEAD で「前回のレビュー以降に変わった分」だけを取り出せます。

そこで、レビューが終わるとPRに投稿されるサマリコメントの中に、レビュー対象のSHAを機械が読める形で埋め込むことにしました。HTMLコメントなので、PRの画面には表示されません。

<!-- devpick-review-sha: <レビューしたコミットのSHA> -->

次にレビューが走るとき、PRのコメント一覧から最新のサマリを探し、このマーカーから前回のSHAを読み取ります。サマリ本文に書かれた前回の指摘も、同時に取り出します。

ここで気をつけたのが、誰が書いたコメントを信じるかです。マーカーはただの文字列なので、PRにコメントを書ける人なら誰でも同じ形式の文字列を投稿できます。偽のサマリを置かれて、それを「前回のレビュー」と読んでしまうと、レビューの範囲を勝手に狭められてしまいます。そのため、サマリとして読むのはCIのボット(github-actions[bot])が投稿したコメントだけに限定しました。投稿者の情報が取れないコメントも、安全側に倒して読み飛ばします。

# 単純化した例:サマリとして扱うのはボットのコメントだけ
for c in comments:
    if SUMMARY_MARKER not in c["body"]:
        continue
    user = c.get("user")
    if not isinstance(user, dict) or user.get("login", "").lower() not in ALLOWED_AUTHORS:
        continue  # 第三者のコメントや、投稿者が分からないものは読まない
    summary_comments.append(c)

このボット限定の絞り込みは、増分レビューを入れた同じ変更の中で、マージ前に追加しています。最初の実装ではマーカーの有無しか見ていませんでした。

マーカーを誰が書き込むかも、その後に一度見直しました。導入時は、AIレビュアーへの指示に「サマリにSHAマーカーを入れる」と書き、レビュー後にもCIの別ステップでマーカーを書き足していました。翌日、レビュー結果の投稿を共通の投稿スクリプトにまとめた際に、サマリとマーカーはそのスクリプトが機械的に生成する形へ変えています。AIが指示どおりにマーカーを書くかどうかに、増分レビューの成否を預けない形です。

IT女子 アラ美
誰でも書けるコメントを信じたら、レビューの範囲を外から操作できちゃうわね。

ITアライグマ
はい。だからボットが書いたサマリだけを読み、マーカーもスクリプトが書く形にしました。

増分に絞れないときは、迷わず全体レビューへ戻す

増分レビューで一番避けたかったのは、「差分だけ見たら問題なかったので通した」という素通しです。差分の取り方を間違えたまま「指摘なし」を出してしまうと、レビューしていないコードが本番へ出ていきます。

そこで、増分レビューに切り替えるかどうかの判定では、条件を1つでも満たさなければPR全体のレビュー(以下、全体レビュー)に戻すことにしました。判断がつかないときに安全側(=いつもどおりの全体レビュー)へ倒す考え方で、fail-closedと呼ばれます。全体レビューへ戻るのは、次のどれかに当てはまるときです。

  1. 初回レビュー(ボットのサマリがまだ無い)
  2. サマリにSHAのマーカーが無い
  3. 今のHEADが、前回レビューしたSHAと同じ
  4. 前回のSHAが今のHEADの祖先ではない(rebase・force push など)
  5. 前回からHEADまでの間にマージコミットがある(mainの取り込み)
  6. 増分が大きすぎる(15ファイル超、300行超、差分本文30KB超のいずれか)
  7. GitやGitHub APIの操作でエラーが起きた

4番の「祖先ではない」は、たとえばrebaseで履歴を書き換えたケースです。前回レビューしたコミットが今のブランチの履歴から消えているので、「そこからの差分」という考え方自体が成り立ちません。判定には git merge-base --is-ancestor を使っています。

5番のマージコミットは、mainの変更をPRに取り込んだケースです。このとき 前回のSHA..HEAD の差分には、このPRで書いたコードではなく、PRの外でmainに入った変更が混ざります。PR自身の修正とmainの変更が組み合わさった結果を見直す必要もあるので、全体レビューに戻しています。

6番の上限は、「増分」と呼ぶには大きすぎる変更をはじくためのものです。大きな作り直しを差分だけで見ると、変更されていない周辺コードとの食い違いを見落としやすくなります。なお、差分本文の30KB上限と、7番のうち「差分の取得に失敗したら全体へ戻す」扱いは、増分レビューを入れた同じ変更の中で、マージ前に追加しました。

増分レビューに切り替わったときは、プロンプトに次の3つを渡します。

  • 前回のサマリに書かれていた指摘
  • 前回のSHAから今のHEADまでの差分(変更されたファイルの一覧と差分本文)
  • 「前回の指摘が直っているかを確認し、未解消なら引き続き指摘として残す」「新しく変わった箇所をレビューする」「変わっていない箇所の読み直しは不要」という手順

前回の指摘を渡すのは、「直したつもりで直っていない」を取りこぼさないためです。差分だけを渡すと、AIは新しく変わった行しか見ません。前回の指摘が残っているかどうかを確認させる手順を、明示的に入れておく必要がありました。

また、増分レビューで「指摘なし」になった場合も、後ろのテストやE2Eの扱いは全体レビューのときと変えていません。レビューの読み方を軽くしただけで、レビューを通ったあとの関門は同じです。

IT女子 アラ美
条件が7つもあると面倒そうだけど、迷ったら全部見るって決めておけば安心ね。

ITアライグマ
はい。増分に絞るのは「確実に絞れると分かったときだけ」にしています。

同じSHAの再実行は、検証できた「指摘なし」だけを使い回す

増分レビューを入れたあと、残っている無駄に気づきました。全体レビューに戻す条件の3番、「今のHEADが前回レビューしたSHAと同じ」です。

コードを変えていないのにCIがもう一度走る場面は、意外とあります。テストが不安定で落ちたときの再実行や、ランナーの都合でのやり直しです。このときは差分がゼロなので、増分レビューは成り立ちません。判定は全体レビューに倒れ、同じコードを最初から推論し直していました。

そこで同じ日の夜、「同じ条件で成功したレビュー結果があれば、それを使い回す」仕組みを別の変更として追加しました。ここで一番慎重にしたのは、何が同じなら「同じ条件」と言えるのかです。コードが同じでも、レビューのルールやプロンプトが変わっていれば、同じ結果になる保証はありません。最終的に、次の項目がすべて一致する場合だけを対象にしました。

  • PRのHEADとbase、およびマージ後のツリー
  • リポジトリとPR番号
  • ワークフロー・プロンプト・レビュー用スクリプト
  • プロジェクトのルールファイルとツール設定
  • AI CLIのバージョンと、モデルを指定する環境変数

手元のグローバル設定ファイルも、変わっていれば対象外にします。ルールファイルは内容のハッシュで比べ、認証情報を含みうるCLIの設定ファイルは中身を読まず、ファイルの更新時刻やサイズだけで変化を検出しています。記録やログに認証情報を残さないためです。

さらに、使い回す結果そのものも絞りました。対象は、CIのボットが今のHEADに対して投稿した、既知の決まった文面の「指摘なし」サマリだけです。指摘が出た結果、途中で終わった結果、文面が曖昧なもの、第三者が書いたマーカー入りのコメントは、すべて保存しません。自由文の「指摘なし」や、AIレビューを省いた事前判定の結果も、保守的に対象外にしています。

記録の保存と利用にも、確認を重ねています。

  • レビュー開始時の条件を一時ファイルに控え、終了時に条件が変わっていたら保存しない
  • 使い回す前に、GitHub APIで記録元の実行がレビュージョブまで成功していること、PRのHEAD・baseが今も一致することを確認する
  • 記録が無い・壊れている・別のランナーで実行された・APIの確認に失敗した、といった場合はすべて通常のレビューに戻す

使い回したときも、PRには今のSHAに対するサマリを投稿し、必須チェックも通常どおり通します。どの実行の結果を使い回したのかは、Actionsのログに記録元の実行として出力しています。使い回した側が元の記録を上書きして有効期間を延ばすこともしません。

効果は、同じSHAで通常のレビューと使い回しを比べて確かめました。通常のレビューはCLIの集計で33,143トークン、AI内でのコマンド呼び出しが3回でした。使い回した側はAIを1回も起動していません。ただし、APIでの照合やサマリの投稿といった処理は残ります。トークンの内訳(入力・出力・キャッシュ読み込み)は取れていないので、何割減ったという言い方はしていません。

IT女子 アラ美
使い回すだけなのに、確認の数が普通のレビューより多いくらいじゃない?

ITアライグマ
そうなんです。間違って使い回すと見落としになるので、確認はAIを使わない軽い処理で重ねました。

導入後の2日間で、実際にどう動いたか

設計どおりに動いているかを、ActionsのログからCIの実行を拾って確かめました。対象は、増分レビューが入った2026年10月3日の夕方から10月5日の夕方までに、2回以上CIが走ったPRです(依存パッケージ更新の自動PRは除いています)。ログには、どちらのレビューを選んだかと、その理由が出力されます。

=== Review Mode Decision ===
Mode: incremental
Reason: valid_incremental_diff (4 files, 51 lines, 4 files changed, 47 insertions(+), 4 deletions(-))

これは、トップページのページ送りを直したPRの2回目の実行です。このPRは4回レビューが走り、1回目は初回なので全体レビュー、2〜4回目はすべて増分レビューでした。2回目で見たのは、指摘を受けて直した4ファイル・51行だけです。

集計した結果は次のとおりです。所要時間は、AIレビューのジョブが始まってから終わるまでの時間です。

レビューの種類 回数 所要時間の中央値(最短〜最長)
増分レビュー 42回 80秒(52〜127秒)
全体レビュー(初回) 34回 121秒(66〜286秒)
全体レビュー(2回目以降で戻ったもの) 8回 119.5秒(50〜245秒)
同じSHAでの使い回し 0回 -

2回目以降のレビューのうち、42回が増分レビューになり、全体レビューへ戻ったのは8回でした。戻った理由で一番多かったのはmainの取り込みによるマージコミットの6回で、行数の超過と差分本文の上限超過がそれぞれ1回です。条件に引っかかったものはきちんと全体レビューに戻っていて、fail-closedの判定は狙いどおりに働いていました。

所要時間だけを見ると、同じ期間の全体レビューの中央値が120秒前後なのに対し、増分レビューは80秒です。ところが、同じ測り方で導入前の1週間(9月26日〜10月3日)も測ってみると、印象が変わりました。

導入前の1週間(すべて全体レビュー) 回数 所要時間の中央値(最短〜最長)
初回 107回 72秒(17〜171秒)
2回目以降 94回 81.5秒(44〜225秒)

導入前の「2回目以降」は81.5秒で、導入後の増分レビューの80秒とほとんど変わりません。時計の上では、増分レビューで2回目以降が速くなったとは言えない結果でした。変わったのは、全体レビューのほうが72秒から120秒前後へ遅くなったことです。

全体レビューが遅くなった時期を絞ると、翌朝にレビュー結果の投稿を共通のスクリプトにまとめた前後で差が出ていました。その変更より前は初回の全体レビューが中央値90秒・増分が56秒、後は129秒・84秒です。ただ、同じ時間帯にはレビューへの入力を整える別の変更も入っています。全体レビューが遅くなった原因をどれか1つに決めることは、この集計ではできませんでした。

同じ期間の中で比べると、増分レビューは全体レビューの6割台の時間で終わっています。ただしPRごとに中身も差分の大きさも違うので、同じ変更を2通りでレビューした比較ではありません。トークン消費の内訳もまだ取れていないので、増分レビューで何割減った、とは言わないことにしました。

同じSHAでの使い回しは、集計した実行の中では一度も発動していませんでした。理由を調べると、使い回しを入れたあとに同じSHAでCIをやり直したケースは7回ありましたが、7回とも「失敗したジョブだけを再実行する」やり直しでした。AIレビューのジョブは1回目に成功していたので、やり直しでは再実行されず、1回目の結果がそのまま引き継がれています(開始・終了時刻が1回目と完全に一致していました)。レビューのジョブそのものが走らなかったので、使い回しの出番がなかったということです。この仕組みが効くのは、すべてのジョブをやり直すときのように、成功済みのレビューまで再実行される場面に限られます。

まだ確かめられていないこともあります。増分レビューにしたことで、見落としが増えていないかです。前回の指摘を渡し、未解消なら残すように指示してはいますが、それで十分かどうかは、この先のレビュー往復と本番の不具合を見ながら確認していくつもりです。効果の測り方で迷った話は、CLAUDE.mdの二重読み込みを除外して効果を測ろうとした話にも書いています。

IT女子 アラ美
導入前と測り比べたら速くなってなかったって、ちょっと拍子抜けね。

ITアライグマ
はい。同じ期間の比較だけで判断していたら、効果を言い過ぎるところでした。

まとめ

今回の変更を振り返ると、次のような整理になります。

  • 前回どこまで見たかを残す:レビュー対象のSHAをサマリに埋め込み、次のレビューではそこからの差分と前回の指摘だけを渡すようにした。
  • 信じる記録を限定する:サマリとして読むのはCIのボットが書いたものだけにし、マーカーもAIではなくスクリプトが書く形にした。
  • 絞れないときは全体に戻す:rebase、mainの取り込み、大きすぎる差分、エラーのどれかがあれば、迷わず全体レビューにする。導入後の2日間では、2回目以降のレビュー50回のうち8回がこの判定で全体レビューに戻った。
  • 使い回すのは確信できる結果だけ:同じSHAの再実行では、条件がすべて一致し、APIで成功を確認できた「指摘なし」だけを使い回す。導入後の2日間は、やり直しがすべて失敗ジョブだけの再実行だったため、一度も出番がなかった。
  • 比べる相手を間違えない:同じ期間の全体レビューと比べると増分レビューは6割台の時間だったが、導入前の2回目以降(81.5秒)と比べると80秒でほぼ同じだった。全体レビュー自体が遅くなっていたためで、何割減ったとは言わない。

AIレビューのコストを減らそうとすると、「読む量を減らす」方向に目が行きます。ただ、レビューの読む量を減らすことは、そのまま見落としの入り口にもなります。今回は、減らしてよい条件を細かく決め、それ以外は従来どおり全部読むという線の引き方にしました。速さよりも「どこまで見たかを説明できること」を優先した形で、この判断が正しかったかは、しばらく運用しながら見ていきます。

IT女子 アラ美
楽をする仕組みほど、楽しちゃいけない条件をちゃんと決めておかないと危ないのね。

ITアライグマ
おっしゃるとおりです。省く条件より、省かない条件を先に固めたのが今回の一番の学びでした。

運営サービス・姉妹メディア

この記事をシェアする
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

ITアライグマのアバター ITアライグマ ITエンジニア / PM

都内で働くPM兼Webエンジニア(既婚・子持ち)です。
AIで作業時間を削って実務をラクにしつつ、市場価値を高めて「高年収・自由な働き方」を手に入れるキャリア戦略を発信しています。

目次