📄
概念 📚 beginner-stepup

生成コードのレビュー・デバッグの勘所

Claude Codeが生成したコードを安全に採用するためのレビュー観点と、エラーが発生したときのデバッグ手順を理解する

Claude Codeはコードを生成するが、そのコードが正しく・安全に動くかを確認する責任は開発者にある。AIが生成したコードを「理解せずにコピー&ペーストする」と、バグや脆弱性を取り込むリスクがある。一方、「逐一すべてを理解してから採用する」のは学習初期には現実的でない。この間のバランスをとるための実践的な観点を示す。

生成コードを受け取ったときのチェックリスト

1. 意図通りの実装か(What)

  • 指示したことを実装しているか
  • 余計な機能が追加されていないか
  • 既存のコードと矛盾していないか

2. 危険なパターンがないか(Security)

// ❌ SQLインジェクション(直接文字列連結)
const query = `SELECT * FROM users WHERE name = '${userInput}'`;

// ✅ パラメータバインディングを使う
const result = await db.select().from(users).where(eq(users.name, userInput));
// ❌ 秘匿情報がコードに直書き
const apiKey = "sk-abc123...";

// ✅ 環境変数から取得
const apiKey = process.env.OPENAI_API_KEY;

3. エラーハンドリングが適切か

// ❌ エラーを無視
const data = await fetchUser(id);

// ✅ エラーを適切に処理
try {
  const data = await fetchUser(id);
  return data;
} catch (error) {
  console.error("ユーザー取得失敗:", error);
  return null;
}

4. TypeScriptの型が正しいか

  • any が多用されていないか
  • as による強制キャストが不適切に使われていないか

「説明して」プロンプトの活用

理解できない生成コードは採用前に説明を求める:

このコードの各行が何をしているか説明してください。
特に useCallback の第2引数(依存配列)がなぜこの値なのかを詳しく教えてください。

説明が理解できれば採用、理解できなければさらに質問するか別のアプローチを試す。

エラーメッセージの読み方

TypeScriptのエラーは「型の不一致」を具体的に示す:

Type 'string | null' is not assignable to type 'string'.
  Type 'null' is not assignable to type 'string'.

→ 「string | null になり得る値を、null を受け付けない箇所に渡している」という意味。null チェックを追加するか、型定義を変更する。

デバッグの基本手順

Step 1: エラーメッセージを全文読む エラーの最初の行よりも、スタックトレースの「自分のコード」の部分(node_modules ではない行)に注目する。

Step 2: 問題を最小化する

// バグがある複雑な関数をデバッグするとき
// まず小さいケースで再現できるか試す
console.log(formatDate(new Date("2026-04-19")));  // 期待値と比較

Step 3: Claudeにエラーを投げる

以下のエラーが発生しています。
エラー: [エラーメッセージの全文を貼る]
発生箇所: app/patients/page.tsx の38行目
試したこと: date型のカラムをstring型として扱ってみたが同じエラー

エラーメッセージの全文・発生箇所・試したことをセットで伝えると回答の精度が上がる。

Claude Codeが苦手なケース

  • プロジェクト固有の複雑なビジネスロジック: コンテキストを十分に与えれば対応可能だが、確認が必要
  • 外部ライブラリの最新API: 学習データのカットオフ以降の変更は知らない。公式ドキュメントで確認する
  • パフォーマンスの最適化: 動くコードは生成できるが、最適なクエリ設計は要確認

デプロイ前の最終確認

npm run build    # ビルドエラーがないか
npm test         # テストが通るか
npx tsc --noEmit # 型エラーがないか

この3つが通れば、コードの品質として最低限のゲートを越えた状態。

Human-in-the-loop設計パターン

コードレベルのレビューに加えて、フロー設計レベルのレビューも重要である。特に医療・製薬データを扱うWebアプリでは「AIが処理した結果をそのまま本番に適用する」設計は避ける。

基本パターン:承認ゲートを挟む

// ❌ AIが処理してすぐ本番DB/外部APIに反映
const result = await callDifyApi(reportText);
await db.update(reports).set({ summary: result.answer });  // 即時反映はNG

// ✅ 承認ゲートを挟む
const result = await callDifyApi(reportText);
await db.insert(aiDrafts).values({
  reportId,
  draft: result.answer,
  status: "pending",   // 承認待ち
  createdAt: new Date(),
});
// → ダッシュボードで確認・承認された後に reports テーブルに反映

Slack/メール通知と組み合わせる

// AI処理完了 → レビュー担当者に通知
await db.insert(aiDrafts).values({ reportId, draft, status: "pending" });

// Slack通知(n8nのWebhookやSlack APIで実装)
await fetch(process.env.SLACK_WEBHOOK_URL!, {
  method: "POST",
  body: JSON.stringify({
    text: `📋 AIレポート要約が承認待ちです\n対象: ${reportId}\n確認URL: ${process.env.APP_URL}/drafts/${draftId}`,
  }),
});

