| name | code-review-expert |
| description | Conducts multi-axis code review. Use before merging any change. Use when reviewing code written by yourself, another agent, or a human. Use when you need to assess code quality across multiple dimensions before it enters the main branch. |
Code Review and Quality
Overview
Multi-dimensional code review with quality gates. Every change gets reviewed before merge — no exceptions. Review covers five axes: correctness, readability, architecture, security, and performance.
The approval standard: Approve a change when it definitely improves overall code health, even if it isn't perfect. Perfect code doesn't exist — the goal is continuous improvement. Don't block a change because it isn't exactly how you would have written it. If it improves the codebase and follows the project's conventions, approve it.
When to Use
- Before merging any PR or change
- After completing a feature implementation
- When another agent or model produced code you need to evaluate
- When refactoring existing code
- After any bug fix (review both the fix and the regression test)
The Five-Axis Review
Every review evaluates code across these dimensions:
1. Correctness
Does the code do what it claims to do?
- Does it match the spec or task requirements?
- Are edge cases handled (null, empty, boundary values)?
- Are error paths handled (not just the happy path)?
- Does it pass all tests? Are the tests actually testing the right things?
- Are there off-by-one errors, race conditions, or state inconsistencies?
2. Readability & Simplicity
Can another engineer (or agent) understand this code without the author explaining it?
- Are names descriptive and consistent with project conventions? (No
temp, data, result without context)
- Is the control flow straightforward (avoid nested ternaries, deep callbacks)?
- Is the code organized logically (related code grouped, clear module boundaries)?
- Are there any "clever" tricks that should be simplified?
- Could this be done in fewer lines? (1000 lines where 100 suffice is a failure)
- Are abstractions earning their complexity? (Don't generalize until the third use case)
- Would comments help clarify non-obvious intent? (But don't comment obvious code.)
- Are there dead code artifacts: no-op variables (
_unused), backwards-compat shims, or // removed comments?
3. Architecture
Does the change fit the system's design?
- Does it follow existing patterns or introduce a new one? If new, is it justified?
- Does it maintain clean module boundaries?
- Is there code duplication that should be shared?
- Are dependencies flowing in the right direction (no circular dependencies)?
- Is the abstraction level appropriate (not over-engineered, not too coupled)?
4. Security
For detailed security guidance, see security-and-hardening. Does the change introduce vulnerabilities?
- Is user input validated and sanitized?
- Are secrets kept out of code, logs, and version control?
- Is authentication/authorization checked where needed?
- Are SQL queries parameterized (no string concatenation)?
- Are outputs encoded to prevent XSS?
- Are dependencies from trusted sources with no known vulnerabilities?
- Is data from external sources (APIs, logs, user content, config files) treated as untrusted?
- Are external data flows validated at system boundaries before use in logic or rendering?
5. Performance
For detailed profiling and optimization, see performance-optimization. Does the change introduce performance problems?
- Any N+1 query patterns?
- Any unbounded loops or unconstrained data fetching?
- Any synchronous operations that should be async?
- Any unnecessary re-renders in UI components?
- Any missing pagination on list endpoints?
- Any large objects created in hot paths?
Fowler Smell Baseline (Always Applied)
A fixed set of 12 "Bad Smells in Code" (Fowler, Refactoring, ch.3) that serves as a baseline even when the repo documents no coding standards. Two binding rules:
- The repo overrides. A documented repo standard always wins; where it endorses something the baseline would flag, suppress the smell.
- Always a judgement call. Each smell is a labelled heuristic, never a hard violation. Skip anything tooling already enforces.
| # | Smell | What it is | How to fix |
|---|
| 1 | Mysterious Name | A function, variable, or type whose name doesn't reveal what it does | Rename it; if no honest name comes, the design is murky |
| 2 | Duplicated Code | The same logic shape appears in more than one hunk or file in the change | Extract the shared shape, call from both |
| 3 | Feature Envy | A method that reaches into another object's data more than its own | Move the method onto the data it envies |
| 4 | Data Clumps | The same few fields or params keep travelling together | Bundle them into one type, pass that |
| 5 | Primitive Obsession | A primitive or string standing in for a domain concept that deserves its own type | Give the concept its own small type |
| 6 | Repeated Switches | The same switch/if-cascade on the same type recurs across the change | Replace with polymorphism, or one map both sites share |
| 7 | Shotgun Surgery | One logical change forces scattered edits across many files in the diff | Gather what changes together into one module |
| 8 | Divergent Change | One file or module is edited for several unrelated reasons | Split so each module changes for one reason |
| 9 | Speculative Generality | Abstraction, parameters, or hooks added for needs the spec doesn't have | Delete it; inline back until a real need shows |
| 10 | Message Chains | Long a.b().c().d() navigation the caller shouldn't depend on | Hide the walk behind one method on the first object |
| 11 | Middle Man | A class or function that mostly just delegates onward | Cut it, call the real target directly |
| 12 | Refused Bequest | A subclass or implementer that ignores or overrides most of what it inherits | Drop the inheritance, use composition |
Change Sizing
Small changes are safer. Target:
~100 lines → Good. Reviewable in one sitting.
~300 lines → Acceptable for single logical change.
~1000 lines → Too large. Split it.
Splitting strategies: Stack (sequential), by file group, horizontal (shared code first), vertical (full-stack slices). Large deletions and automated refactoring are acceptable exceptions.
Separate refactoring from feature work. Small cleanups (renaming) can be included; structural refactors must be separate commits.
Change Descriptions
Every commit needs a standalone description. First line: short, imperative ("Delete FizzBuzz RPC" not "Deleting..."). Body: what and why — decisions, reasoning, links. Avoid: "Fix bug," "Add patch," "Moving code from A to B."
Review Process
Step 0: Parallel Sub-Agent Review (Recommended for non-trivial changes)
For changes >50 lines, split review into two parallel sub-agents to keep contexts clean:
Sub-agent A — Standards: Review code style, naming, architecture, and smells. Include the Fowler Smell Baseline above.
Sub-agent B — Spec: Review correctness against the originating spec/issue/task. Quote the spec line for each finding.
Spawn both via task tool simultaneously. Aggregate findings without merging or re-ranking — the two axes are deliberately separate.
Step 1: Understand the Context
Before looking at code, understand the intent:
- What is this change trying to accomplish?
- What spec or task does it implement?
- What is the expected behavior change?
Step 2: Review the Tests First
Tests reveal intent and coverage:
- Do tests exist for the change?
- Do they test behavior (not implementation details)?
- Are edge cases covered?
- Do tests have descriptive names?
- Would the tests catch a regression if the code changed?
Step 3: Review the Implementation
Walk through the code with the five axes in mind:
For each file changed:
1. Correctness: Does this code do what the test says it should?
2. Readability: Can I understand this without help?
3. Architecture: Does this fit the system?
4. Security: Any vulnerabilities?
5. Performance: Any bottlenecks?
Step 4: Categorize Findings
Label every comment with its severity so the author knows what's required vs optional:
| Prefix | Meaning | Author Action |
|---|
| (no prefix) | Required change | Must address before merge |
| Critical: | Blocks merge | Security vulnerability, data loss, broken functionality |
| Nit: | Minor, optional | Author may ignore — formatting, style preferences |
| Optional: / Consider: | Suggestion | Worth considering but not required |
| FYI | Informational only | No action needed — context for future reference |
This prevents authors from treating all feedback as mandatory and wasting time on optional suggestions.
Step 5: Verify the Verification
Check the author's verification story:
- What tests were run?
- Did the build pass?
- Was the change tested manually?
- Are there screenshots for UI changes?
- Is there a before/after comparison?
Multi-Model Review
Different models have different blind spots. Use Model A to implement, Model B to review for correctness/architecture, then Model A addresses feedback. Final call is human.
Dead Code Hygiene
After refactoring, check for unreachable/unused code. List it and ask before deleting: "Should I remove these now-unused elements: [list]?" Don't leave dead code — it confuses future readers and agents.
Honesty in Review
When reviewing code — whether by you, another agent, or a human:
- Don't rubber-stamp. "LGTM" without evidence helps no one.
- Don't soften real issues. If it's a production bug, say so.
- Quantify: "N+1 query adds ~50ms/item" beats "could be slow."
- Push back on flawed approaches. Sycophancy fails reviews. Propose alternatives directly.
- Accept override gracefully. Author has full context; defer when they disagree. Comment on code, not people.
Dependency Discipline
Part of code review is dependency review:
Before adding any dependency:
- Does the existing stack solve this? (Often it does.)
- How large is the dependency? (Check bundle impact.)
- Is it actively maintained? (Check last commit, open issues.)
- Does it have known vulnerabilities? (
npm audit)
- What's the license? (Must be compatible with the project.)
Rule: Prefer standard library and existing utilities over new dependencies. Every dependency is a liability.
The Review Checklist
## Review: [PR/Change title]
### Context
- [ ] I understand what this change does and why
### Correctness
- [ ] Change matches spec/task requirements
- [ ] Edge cases handled
- [ ] Error paths handled
- [ ] Tests cover the change adequately
### Readability
- [ ] Names are clear and consistent
- [ ] Logic is straightforward
- [ ] No unnecessary complexity
### Architecture
- [ ] Follows existing patterns
- [ ] No unnecessary coupling or dependencies
- [ ] Appropriate abstraction level
### Security
- [ ] No secrets in code
- [ ] Input validated at boundaries
- [ ] No injection vulnerabilities
- [ ] Auth checks in place
- [ ] External data sources treated as untrusted
### Performance
- [ ] No N+1 patterns
- [ ] No unbounded operations
- [ ] Pagination on list endpoints
### Verification
- [ ] Tests pass
- [ ] Build succeeds
- [ ] Manual verification done (if applicable)
### Verdict
- [ ] **Approve** — Ready to merge
- [ ] **Request changes** — Issues must be addressed
See Also
- For detailed security review guidance, see
references/security-checklist.md
- For performance review checks, see
references/performance-checklist.md
Common Rationalizations
| Rationalization | Reality |
|---|
| "It works, that's good enough" | Working code that's unreadable, insecure, or architecturally wrong creates debt that compounds. |
| "I wrote it, so I know it's correct" | Authors are blind to their own assumptions. Every change benefits from another set of eyes. |
| "We'll clean it up later" | Later never comes. The review is the quality gate — use it. Require cleanup before merge, not after. |
| "AI-generated code is probably fine" | AI code needs more scrutiny, not less. It's confident and plausible, even when wrong. |
| "The tests pass, so it's good" | Tests are necessary but not sufficient. They don't catch architecture problems, security issues, or readability concerns. |
Red Flags
- PRs merged without any review
- Review that only checks if tests pass (ignoring other axes)
- "LGTM" without evidence of actual review
- Security-sensitive changes without security-focused review
- Large PRs that are "too big to review properly" (split them)
- No regression tests with bug fix PRs
- Review comments without severity labels — makes it unclear what's required vs optional
- Accepting "I'll fix it later" — it never happens
Verification
After review is complete: