Setup and context
Code review is one of the most demanding tasks in software development. Human reviewers experience fatigue, leading to inconsistent judgments and sometimes even oscillating feedback patterns.
Harness Engineering, coined by HashiCorp founder Mitchell Hashimoto, offers a solution. Its core principle is:
Humans focus on decision-making (what to build), while AI handles quality checks (how to implement).
This guide shows you how to implement this pattern with Claude Code, transforming code review into a fully automated, consistent process.
What is Harness Engineering?
Four Core Roles
Harness engineering divides AI responsibilities into four distinct roles:
| Role | Description | Implementation |
|---|---|---|
| Constrain | Define what NOT to do | CLAUDE.md Prohibitions section |
| Inform | Share design intent and context | Commit messages, AGENTS.md, architecture docs |
| Verify | Check for constraint violations | Linting, type checking, tests |
| Correct | Fix issues automatically | Claude Code auto-fix mode |
How It Differs from Traditional Review
| Aspect | Traditional Review | Harness Engineering |
|---|---|---|
| Decision Maker | Human reviewer | AI agent (configured by humans) |
| Fatigue | Present (judgment wavers) | Absent (machine-consistent) |
| Scalability | O(n) - tied to reviewer count | O(1) - independent of reviewer count |
| Speed | Hours to days | Minutes |
| Consistency | Low (human variation) | High (rule-based) |
Claude Code Implementation: The 5-Step Workflow
Step 1: Configuration Setup (Constrain + Inform)
Begin by defining constraints and context in CLAUDE.md:
# MyProject — CLAUDE.md
## Critical Rules
### 1. TypeScript Strict Mode Required
- Enable `strict: true` in tsconfig.json
- No implicit `any` types
- Reason: Compile-time type safety
### 2. Consolidate test files in src/tests/
- ❌ src/components/Button.test.tsx
- ✓ src/tests/components/Button.test.tsx
- Reason: Clear structure, unified npm test execution
### 3. Prohibit external library additions
Approved libraries:
- react, react-dom
- typescript
- vitest, playwright
Forbidden libraries:
- lodash (existing utilities suffice)
- moment, date-fns (use Intl API)
- axios (fetch suffices)
Reason: Bundle size control, vulnerability minimization
### 4. CSS via Tailwind only
- ❌ styled-components, emotion
- ✓ Tailwind className
- Reason: Eliminate CSS-in-JS runtime dependency
### 5. Snapshot testing forbidden
- ❌ expect(output).toMatchSnapshot()
- ✓ expect(output).toBe('expected')
- Reason: Prevent accidental snapshot updates
## Design Philosophy
### Three-Layer ArchitectureUI Layer (React Components) ↓ [via custom hooks] Logic Layer (Business Logic) ↓ [via fetch API] API Layer (REST endpoints)
- Consolidate API calls in src/services/
- Keep UI and Logic loosely coupled
- Test each layer independently
### Data Flow
User Input ↓ Custom Hook (state, effect) ↓ Service Function (API call) ↓ Response Processing ↓ Component Render
## Steps 0-5: Automated Code Review Workflow
### Step 0: Developer Writes Code
```bash
# Developer implements new feature
code src/features/payment.ts
Step 1: Pre-submission Lint
npm run lint:fix # Auto-fix linting issuesStep 2: Claude Code Review Execution
# Claude Code executes:
"Review payment.ts against CLAUDE.md rules"Claude Code's execution:
1. Read CLAUDE.md
2. Compare code against rules
3. List violations
4. Generate fix suggestions
Step 3: Triage (Prioritize Fixes)
Claude Code automatically categorizes:
Priority 1 (Critical)
├─ TypeScript type errors
├─ Test coverage < 70%
└─ Forbidden library usage
Priority 2 (High)
├─ Architecture violations (layering)
├─ Naming convention violations
└─ File > 300 lines
Priority 3 (Medium)
├─ Missing comments
├─ Poor variable names
└─ Cyclomatic complexity > 10
Priority 4 (Low)
├─ Whitespace issues
└─ Import ordering
Step 4: Auto-fix + Validation
# Claude Code executes:
npm run lint:fix # Auto-fix linting
npm run type-check # TypeScript type check
npm run test:unit # Unit tests
npm run test:integration # Integration testsIf fixes are incomplete, repeat (max 6 times).
Step 5: Commit & Deploy
# Auto-commit after all tests pass
git add .
git commit -m "feat: payment system - auto-reviewed by Claude Code"
git push origin feature/paymentSolving Over-suggestion and Review Oscillation
Problem: Over-suggestion
Symptom:
❌ "This function is too complex. It has 13 responsibilities."
→ Developer confused. The function has only one real responsibility.
Root Cause: AI counts each processing step as a separate "responsibility"
Solution: Define responsibility clearly
## Definition of Responsibility
- "One responsibility = one business rule change causes cascade"
- Counts as single responsibility:
✓ Multiple processing steps (validation → calculation → output)
✗ Number of functions called (unrelated to function granularity)
## Acceptable Thresholds
- Function lines: < 300
- Cyclomatic complexity: < 10
- Nesting depth: < 3Problem: Review Oscillation
Symptom:
Loop 1: "Add types" → Types added
Loop 2: "Types are redundant" → Types removed
Loop 3: "Add types" → Loop continues...
Root Cause: Contradictory review criteria
Solution: Strict priority hierarchy
## Fix Priority (No Contradictions)
1. TypeScript compile errors (non-negotiable)
2. Test failures (non-negotiable)
3. Architecture violations (must fix)
4. Style violations (recommended)
5. Comment additions (suggested)
Rules:
- Don't re-flag Priority 1-3 if already fixed
- Don't flag contradictory Priority 4-5 on subsequent iterationsProblem: Insufficient Validation
Symptom:
Fix applied, no validation → Tests fail → More fixes needed
Solution: Enforce Verify step
## Step 4: Verify Checklist
- [ ] TypeScript compiles: tsc --noEmit
- [ ] Unit tests pass: npm test
- [ ] Integration tests pass: npm run test:integration
- [ ] Lint succeeds: npm run lint
- [ ] Build succeeds: npm run build
Don't proceed to next Correct step until all checked.Implementation Pattern: MCP Server Automation
Enhance Claude Code by integrating with MCP (Model Context Protocol) servers for deeper automation:
Pattern: 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 content
}
async function reviewCode(req: ReviewRequest): Promise<string> {
// Step 1: Read file
const fileContent = await fs.readFile(req.filePath, "utf-8");
// Step 2: Claude reviews
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 : "";
}
// Step 3: Triage
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;
}
// Step 4: Auto-fix
async function autoFix(
filePath: string,
issues: string[]
): Promise<boolean> {
// Fix only Critical/High
const criticalAndHigh = issues.filter((i) =>
i.match(/PRIORITY: (Critical|High)/)
);
if (criticalAndHigh.length === 0) {
return true; // Nothing to fix
}
const fixPrompt = criticalAndHigh
.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}`,
},
],
});
// Step 5: Validate
await execSync("npm test", { cwd: process.cwd() });
await execSync("npm run lint", { cwd: process.cwd() });
return true;
}
// Main: Max 6 iterations
async function reviewLoop(filePath: string, rules: string) {
let iteration = 0;
const maxIterations = 6;
while (iteration < maxIterations) {
iteration++;
// Step 2: Review
const review = await reviewCode({ filePath, rules });
// Step 3: Triage
const triaged = triageReview(review);
// If no Critical/High, done
if (
!triaged.has("Critical") &&
!triaged.has("High")
) {
console.log(`✓ Review passed after iteration ${iteration}`);
return;
}
// Step 4: Auto-fix
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`);
}Conclusion
| Aspect | Traditional | Harness Engineering | |---|---|---| | Reviewer Fatigue | High | None | | Judgment Consistency | Low | High | | Scalability | Reviewer-dependent | Constant | | Review Time | Hours to days | Minutes | | Human Role | All judgments | Critical decisions only |
By implementing harness engineering with Claude Code, you maintain quality while accelerating development by 10x or more.
Start with 5-10 critical rules in CLAUDE.md, then expand gradually as you refine operations.
Related Resources
- CLAUDE.md & AGENTS.md Complete Guide
- Claude Code AI Harness Design Patterns
- Claude Code settings.json Complete Reference
- Building MCP Servers
- Claude Code Hooks Automation Guide
Readers who want to take their understanding of AI agent development further will find Building AI-Powered Agents by Manus Hand invaluable.