このパターンはDifyのHuman-in-the-loopノードやn8nの承認ワークフローと同じ思想。Webアプリで自作する場合も同じ構造になる。

医療・製薬データを扱う際のレビューポイント

Claude Codeが生成したコードで、特に確認が必要な箇所:

個人情報の扱い

// ❌ 患者の生の情報をログに出力
console.log("処理完了:", patient.name, patient.birthDate);

// ✅ IDのみログに残す
console.log("処理完了: patientId=", patient.id);

AI出力の素通し

// ❌ LLMの出力をバリデーションなしでDBに格納
const { answer } = await difyApi.call(text);
await db.insert(results).values({ summary: answer });

// ✅ バリデーション + 承認フローを通す
const { answer } = await difyApi.call(text);
validateAiOutput(answer);  // 文字数・禁止キーワード・形式チェック
await db.insert(aiDrafts).values({ draft: answer, status: "pending" });

外部APIへの意図しない送信

// ❌ 医療データをそのまま外部LLMに送信
await openai.chat.completions.create({
  messages: [{ role: "user", content: `患者名: ${patient.name}, 診断: ${diagnosis}` }],
});

// ✅ 匿名化してから送信
const anonymized = anonymize(diagnosis);  // 氏名・IDを除去
await openai.chat.completions.create({
  messages: [{ role: "user", content: `診断内容: ${anonymized}` }],
});

「小さく試す → 確認 → 本番適用」のスプリントパターン

大きな機能を一気にリリースするより、小さい単位で試してフィードバックを得る方が安全:

Week 1: CSVアップロード機能だけ本番リリース(AI処理なし)
    ↓ 動作確認・ユーザーフィードバック
Week 2: AI処理を「承認待ち」状態で追加(まだ本番反映しない)
    ↓ 出力品質を確認
Week 3: 承認フロー(ダッシュボード)を本番リリース
    ↓ 実際の承認・反映フローを確認
Week 4: 自動化の範囲を徐々に拡大

Claude Codeでも同じ原則が適用できる:一度に大きな機能を生成させるより、小さい単位で生成・確認・統合を繰り返す方がレビューしやすく、問題の特定も容易になる。


学習としてのコードレビュー

生成コードをただ採用するのではなく、「なぜこの書き方なのか」を理解することが重要。同じパターンを次回は自分で書けるようになることが目標。知らなかったパターンをメモしておくと、次のセッションでCLAUDE.mdの「好みのパターン」として追記できる。

sns_clone 参考実装


参考リソース


確認クイズ

Q1. 生成コードのセキュリティチェックで、SQLインジェクション対策として正しい方法はどれですか? A. SQL文字列をすべて大文字で書く B. ユーザー入力をそのままSQL文字列に連結する C. パラメータバインディング(eq(users.name, userInput) のような形式)を使う D. SQLにコメントを追加する

正解: C

解説: SELECT * FROM users WHERE name = '${userInput}' のような文字列連結は、ユーザーが悪意のあるSQLを埋め込めるSQLインジェクション脆弱性につながる。Drizzle ORMの eq() のようなパラメータバインディングを使うことで安全にクエリを組み立てられる。

Q2. 生成コードに const apiKey = "sk-abc123..." のように秘匿情報が直書きされていた場合、何が問題でどう修正すべきか説明してください。

正解: リポジトリにコミットされた場合に情報が漏洩するリスクがある。process.env.OPENAI_API_KEY のように環境変数から取得する形に修正する。

解説: コードに直書きされたAPIキーはgitのコミット履歴にも残るため、後から削除しても履歴に残り続ける。秘匿情報は必ず環境変数(.env ファイル)から取得し、.env はgitignoreに追加して管理する。

Q3. TypeScriptエラー「Type 'string | null' is not assignable to type 'string'」の意味と対処法を説明してください。

正解: string | null になり得る値を null を受け付けない箇所に渡している。null チェックを追加するか型定義を変更する。

解説: TypeScriptの型システムが「この変数は null になる可能性があるのに、null を受け付けない型の変数に代入しようとしている」という問題を検出している。if (value !== null) のようなnullチェックを追加するか、型定義を string | null に修正する。

Q4. デバッグの基本手順で「問題を最小化する」とはどういう意味ですか?

正解: バグがある複雑なコードから問題を再現する最小のケースを取り出して確認すること

解説: 複雑なコードをそのままデバッグすると問題の原因を特定しにくい。console.log(formatDate(new Date("2026-04-19"))) のように、問題の関数を小さいケースで呼び出して期待値と比較することで、どこで問題が起きているかを絞り込みやすくなる。

Q5. Claudeにエラーを投げてデバッグを依頼するとき、精度を上げるために伝えるべき3要素はなんですか?

正解: エラーメッセージの全文、発生箇所(ファイルと行番号)、試したこと

解説: エラーメッセージだけでは原因の推測が困難な場合が多い。「エラーメッセージの全文・発生箇所・試したこと」をセットで伝えることで、Claudeが文脈を理解して的確な原因と対処法を提示できる。

