不要なレビューをAIにまかせてAIコーディングの環境改善を加速した

こんにちは。こんばんは。 Findy Team+ 開発のフロントエンドリードをしている @shoota です。 今回はフロントエンドからは少し離れ、AIによるプルリクエストのレビューシステムを作成した話を書きます。

Findy Team+フロントエンドの現状と課題

過去のブログでも触れていますが、Findy Team+のフロントエンドは、非常に大きなモノレポで構成されています。

tech.findy.co.jp

このため、CIの最適化・チューニングはもちろんのこと、コードベース全体の秩序の品質を維持するためにさまざまな工夫や制約をもって運用してきました。 AIコーディングが主流になってからは更にこれらを強化し、制約やルールの策定、ESLintやユニットテストなどのガードレール整備や強化をおよそ1年をかけて進めてきました。 (この内容については別で発表したいと思います)

これらの投資とAIモデルの進化により、これまでバックエンドを中心に活動しているメンバーのフロントエンドの参入障壁を大きく下げることに成功しています。 現在では、AIが生成するコードの質は1年前から大きく向上していると体感しています。

AIコーディング時代の新たな課題

しかしここで新たな課題が生まれました。それは、「AIが真似すべきでないコードの修正や、それらを防ぐルールの導入のためのレビューコストが異常に高い」ということです。

  • AIの活用のためにコードを直し続ける活動は巨大なコードベースではかなりコストがある
  • 技術的な障壁よりも単純な物量がつらく、対抗する手段がない
    • ESLintのルールを1つ変更するだけでプルリクエストが大量に必要になる
      • 特定のルールのちょっとした厳格化で25件のプルリクエストを作成
      • あたらしいルールを追加したときは150件のプルリクエストが必要になった
    • 軽微な変更でも意味のある粒度で修正しなくてはレビューできないため、ひたすらに数を重ねる

このような事象が定常的に発生しており、次のFindy Team+でのグラフでも確認できます。

大量のプルリクエストが発生する様子

Vitestの一部のassertionがdeprecatedになったことに対して、私が一人でClaude Codeを回し、1日に72件、2日で100件以上のプルリクエストを作成しました。 レビュー待ちが多く発生する割に、内容は関数の置き換えなのでレビュー価値が低いプルリクエストが大量に必要になったのです。

このような 「AIを利用して品質を維持するための整備が人間を苦しめている」 という状況は、近頃話題になっていたHustle Pornに近いのではないか?と考えました。

プルリクエストをたくさん書くことは素晴らしいですが、そこに意味のある粒度と価値が伴わなければなりません。

AIによる自動レビュー(Approve)を作ろう

このような背景からレビュー負荷を可能な限り低減させ、ただしい怠惰を取り戻すために、AIレビューの仕組みを作成することにしました。 まずはやりたいことから全体の構想とコンセプトを決めていき、次のような内容になりました。

解決したい課題

  • プルリクエストを高速に作り続けることはむしろ歓迎すべき
  • 課題の本質は「レビュープロセスが対ひとでしか機能していない」こと
    • 人の時間をうばう
    • 人のアクションを待つ
    • 両者にメリットが少ない

レビューは大きく3種類ある

  1. レビューする価値がないことを確認するレビュー。レビューというワークフローを実施しているだけであり、本質的なレビューはしていない。レビューワークフローに相乗りしている儀式のひとつ。
  2. 品質、プログラム観点としてのコードレビュー。いわゆる本質的なコードレビュー。
  3. レビュー結果が反映されていることのレビュー。論点が絞られており、コードの内容がレビュー指摘に即して修正されているかを「確認する」だけの作業的な儀式。

今回は1. の「レビューする価値がないことを他者が保証する儀式」をAIにやらせることをゴールとします。

誰が使えるのか

初手はフロントエンドのレビュワーチームのみに絞る

  • 最大リスクは、誤判定や手順のハックによるレビューのザル化
  • フロントレビュワーチームはセルフレビューで一定の品質担保ができている前提で考える
  • 条件的に一般解放は想定するが、判定の成功率データがたまるまではリスクのほうが大きい

何を判定するのか

  • 要はプルリクエストの分類器をつくる
  • Claude CodeのReview (~$25) は必要がない
  • 変更内容から人間がレビューする必要があるかどうか?を判定する
    • 振る舞いが変わらない構造的なリファクタリング
    • code generatorなどの既存の機械的なワークフローの結果も不要と判断する

判定のキモはTidy First?の分類

レビューの必要性を測る判定処理に、構造的なリファクタリングという概念を持ち込みました。 これは昨年の弊社のイベントでキーノートを務めていただいたKent Beck氏の書籍Tidy First?から引用した言葉です。 この書ではリファクタリングの分類が紹介されており、リファクタリングを効率的に進めるための作業を指します。 和訳では「整頓」と表現されています。

  • 振る舞いの変化を伴わない、「コードの移動・分割」、「名前の変更」、「配置の変更」
  • プログラム(群)の凝集度に対するリファクタリングアプローチ
  • これらの修正は人間が目で追うのは意外と面倒で、レビューコストが割に合わない

