🧱

フォーマット指摘に30分使っているチームへ -- レビューの3層モデルという設計

に公開

料理人が皿洗いに時間を使うようなもの、というたとえが正確かどうかはわからない。でも数年前、CIにlinterすらまともに入っていなかったチームで、PRに「lintエラー出てますよ」と手で書き続けていたとき、自分がやっていたのはそれに近いことだったと思っています。

今なら笑い話です。Prettierを入れてpre-commitフックを設定すれば5分で終わる。ESLintのCI連携だって、設定ファイルをコピペすれば動く。でも当時の私はそういうツールの存在を知らなかった。知らなかったから、毎週月曜日に同じ指摘を手動で繰り返していました。「no-unused-varsに引っかかってます」「importの順番、ルール通りにしてください」。一件ずつ丁寧にPRにコメントして、修正を待って、また次のPRで同じことをやる。

30分かけて。毎週。

formatterやlinterをCIに組み込むだけで消える種類の指摘に、人間の時間を使っていた。指摘している側は「品質を守っている」という満足感がある。でも指摘される側からすると「また言われた」という純粋なストレスです。設計の議論なら学びがあります。でも「unused importを消してください」に学びはありません。消すだけです。

そうやって両方が疲弊していくと、レビュー自体が機能しなくなります。PRが3日開きっぱなし。誰もコメントしない。とりあえずApproveだけ押してマージ。形だけのレビュー。

本当にレビューが必要だったのは、設計の判断でした。「このロジック、このレイヤーに置いていいの?」「この方向で機能を増やし続けると2ヶ月後にどうなる?」という話です。そちらに使える時間と集中力が、lintエラーの手動指摘で消耗していた。


「誰が何を見るか」という設計問題

問題を整理すると、チームはレビューを1つの行為として扱いすぎていました。1人のレビュアーが、1つのPRで、スタイルチェックも型エラーもセキュリティ脆弱性も設計判断も全部やろうとしている。

これは役割の混在です。解決策は分業です。

「3層モデル」と呼んでいる構造があります。コードレビューを3つのサブレイヤーに分解する考え方です。

  • 第1層: 自動ゲート — 人間が関与しない
  • 第2層: AIレビュー — パターン認識
  • 第3層: 人間レビュー — 判断と方向性

それぞれの層に「担当できるもの」があります。境界を決めて、各層に集中させる。それだけです。


第1層: PRに到達させない

第1層の原則は単純です。「機械的に判断できるものは機械に任せる」。

私が手動でやっていたlintエラー指摘、あれは全部ここに属します。formatter、linter、型チェック、テスト実行。これらは正解が決まっている。Prettierを実行すれば整形される。ESLintを通せばルール違反が検出される。tsc --noEmitで型エラーが出る。人間が判断する余地はゼロです。

だから人間の目に触れる前に弾く。コミット時のpre-commitフックでformatterとlinterを実行して、通らなければコミット自体を拒否する。PRにすら到達しない状態を作ります。

「でも開発者のストレスにならない?」という反論があります。私も最初そう思っていました。結果はその逆で、「PRを出す前に自動で直してくれる」環境を好む開発者の方が多かった。PRで指摘を待って直すより、コミット時に即フィードバックが返ってくる方が気持ちいい。

ローカルのフックは迂回できる可能性があるので、CIでも同じチェックを実行します。ローカルが空港の保安検査なら、CIは入国審査です。2段階で確認する。抜け道を使う人がいることを前提に設計する、というのは少し性悪説的ですが、長く使える仕組みを作るには必要な視点です。

この層の成果は「レビュアーがフォーマット指摘を一切しなくてよくなる」こと。 毎週30分使っていたあれが、構造的にゼロになります。


第2層: パターンで検出できるものをAIに任せる

第1層を越えてきたPRには、フォーマット問題はありません。次の層では、AIにパターン認識を担当させます。

AIが得意なのは、既知のパターンを検出することです。

  • N+1問題
  • SQLインジェクションリスク
  • 未使用のインポートや変数
  • テストカバレッジの薄い箇所
  • 命名規則の違反

これらは「正解を定義できる問題」です。厳密なルールに落とし込めれば、人間が一件ずつ確認しなくていい。

AIレビューは「優秀なインターン」と思うとわかりやすい。良い仕事をするが、最終判断はシニアがする。AIの指摘を鵜呑みにしないのは当然として、見逃しがある前提で設計します。第2層はパターンの一次スクリーニングです。完璧さを求めない。

重要なのは、AIに適切な文脈を与えることです。「このファイルはNext.jsのServer Component。use clientは最小限に」という情報を渡しておくのと、渡さないのでは指摘の質が変わります。設定ファイルを書くのに30分かかっても、それ以降の手間が減る投資です。

この層の成果は「既知の問題が人間のレビュアーに届かなくなる」こと。 PRが届いた時点で、フォーマット問題も典型的なバグパターンも解消されている状態になります。


第3層: 人間の仕事を再定義する

第1層と第2層が機能している状態で人間の手元に届くPRは、景色が違います。

「フォーマットを直してください」はない。「N+1を確認してください」はない。残っているのは機械が判断できないものだけです。

人間がやるべきことは3つです。

設計判断: 「このコードは動くか」ではなく「このコードはここにあるべきか」。このロジックはこのレイヤーに属すべきか。既存の抽象化を活用できているか。今後の変更に対してどれだけ耐性があるか。

ビジネスロジックの確認: AIはコードの構造は理解できますが、「この条件分岐が仕様書の要件を正しく実装しているか」は判断できません。エッジケースがビジネス上の正しい挙動かどうか、機械には判断不可能です。