Q6. デプロイ前の最終確認として示されている3つのコマンドを答えてください。

正解: npm run buildnpm testnpx tsc --noEmit

解説: ビルドエラー・テスト失敗・型エラーがないことを確認することが「コードの品質として最低限のゲートを越えた状態」とされている。この3つが通ればデプロイに進んでよい最低限の品質基準が満たされている。

Q7. Human-in-the-loop設計パターンで「承認ゲートを挟む」ことの目的はなんですか?

正解: AIが処理した結果を人間がレビューしてから本番に反映するため。AIの誤出力が直接本番に影響しないようにするため。

解説: status: "pending" で保存してダッシュボードで確認・承認された後にはじめて本番に反映する設計が推奨される。医療・製薬データのような重要なデータでは特にこのパターンが重要であり、Difyのhuman-in-the-loopノードやn8nの承認ワークフローと同じ思想。

Q8. 医療データを扱う際にLLMへのリクエストで注意すべき点はなんですか?

正解: 患者の氏名・IDなどの個人情報(PII)をそのまま外部LLMに送信しないこと。匿名化してから送信する。

解説: 患者名: ${patient.name} のような形でそのまま送ると、外部サービスに個人情報が渡ってしまう。anonymize() のような関数で氏名・IDを除去した上でLLMに渡す設計が必要。また、処理結果もバリデーション + 承認フローを通すことが推奨される。

Q9. 「小さく試す→確認→本番適用」のスプリントパターンで、AI処理を「承認待ち状態で追加してまだ本番反映しない」週を設ける理由はなんですか?

正解: AI出力の品質を本番に反映する前に確認・検証するため

解説: AIの出力品質は最初から保証されているわけではない。まず承認待ち状態で保存してAIの実際の出力を確認し、品質が妥当であると判断してから承認フローを本番リリースする段階的なアプローチにより、リスクを最小化できる。

Q10. 生成コードを「理解せずにコピー&ペーストする」リスクと、「説明して」プロンプトを活用する意義を説明してください。

正解: 理解せずに採用するとバグや脆弱性を取り込むリスクがある。「説明して」プロンプトで理解してから採用することで、問題のあるコードを弾きつつ学習にもなる。

解説: AIが生成したコードが正しく・安全に動くかを確認する責任は開発者にある。理解できない箇所は「このコードの各行が何をしているか説明してください」と求めることで、採用前に問題を発見できる。また、同じパターンを次回は自分で書けるようになる学習効果も得られる。

生きているコード

本ドキュメントで扱ったパターンの完全な動作コードは、メンター側リポジトリの参照ブランチで確認できます。

ブランチの作り方・見方は b00-curriculum-map を参照してください。

📚 beginner-stepup 全 53 章
導入編 13 章
  1. 1. 📄Web とは何か
  2. 2. 📄URL を打ってから画面が表示されるまで
  3. 3. 📄ネットワーク基礎(TCP/IP・DNS・HTTPS)
  4. 4. 📄【発展】物理層から通信が成立するまで(電力・Ethernet・Wi-Fi・Bluetooth)
  5. 5. 📄WSL2・Docker セットアップ詳細(Windows 向け)
  6. 6. 📄環境構築の段階的導入(macOS / Windows)
  7. 7. 📄カリキュラム全体マップ(Week × 教材 × 参照ブランチ × 要求チェックリスト)
  8. 8. 📄このカリキュラムの使い方(SQL・Python・Dify経験者向け)
  9. 9. 📄シェル・ターミナル基礎
  10. 10. 📄Windows で完全にゼロから始める開発環境構築(Week 1)
  11. 11. 📄Git基礎
  12. 12. 📄GitHubワークフロー
  13. 13. 📄パッケージ管理(pnpm workspace)
応用編 16 章 ● この編を閲覧中
  1. 1. 📄AWSインフラ基礎
  2. 2. 📄AWS Budget Alert の設定(Month 5 Week 17)
  3. 3. 📄環境変数管理
  4. 4. 📄Bastion EC2 と SSH ProxyJump(Month 5 Week 18)
  5. 5. 📄CI/CD基礎
  6. 6. 📄ECR への Docker イメージ push と App EC2 デプロイ(Month 5 Week 19)
  7. 7. 📄テスト設計の基本
  8. 8. 📄CloudFront + S3 + ALB で公開する(Month 5 Week 20)
  9. 9. 📄CLAUDE.md・プロジェクト設定
  10. 10. 📄PR レビュー 5 観点ルーブリック(全 Week 共通)
  11. 11. 📄タスク分解・仕様の書き方
  12. 12. 📄Playwright で E2E テスト(Month 6 Week 22)
  13. 13. 📄生成コードのレビュー・デバッグの勘所
  14. 14. 📄Trivy で脆弱性スキャン(Month 6 Week 23)
  15. 15. 📄CloudWatch Logs の読み方と運用(Month 6 Week 23)
  16. 16. 📄PDF ポートフォリオの自動生成(Month 6 Week 24)