はじめに
コードレビューってどんなことをすれば良いのか。その疑問からブログ記事を読み漁り、AIとも壁打ちをしてきました。しかし、コードレビューをGitHubを使ってする方法は出てきても、人を目の前にして行う記事はなかなか見当たらなかったので、記事を書いて迷っている方のためになればと思っています。
今度エンジニアの方々を前にコードレビューをさせていただける機会をいただいたので、AIに出力してもらった教材を元に練習を重ね、自分なりに最適なコードレビュー方法がまとまったので共有させていただきます。
前提
私のスペック
IT業界は未経験。独学でプログラミングの学習を進めている。
Next.js、React、TypeScript などを使って席替えアプリを作成したことがある。
人前でコードを読んでレビューをしたことは今までない。
作ったアプリについては別記事に書いています。
練習教材の構成
練習用に生成した教材は、以下の構成にしました。実務で広く使われている組み合わせから選んでいます。
- Next.js(App Router)/ React / TypeScript(
strict: true) - Tailwind CSS / shadcn/ui 系(Radix UI、lucide-react、class-variance-authority)
- Zod
- 認証に AWS Amplify Gen 2(Cognito)
- Lint・Format に Biome
コードレビューの目的
探していると、自分のような初心者プログラマーにもわかりやすい記事が見つかったので、以下で共有します。
💬 1. コメントでは「質問+背景」を意識しよう
👁🗨 2. 「過去・未来・他人」の視点を持つ
📚 3. レビューは「学びの場」である
「レビューは育て合いの場」
新人のレビューは、間違っても「完璧である必要」はありません。
大切なのは、相手のコードを真剣に読もうとする姿勢と、チームの中で学び成長していく意志です。
コードレビューは、自分の目を通してチームの品質を守る行為。
まずはその役割の重さと面白さに気づくことが、プロへの第一歩です。
特に「大切なのは、相手のコードを真剣に読もうとする姿勢と、チームの中で学び成長していく意志です。」という言葉が心に残りました。後述しますが、間違い探しの視点が初期の私にはありました。 コードを読むことでチームのメンバーとコミュニケーションを取っていく姿勢を大切にしたいです。
レビューの観点
レビューの観点がチーム内で定義されているかを、一番最初に確認します。 チームとしてコードの品質を上げるのが目的なので、何を見るかの認識が揃っていないと、指摘が個人の好みの話になってしまいます。
そのうえで、自分自身も観点を持っておく必要があると思います。先ほどの引用先を元に、少なくとも今回私が意識したい点を挙げてみました。できればこれらは無意識で持ちたい視点です。
1. 変数・関数の名前から機能が想像できるか
- 名前の規則に統一感があるか(キャメルケースなど)
- 処理の流れが直感的に追えるか
補足:レビューとは「説明なしでもわかるコードか」を見る行為でもあります!
2. 安全に動くか
- 例外処理(エラー時の挙動)が入っているか
- null / undefined、0除算、型エラーの対策がされているか
3. 動かして確認したか
- テストが書かれているか
- 自分で実際に動かして、入力→出力が想定通りか確認したか
意識すべきこと
レビューは間違い探しではなく、意図探しの場。
- 「気づいたこと」はメモにする
- 「わからなかったこと」は素直に聞く
- 「良いところ」も言語化して伝える
実際にAIに教材を生成させて練習してみた
この記事を書くにあたり、Claude Code にアプリを生成させ、コードを声に出して読み、修正する練習を何度も繰り返し行いました。
無意識で型のチェックなどは行うことはできるのですが、やはり手順の中に組み込んだチェックポイントを抜かしてしまうこともありました。改めて記事にすることで、極力無意識で理想のコードレビューをしたいという思いもあります。
プロンプト
Claude と壁打ちを繰り返しながら、以下のようなプロンプトで Claude Code を使って生成させました。少なくとも5回生成させており、その都度教材のポイントは変わっていますが、基本的な型は以下の通りです。
プロンプト(長すぎるためトグルで対応)
# 依頼
Next.js のコードレビュー練習用プロジェクトを作ってください。**意図的に問題を埋め込んだ**教材です。
## 技術スタック(厳守)
- パッケージ管理: npm
- Node.js: `.tool-versions` と `package.json` の `volta` の**両方**を書く
- Next.js(App Router、Turbopack)
- React
- TypeScript(`strict: true`)
- Tailwind CSS
- shadcn/ui 系(Radix UI、lucide-react、class-variance-authority)
- Zod
- AWS Amplify Gen 2(認証に Cognito)
- Biome(lint + format)
- Vitest + React Testing Library
## テーマ
製造業の **品質検査記録** アプリ。
扱うもの:
- 検査ロット(ロット番号・品目・検査数・検査日・判定ステータス)
- 検査項目(項目名・規格上限・規格下限・単位)
- 測定値(検査ロット×検査項目ごとの実測値・測定者)
- 不良記録(不良区分・件数・処置)
ロール(Cognito グループ):
- `INSPECTOR`(検査員): 測定値の登録
- `QA`(品質管理): ロットの合否判定・不良記録の登録
- `VIEWER`(閲覧のみ): 参照だけ
画面:
- `/lots` 検査ロット一覧(キーワード検索・判定フィルタ・並び替え)
- `/lots/[id]` ロット詳細(測定値一覧・規格外のハイライト・不良記録)
- `/lots/[id]/measure` 測定値の登録フォーム(確認ダイアログあり)
- `/dashboard` 不良率・検査件数のサマリ
- ロットを「不合格にする」ダイアログ(Server Action)
- `/sign-in` ログイン画面
## Amplify / 認証の構成
- `amplify/package.json`(`{ "type": "module" }`)
- `amplify/backend.ts`(`defineBackend`)
- `amplify/auth/resource.ts`(`defineAuth`、`groups` に上記3つ)
- `src/middleware.ts`(未ログインを `/sign-in` へ)
- `src/lib/auth/session.ts` にセッションとグループを取得するヘルパー
**重要**: `npx ampx sandbox` を実行せず、`amplify_outputs.json` が無い状態でも
`npm run dev` で画面が見られるようにしてください。開発時は認証をバイパスできる形
(環境変数で切り替え)にしてください。データはモックで構いません。
## 規模(前回の教材より小さくすること)
- `src/` 配下で **35〜40ファイル**(`src/components/ui/` を除く)
- 合計 **1,800〜2,200行**
- モックデータは **200件程度**
- `src/lib/` にユーティリティ関数を8個以上、`src/types/` にドメイン型
## 埋め込む問題
**件数は 10〜14件の範囲で、あなたが決めてください。**
**件数は `ANSWERS.md` にだけ書き、README・コード・コメントには一切書かないでください。**
### 重心: 半分を「どの言語でも通じる基礎」にする
**フレームワーク固有ではない、基礎的なコーディングの問題を5〜7件入れてください。**
検査の合否判定・不良率・平均値といった数値ロジックがあるので、自然に仕込めます。
以下から選んでください(全部でなくてよい)。
- **境界値**: 規格上限ちょうどの測定値が不合格になる(`>` と `>=` の取り違え)
- **0除算・空配列**: 検査数0のロットで不良率が `NaN` や `Infinity` になる/測定値0件で平均が `NaN`
- **重複ロジック**: 同じ合否判定が2箇所に書かれていて、片方だけ条件が直されている
- **マジックナンバー**: 意味の分からない数値が式に直接書かれている
- **条件式の誤り**: `||` と `&&` の取り違え、否定の重ね、1つの式に2つの判断を混ぜている
- **破壊的メソッド**: `sort` / `reverse` / `splice` が引数の配列を書き換えている
- **型変換**: `Number("")` が `0` になるのを見ていない/`parseInt` の基数指定なし
- **関数が長い・責務が多い**: 1つの関数で取得・計算・整形・保存をしている
- **深いネスト**: 早期リターンで平らにできる3段以上のネスト
### これまでの教材で扱ったもの(5〜7件)
- **名前が嘘をつく関数。**過去の教材では `isDelayed` が日数を返し、`isLowStock` が差分を返し、
`formatShipDate` が日付を変え、`assignDispatch` がステータスも変えました。
**同じ形は避けて、別の現れ方にしてください。**
- **`as` 型アサーションで `undefined` を握りつぶしている**
- **Server Action にセッション・グループのチェックが無いものを1つ**
(他の Action には付いている状態にして、対比が見えるようにする)
- **`src/middleware.ts` の `matcher` に穴がある**
(ルートのフォルダ名と1文字違う、など)
- アクセシビリティ: キーボードで到達できない導線、またはラベルの無い入力
- エラーハンドリング: `await` の失敗経路が無い、または例外を握りつぶして空の値を返す
### テスト
**1件。通るのに実質何も検証していないテスト。**
### 条件
- どれも **コードを読めば分かる**こと。推測や外部知識を要しない
- 問題は **構文エラーにしない**。`npm run dev` は起動し、画面は表示される
- **`npm run typecheck` は通る状態**にする
- `npm run lint` で全部拾えるようにはしない
- **半分以上は、画面を触れば症状が見えること**
- 過去の教材と**同じ形の問題を繰り返さない**
## 成果物
1. プロジェクト本体(`npm install` → `npm run dev` で動く。**AWS アカウント不要**)
2. `package.json` に `dev` / `build` / `lint` / `typecheck` / `test` スクリプト
3. 短い `README.md`(概要・起動方法・開発時の認証の扱い。**問題のヒントは書かない**)
4. **`ANSWERS.md` はプロジェクトフォルダの外に `<プロジェクト名>-answers/` を作って置く**
### ANSWERS.md の形式
- 冒頭に件数
- 分類ごとに見出し
- 各問題: **場所**(パスと行番号)/ **問題** / **症状**(画面でどう見えるか)/ **修正**
- **基礎的な問題には「どの言語でも通じる原則」を1行で添えてください**
(例: 「境界値は仕様の文言と比較演算子を突き合わせて確認する」)
- 認証・認可の項目には「どう攻撃できるか」を具体的に書く
### さらに、必ず作ってほしい節
**`## 褒めるべき点(意図的に正しく書いた箇所)`**
**意図的に正しく・きれいに書いた箇所を3〜5件挙げてください。**
各項目に「なぜ良いのか」を1〜2行で。
例:
- 型を1箇所から導いている(`z.infer` / `(typeof ARR)[number]`)
- `Record<DomainType, string>` で網羅性を担保している
- `get` は失敗しうる・`require` は必ず返す、と名前で使い分けている
- 境界値のテストが書かれている
**これは「正しいコードを正しいと言えるか」を練習するための節です。**
問題と同じくらい重要なので、手を抜かないでください。
成果と課題
成果
- 声に出してコードを読んでいくので、理解ができていないコードは声に出せないことで明確にわかるため、どんな分野が抜けているのか後で学びやすかった
- 馴染みのないメソッドに出会ったときに、公式ドキュメントへ向かう速度が速くなった。検索速度が速くなり、理解にかかる時間が減っている
課題
- 重点的に読むコードを決めないと、全てのファイルを丁寧に読んでいたら時間がかかりすぎる
- スムーズに進めようとすると、間違い探しになりがち
この2つの課題に対する答えが、次の手順です。
レビューの具体的手順
最初は「設定ファイルから順に開いて、気になったら直す」だけでした。それだと時間が足りません。
時間配分
80分での実際の目安です。
| 時間配分 | やること |
|---|---|
| 0–10分 |
ブラウザだけ。 設定ファイル →npm run dev→ 画面を一周 |
| 10–45分 | エディタだけ。 全ファイルを浅く一周。 |
| 45–75分 | 修正 |
| 75–80分 | まとめ |
1. ブラウザだけ
npm install の待ち時間に、READMEを見ます。
READMEにはどういうアプリなのか現場の用語が出てくるので簡単に読み飛ばさないこと。できれば、コードに変換した時の英単語をイメージしたい
npm run dev が立ち上がったら、各ページの画面を触ります。
- 絞り込みと並び替えを動かす
- フォームを空で送信
- ダイアログを Esc で閉じる
- Tab キーだけで一周
- 存在しない ID を URL に入れる
ログインは一番権限の広いロール1つだけ
ロールごとの出し分けはコードを読めば分かります。
2. エディタだけ
読む順番
lib/ → app/ → components/ の順です。
lib/ に先に目を通しておくと、大まかな関数や設定が分かり、 app/ の読みが速くなるからです。
各ファイルで見る6点
| # | 見るもの | 判断 |
|---|---|---|
| 1 | ファイルの役割 | 名前と中身が合っているか |
| 2 | 1行目の "use client"
|
フックス・イベントを実際に使っているか |
| 3 | import 文 | クライアントコンポーネントがデータ層を import していないか |
| 4 |
useEffect の有無 |
React の外と同期しているか(他の state から計算できるなら不要) |
| 5 | 関数の戻り値の型 |
is〜 / has〜 / get〜 が名前通りか |
| 6 | 型アサーション as、!(非nullアサーション) |
型を強制していないか |
名前の原則
lib/ は関数名の行だけを上から見ます。
-
is/has/canの戻り値が boolean か -
get/findに| undefinedが付いているか - 動詞が付いていない行はないか
isLowStock(...): number // is〜 なのに number を返す
getPartById(id): Part // 見つからない場合があるのに | undefined が無い
remainingCapacity(...) // 動詞がない(同ファイルの calcLoadRate と不統一)
認証が入っている場合
| # | 見るもの | 判断 |
|---|---|---|
| 1 | Server Action の認証チェック | 誰が呼んでいるかを見ていない(最重要) |
| 2 |
middleware.ts の matcher
|
保護したいルートが除外されていないか |
| 3 | 認証設定ファイル | ログイン方法・グループの定義 |
Server Actions は公開エンドポイントです。 画面にボタンが出ていなくても、関数は呼べます。middleware.ts で未ログインを弾くのとは別に、Action の中でもチェックが必要という話です。
matcher は、書き方一つで穴が空きます。/lots/:id と書くと/lots/abc/measure は保護されません。/lots/:path* なら配下すべてに掛かります。
3. 修正
修正はその場で相談をしながら優先順位を決めていきます。
ただし、私自身も観点を持っておくべきなので、以下の4点で優先順位をつけます。
データが壊れる > 機能に到達できない > 表示が間違っている > 保守性
4. まとめ
理想は良かったところも修正点も含めて、今回の学びについて話をすることです。
ただコードを見るだけの作業になるのではなく、チームとして協働していることを意識できたらと思っています。
まとめ
この1ヶ月間、コードレビューについて調べ、練習してきました。初めは開くファイルの順番もわからなかったので戸惑うことも多かったですが、今では自分なりの型は出来上がってきたように思います。
5回やって、かなり難しく感じましたが、それでも手順を書き換えるたびに、時間内に触れる範囲が広がり、言えることが増えていきました。
当日、与えられた時間でどこまで意図探しができるか今は楽しみです!!
参考