フォーマット指摘に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レビューの導入、人間レビューのガイドライン設計まで -- をコピペできる粒度でまとめた本を書きました。
Discussion