方向性の編集: 「このパターンはコードベースで増やしたいか、減らしたいか」という編集判断。単なる動作確認ではなく、コードベースをどういう未来に向けて育てるかの決定です。

この3つは全て、フォーマット指摘にはない「判断」が含まれています。そしてこの判断こそが、人間のレビュアーが本来やるべきことです。


全PRが第3層まで行く必要はない

もう一点、設計として重要なことがあります。

バグ修正や依存関係の更新、シンプルなチケット対応など、設計判断が不要なPRは第3層まで送る必要がありません。

PRの種類 必要な層
フォーマット修正 第1層のみ
依存関係の更新 第1層 + 第2層
バグ修正(原因が明確) 第1層 + 第2層
新機能追加 第1層 + 第2層 + 第3層
アーキテクチャ変更 第1層 + 第2層 + 第3層

人間のレビューが必要なのは「判断」が伴うPRだけです。

これを明確にしないと、全PRに人間レビューを要求して、結局レビューがボトルネックに戻ります。「とりあえず全部レビューしてもらう」が習慣になっている組織は多い。3層モデルはその習慣を変えるための設計でもあります。


review.md -- 3層モデルをAIに認識させる

この設計を実際にプロジェクトに適用するとき、口頭で共有しても定着しません。AIレビューツールにも伝わりません。

私はリポジトリのルートに review.md を置いています。AGENTS.mdやCLAUDE.mdと同じ発想で、「レビューの方針をファイルとして宣言する」ためのものです。CodeRabbitやClaude Codeはこのファイルを読めるので、AIレビューの挙動も変わります。

以下がテンプレートです。そのままコピペして、自分のプロジェクトに合わせて書き換えてください。

# Review Policy -- 3-Layer Model

## Layer 1: 自動ゲート(人間が関与しない)

pre-commit hookとCIで強制する。PRに到達させない。

- formatter: Prettier (`.prettierrc`)
- linter: ESLint (`eslint.config.js`)
- 型チェック: `tsc --noEmit`
- テスト: `vitest run`

### 通過条件
- 上記すべてがパスすること
- 失敗したPRはマージ不可(branch protection)

## Layer 2: AIレビュー(パターン認識)

AIが検出すべき問題。人間は原則ここに口を出さない。

- N+1クエリ
- SQLインジェクションリスク
- 未使用のimport / 変数
- テストカバレッジの不足(新規コードは80%以上)
- 命名規則違反(キャメルケース統一)
- ハードコードされた秘密情報

### AIレビューツール
- CodeRabbit(プライマリ)
- GitHub Copilot Code Review(セカンダリ)

### コメント形式
Conventional Commentsのラベルを使う:
- `issue:` 修正必須
- `suggestion:` 改善提案(任意)
- `nitpick:` 些細な改善(任意)

## Layer 3: 人間レビュー(判断と方向性)

以下のPRのみ人間レビューを要求する:
- 新機能追加
- アーキテクチャ変更
- 新しいパターンの導入

### 人間が確認すること
1. **設計判断**: このコードはこのレイヤーに属すべきか
2. **ビジネスロジック**: 仕様の意図通りか、エッジケースは正しいか
3. **方向性**: このパターンをコードベースで増やしたいか

### 人間レビュー不要なPR
- フォーマット修正 → Layer 1のみ
- 依存関係の更新 → Layer 1 + 2
- バグ修正(原因が明確) → Layer 1 + 2
- リファクタリング(小規模) → Layer 1 + 2

## Code Direction

**増やしたいパターン:**
- (ここにチームの方針を書く)

**減らしたいパターン:**
- (ここにチームの方針を書く)

ポイントは、Layer 2とLayer 3の境界を明文化していることです。「AIが見つけたら人間はスルーしていい」と書くだけで、レビュアーの認知負荷が下がる。逆にLayer 3の「人間が確認すること」を3項目に限定することで、レビューの焦点がぶれなくなります。


副作用: LGTMの重みが変わる

この設計が動き始めると、Approveの意味が変わります。

以前のApproveは「とりあえず見た」のこともありました。形だけのLGTM。フォーマット問題も設計問題も混在した状態で、疲れたからApproveしてしまう、というやつです。

3層モデルが機能している状態のApproveは、「第1層のチェックをパスしていて、第2層のAIレビューが問題なしと判断していて、私自身も設計とビジネスロジックを確認した」という意味になります。

コメントする頻度が下がって、1回のApproveの重みが上がる。レビューが「形式」から「判断」に変わる。これが目的でした。


料理人が皿洗いに時間を使うようなものだ、というたとえに戻ると、問題は「皿洗いをやめる」ことではありません。皿洗いが必要なのは確かです。ただ、それは食洗機に任せられる。料理人がやるべきは、食材の選定と火加減の判断です。

コードレビューも同じ構造です。lintエラーは自動ゲートに。パターン検出はAIに。設計判断は人間に。「誰が何を見るか」を設計することが、レビューを機能させる唯一の道だと、lintエラーの手動指摘を毎週繰り返した末に気がつきました。


3層モデルの全体像を、12枚のスライドに

本記事の3層モデルを、hooks強制・AI一次レビュー・責任境界・進化ループまで含めて12枚にまとめました。スライドだけでも流れがつかめます。

この3層モデルの実装 -- フックの設定からAIレビューの導入、人間レビューのガイドライン設計まで -- をコピペできる粒度でまとめた本を書きました。

📕 AIコードレビューを仕組み化する技術 -- hooks・AI・人間の3層モデル

Discussion