例)ファイル移動や統合、変数・関数のリネームor削除、関数統合・分離

www.oreilly.co.jp

Tidy First?に関する考察は非常に多く議論されているので、Claude Codeに調査させながら「人間のレビューが不要」の定義を確定し、プロンプトに起こしていきました。 次に示すのがそのパターンの概要です。詳しい内容はぜひ書籍を確認していただけるとよいかと思います。

  • 対象パターン(T1〜T14)

    • T1: ファイル移動・リネーム
    • T2: import整理・barrel export
    • T3: 型定義の移動・分離
    • T4: コンポーネント・関数の分割・統合・抽出
    • T5: 変数・関数・propsのリネーム(テスト/モックへの機械的波及含む)
    • T6: ESLint/Prettier修正
    • T7: 不要コード・ファイル削除
    • T8: 非推奨APIの機械的移行(1:1対応)
    • T9: GraphQL codegen実行結果
    • T10: Nx generator実行結果
    • T11: ガード節への変換
    • T12: 説明変数・定数の導入
    • T13: Feature Flag名の追加
    • T14: コメントの追加・修正・削除
  • NGパターン(NG1〜NG7)

    • 新条件分岐、新ビジネスロジック、API変更、状態管理変更、テスト追加、UI変更、設定値変更

実装とポイント

全体の要件が決まったのでここからは実装です。

GitHub Actions

GitHub Actionsをランナーとし、プロンプトファイルを読み込んだうえでClaude Code Actionsに渡して実行、結果をパースしてレポートする非常にシンプルなフローです。 またFindy Team+はCI結果を送ってCIの実行分析ができるので、これも組み込んであります。

GitHub Actionsのフロー

Claude Code Actions

Claude Code Actionsの呼び出しのポイントは--max-turnsです。 これはClaude Codeが処理を何手まで行うかを制限するもので、変更ファイル数やそれらのファイルの関連性によって前後します。

      - name: Classify PR changes
        id: claude
        uses: anthropics/claude-code-action@5d5c10a4f389689f992ea10bb14dcb6fcc83146d # v1.0.102
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          claude_args: '--max-turns 20'
          prompt: ${{ steps.prompt.outputs.content }}

何パターンかの構造的なリファクタリングのプルリクエストで試したところ〜15 turnsにおさまるようでしたが、余裕を見て20を設定しています。 これを設定しておくことでプロンプトの誤認や想定しない大きな差分で実行された場合でも「判定不能」で安全に終了するようにしています。

ワークフローの変化とプロンプトの改善

何度か試していくうちに、ひとつの気づきがありました。

これまでプルリクエストは 「人間が認知できる変更量」にサイジングするようにすべき でしたが、AIは機械的にこれを分析できるので「同じ作業なら差分が大きくても1つのプルリクエストにまとめる」ことができる、ということです。 そのため大きな差分でもApproveができるような負荷耐性をもたせる必要があります。

そこで変更ファイルが多い場合は変更そのものを見るのではなく、いくつかをサンプリングし、「diffのパターンが同一であるか?」をClaudeが確認するようにしています。

<!-- 追加したプロンプトの抜粋 -->
## 大規模 PR のフォールバック

- **変更ファイル数が 30 ファイルを超える場合**: 同一パターンの変更が繰り返されるファイル群はサンプリング(代表的な数ファイルの確認)で判定してよい。サンプリングした旨と確認したファイルを注意事項に記載する
- **サンプリング時は副作用の取り逃しに注意する**: ある T パターンを特定したら、**その波及先ファイル群(スナップショット、spec、モック、`index.ts` の re-export、import 更新 等)も必ず1件以上サンプリングに含め、元の T パターンとの対応を確認する**。波及先のファイルだけを単独で見ると NG に誤分類しやすい(例: T5 リネームのインラインスナップショット更新を NG5 と誤認)。波及元・波及先を**セットで**サンプリングすること
(以下略)

実行コスト

Claude CodeのReviewは 最大$25の費用が必要ですが、今回はプルリクエストの分類器なのでそこまでのコストがかかりません。 CIから実行したログからみると、1 runあたり、$0.20〜0.50で分類ができていました。

{
  "type": "result",
  "subtype": "success",
  "is_error": false,
  "duration_ms": 62472,
  "num_turns": 10, // Claude Codeの手数の実績値。最大20で絞ってある。
  "total_cost_usd": 0.24731475000000003, // 約$0.25を消費
  "permission_denials_count": 0
}

おわりに

プルリクエストの分類器をGitHub Actions + Claude Code Actionsで作成することで、人がレビューしなくて済むプルリクエストは作成者が単独でマージできるようになりました。

この仕組みの導入は想定以上に開発者体験がよく、また機能開発の合間でリファクタリングするモチベーションも高めてくれます。 まさにTidy First?のなかでの「先に整頓、あとに整頓」を仕組みで支えることができたと感じています。

Claude Code Actionsはチャット形式に似たJSONレスポンスを返すため、AIチャットの実装経験もCIのデバッグに役立ちました。

今後はさらに対象を拡大させつつ、組織的に利用するAIワークフローを充実させていきたいと思います。


ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。

herp.careers