取り組みの背景
コードレビューは開発プロセスで最も負担が大きいタスクの一つです。人間レビュアーは疲労により判断が揺らぎ、時には過度な指摘(振り子現象)が発生します。
ハーネスエンジニアリング は、HashiCorp 創業者 Mitchell Hashimoto が提唱した「AIとの協働パターン」です。その本質は:
人間は 意思決定(何を作るか) に専念し、AIは 品質チェック(どう実装するか) を担当する
このパターンを Claude Code で実装し、自動コードレビュー を現実にする方法を順を追って整理していきます。
ハーネスエンジニアリングとは
4つの役割
ハーネスエンジニアリングは、AIが担う4つの役割で構成されています:
| 役割 | 説明 | 実装例 |
|---|---|---|
| Constrain(制約) | 「してはいけないこと」を定義 | CLAUDE.md の Prohibitions セクション |
| Inform(情報提供) | 設計意図と背景を共有 | コミットメッセージ、AGENTS.md、アーキテクチャドキュメント |
| Verify(検証) | 制約違反をチェック | Lint、型チェック、テスト |
| Correct(修正) | 自動的に問題を直す | Claude Code の auto-fix |
従来のコードレビューとの違い
| 側面 | 従来のコードレビュー | ハーネスエンジニアリング |
|---|---|---|
| 判定者 | 人間レビュアー | AIエージェント(人間が設定) |
| 疲労 | あり(判断が揺らぐ) | なし(機械的で一貫性がある) |
| スケール | O(n) - リビュアー数に依存 | O(1) - リビュアー数に無関係 |
| 速度 | 数時間~数日 | 数分 |
| 一貫性 | 低い(人間による変動) | 高い(ルールベース) |
Claude Code での実装:5ステップフロー
ステップ1:設定ファイルの準備(Constrain + Inform)
まず CLAUDE.md に「制約」と「情報」を定義します。
# MyProject — CLAUDE.md
## 最重要ルール
### 1. TypeScript 厳密モード必須
- `strict: true` をtsconfig.jsonで有効化
- 暗黙の `any` は禁止
- 理由: ビルド時型安全性確保
### 2. テストファイルは src/tests/ に集約
- ❌ src/components/Button.test.tsx
- ✓ src/tests/components/Button.test.tsx
- 理由: ファイル構造の明確化、npm test での統一
### 3. 外部ライブラリ追加禁止
許可ライブラリ:
- react, react-dom
- typescript
- vitest, playwright
禁止ライブラリ:
- lodash (既存のユーティリティで十分)
- moment, date-fns (Intl API で対応)
- axios (fetch で十分)
理由: バンドルサイズ管理、脆弱性最小化
### 4. CSS は Tailwind CSS のみ
- ❌ styled-components, emotion
- ✓ Tailwind className
- 理由: CSS-in-JS のランタイム脱依存
### 5. スナップショットテスト禁止
- ❌ expect(output).toMatchSnapshot()
- ✓ expect(output).toBe('expected')
- 理由: スナップショット更新の誤爆防止
## 設計方針
### アーキテクチャの3層構造UI Layer (React Components) ↓ [via custom hooks] Logic Layer (Business Logic) ↓ [via fetch API] API Layer (REST endpoints)
- API呼び出しは src/services/ に集約
- UI と Logic は結合度を低く保つ
- テストは各レイヤーで独立して実行
### データフロー
User Input ↓ Custom Hook (state, effect) ↓ Service Function (API call) ↓ Response Processing ↓ Component Render
## Step 0-5: コードレビュー自動化ワークフロー
### Step 0: 開発者がコードを書く
```bash
# 開発者が新機能を実装
code src/features/payment.ts
Step 1: 提出前の軽微な Lint
npm run lint:fix # 自動修正可能なエラーを直すStep 2: Claude Code でレビュー実行
# Claude Code で以下の指示を実行:
"Review payment.ts against CLAUDE.md rules"Claude Code の実行内容:
1. CLAUDE.md を読み込み
2. コードと照らし合わせ
3. 違反をリスト化
4. 修正案を生成
Step 3: トリアージ(修正の優先度付け)
Claude Code が自動的に分類:
優先度1(Critical)
├─ TypeScript 型エラー
├─ テストカバレッジ < 70%
└─ 禁止ライブラリ使用
優先度2(High)
├─ アーキテクチャ違反(Layering)
├─ 命名規則違反
└─ ファイル行数 > 300行
優先度3(Medium)
├─ コメント不足
├─ 変数名が不適切
└─ 関数複雑度 > 10
優先度4(Low)
├─ ホワイトスペース
└─ インポート順
Step 4: 自動修正 + バリデーション
# Claude Code が以下を実行:
npm run lint:fix # Lint自動修正
npm run type-check # TypeScript型チェック
npm run test:unit # ユニットテスト実行
npm run test:integration # 統合テスト実行修正後、もう一度レビュー実行。 修正が不十分な場合、ループを最大6回まで実行。
Step 5: コミット & デプロイ
# 全テストが pass したら自動コミット
git add .
git commit -m "feat: payment system - auto-reviewed by Claude Code"
git push origin feature/payment過剰指摘と振り子現象への対策
問題: 過剰指摘(Over-suggestion)
現象:
❌ 「この関数は複雑すぎます。13個の責務があります」
→ 開発者が困惑。関数は1つの責務しかない
原因: AIが細粒度レベルで各ステップを「責務」と数えている
対策: 「責務」を明確に定義する
## 責務の定義
- 「1つの責務 = 1つのビジネスルール変更で影響を受ける処理」
- 以下は「1つの責務」としてカウント:
✓ 複数のステップ(入力検証 → 計算 → 出力)
✗ 関数が呼ぶ他の関数の数(関数の粒度ではない)
## 許容基準
- 関数行数: < 300行
- 循環複雑度: < 10
- ネスト深度: < 3問題: 振り子現象(Oscillation)
現象:
Loop 1: 「タイプを付けるべき」 → 関数に型追加
Loop 2: 「型が冗長だ」 → 型削除
Loop 3: 「タイプを付けるべき」 → 以下ループ...
原因: レビュー基準が矛盾している
対策: 優先度ルールの厳密化
## 修正の優先度(矛盾は禁止)
1. TypeScript 型エラー(非交渉)
2. テスト失敗(非交渉)
3. アーキテクチャ違反(修正必須)
4. ケーススタイル・命名(修正推奨)
5. コメント不足(追加推奨)
ルール:
- Priority 1-3 は修正しない限り再指摘しない
- Priority 4-5 で矛盾する指摘は2回目以降は指摘しない問題: 検証ステップの不足
現象:
修正後、バリデーションなしに「完了」と判定
→ テストが fail → 修正がもう一度必要
対策: Verify ステップを強制
## Step 4: Verify(バリデーション)チェックリスト
- [ ] TypeScript コンパイル成功: tsc --noEmit
- [ ] ユニットテスト pass: npm test
- [ ] 統合テスト pass: npm run test:integration
- [ ] Lint エラーゼロ: npm run lint
- [ ] ビルド成功: npm run build
全てチェック済みまで Correct ステップに戻らない実装パターン:MCP Server での自動化
Claude Code を MCP(Model Context Protocol)サーバーと連携させることで、さらに自動化が進みます。
パターン: Code Review Server
// mcp-code-review/index.ts
import Anthropic from "@anthropic-sdk/sdk";
const client = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
interface ReviewRequest {
filePath: string;
rules: string; // CLAUDE.md の内容
}
async function reviewCode(req: ReviewRequest): Promise<string> {
// ステップ1: ファイル読み込み
const fileContent = await fs.readFile(req.filePath, "utf-8");
// ステップ2: Claude でレビュー実行
const review = await client.messages.create({
model: "claude-opus-4-1",
max_tokens: 4096,
messages: [
{
role: "user",
content: `
You are a code reviewer using Harness Engineering principles.
Rules (from CLAUDE.md):
${req.rules}
File to review:
${fileContent}
Follow these steps:
1. Check each rule violation
2. Categorize by priority (Critical/High/Medium/Low)
3. Suggest fixes ONLY for Critical/High
4. For each suggestion, explain why
Output format:
PRIORITY: <level>
RULE: <violated rule>
ISSUE: <description>
FIX: <suggested code>
---
`,
},
],
});
return review.content[0].type === "text" ? review.content[0].text : "";
}
// ステップ3: トリアージ
function triageReview(review: string): Map<string, string[]> {
const issues = new Map<string, string[]>();
review.split("---").forEach((block) => {
const priorityMatch = block.match(/PRIORITY: (\w+)/);
if (priorityMatch) {
const priority = priorityMatch[1];
issues.set(priority, [...(issues.get(priority) || []), block]);
}
});
return issues;
}
// ステップ4: 自動修正
async function autoFix(
filePath: string,
issues: string[]
): Promise<boolean> {
// Critical/High のみ修正
const criticalsAndHighs = issues.filter((i) =>
i.match(/PRIORITY: (Critical|High)/)
);
if (criticalsAndHighs.length === 0) {
return true; // 修正対象なし
}
const fixPrompt = criticalsAndHighs.map((i) => i.match(/FIX: ([\s\S]+?)---/)?.[1]).join("\n");
const fixed = await client.messages.create({
model: "claude-opus-4-1",
max_tokens: 4096,
messages: [
{
role: "user",
content: `Apply these fixes to the code:\n${fixPrompt}`,
},
],
});
// ステップ5: バリデーション
await execSync("npm test", { cwd: process.cwd() });
await execSync("npm run lint", { cwd: process.cwd() });
return true;
}
// メイン処理: 最大6ループ
async function reviewLoop(filePath: string, rules: string) {
let iteration = 0;
const maxIterations = 6;
while (iteration < maxIterations) {
iteration++;
// Step 2: レビュー
const review = await reviewCode({ filePath, rules });
// Step 3: トリアージ
const triaged = triageReview(review);
// Critical/High がなければ終了
if (
!triaged.has("Critical") &&
!triaged.has("High")
) {
console.log(`✓ Review passed after iteration ${iteration}`);
return;
}
// Step 4: 自動修正
const fixed = await autoFix(
filePath,
Array.from(triaged.values()).flat()
);
if (!fixed) {
console.log(`✗ Auto-fix failed at iteration ${iteration}`);
return;
}
}
console.log(`⚠ Max iterations (${maxIterations}) reached`);
}まとめ
| 側面 | 従来 | ハーネス工学 | |---|---|---| | レビュアーの疲労 | あり | なし | | 判定の一貫性 | 低い | 高い | | スケーラビリティ | リビュアー数に依存 | 一定 | | レビュー時間 | 数時間~数日 | 数分 | | 人間の役割 | 全ての判定 | 重要な判定のみ |
ハーネスエンジニアリングを Claude Code で実装することで、品質を落とさずに開発速度を 10倍以上 に加速できます。
最初は CLAUDE.md の 5〜10個ルールから始め、運用しながら段階的に追加するのがコツです。
関連リソース
- CLAUDE.md & AGENTS.md 完全ガイド
- Claude Code AI Harness デザインパターン
- Claude Code 設定ファイル完全ガイド
- MCP サーバー構築ガイド
- Claude Code Hooks 自動化ガイド