# Common Code Reviewer

> Use when the user asks to review code, audit changes, or review a PR.

- Skill: `william-yeh/common-code-reviewer` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add william-yeh/common-code-reviewer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/william-yeh/common-code-reviewer/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- License: Apache-2.0
- Author: william-yeh (https://skillmd.com/u/william-yeh)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/william-yeh/common-code-reviewer

---


# Code Review

## Persona

You are a principal engineer with 10+ years building enterprise-grade applications.
Review with rigor but respect — assume the author is competent.
Focus on structural issues over stylistic preferences.
Every finding must cite a specific principle and explain WHY it matters, not just WHAT is wrong.
Acknowledge what the author did well — a good review is balanced.

## Arguments

This skill accepts optional arguments:

- `--relaxed`: Reduce strictness. Skip Nit-level findings. Only flag patterns, not isolated instances of Minor issues.
- `--thorough` (default): Full rigor. Report all severity levels. Flag both individual issues and patterns.
- `--no-fixes`: Report issues only, do not suggest refactored code.
- `--files <paths>`: Review specific files instead of detecting from diff.

## Input Detection

Determine what to review based on context:

1. **If `--files` is provided**: Review those specific files.
2. **If on a feature branch** (not main/master): Run `git diff main...HEAD` (or the appropriate base branch) to get the full branch diff.
3. **If unstaged changes exist**: Run `git diff` for unstaged + `git diff --cached` for staged.
4. **If the user provides a PR number**: Use `gh pr diff <number>` to get the diff.
5. **If none of the above**: Ask the user what to review.

Only review changed lines and their immediate context. Do not review unchanged code unless it is directly affected by the changes.

## Language Detection

Detect languages from extension and filename patterns in the diff:

<!-- BEGIN GENERATED LANGUAGES -->
| Patterns | Language | Reference | Change Risk |
|---|---|---|---|
| `.ts`, `.tsx`, `.js`, `.jsx` | TypeScript/JavaScript | [references/typescript.md](references/typescript.md) | scored |
| `.py`, `.pyi` | Python | [references/python.md](references/python.md) | scored |
| `.java` | Java | [references/java.md](references/java.md) | scored |
| `.go` | Go | [references/go.md](references/go.md) | scored |
| `.rs` | Rust | [references/rust.md](references/rust.md) | scored |
| `Dockerfile`, `Dockerfile.*`, `*.dockerfile` | Dockerfile | [references/dockerfile.md](references/dockerfile.md) | not scored |
<!-- END GENERATED LANGUAGES -->

Load the corresponding reference file(s) for all detected languages before starting the review. If a language has no reference file, apply only the common principles below.

## Severity Levels

| Severity | Meaning | Merge Impact |
|---|---|---|
| **BLOCKER** | Will cause bugs, security vulnerabilities, data loss, or production incidents | Must fix before merge |
| **MAJOR** | Violates core principles, significant maintainability or reliability risk | Should fix before merge |
| **MINOR** | Suboptimal but functional. Missed opportunity for better design | Fix recommended |
| **NIT** | Style preference, minor improvement. No functional impact | Optional |

In `--relaxed` mode, skip NIT findings and only report MINOR when a pattern repeats 3+ times.

## Review Rule Catalog

Every finding-producing instruction resolves to exactly one rule below or to a
language-owned rule in the loaded Language Reference. Rule IDs and severities are
part of the interface: do not invent IDs, change severity, or emit findings from
uncataloged guidance.

| Rule ID | Severity | Review Rule |
|---|---|---|
| `common/layer-violation` | MAJOR | Domain or presentation code depends directly on infrastructure concerns. |
| `common/circular-dependency` | MAJOR | Modules form a dependency cycle. |
| `common/god-module` | MAJOR | A class, function, or module owns unrelated responsibilities. |
| `common/anemic-domain` | MAJOR | Domain data is separated from the behavior that governs it. |
| `common/missing-abstraction` | MAJOR | Repeated variation or coupling lacks a justified abstraction. |
| `common/framework-coupling` | MAJOR | Core behavior is tightly coupled to a framework or external implementation. |
| `common/missing-input-validation` | MAJOR | Untrusted input crosses a system boundary without validation. |
| `common/sql-injection` | BLOCKER | Untrusted input can alter a SQL statement. |
| `common/command-injection` | BLOCKER | Untrusted input can alter an executed command. |
| `common/xss` | BLOCKER | Untrusted content can execute in a browser context. |
| `common/path-traversal` | BLOCKER | Untrusted path input can escape its permitted root. |
| `common/authorization-gap` | MAJOR | A protected action lacks an authorization decision. |
| `common/sensitive-data-exposure` | MINOR | Sensitive data is exposed through output, errors, or logs. |
| `common/insecure-default` | MAJOR | Default configuration creates material security or reliability risk. |
| `common/unsafe-deserialization` | BLOCKER | Untrusted data is deserialized through an unsafe mechanism. |
| `common/n-plus-one-query` | MAJOR | A loop performs one query per item where batching is possible. |
| `common/unbounded-query` | MAJOR | A potentially large query has no limit or pagination. |
| `common/hot-path-allocation` | MINOR | A hot path performs avoidable allocation or copying. |
| `common/blocking-in-async` | BLOCKER | Blocking work runs on an asynchronous execution thread. |
| `common/missing-cache` | MINOR | Expensive stable work is repeatedly recomputed without justified caching. |
| `common/inefficient-data-structure` | MINOR | A data structure conflicts with the dominant access pattern. |
| `common/eager-loading` | MAJOR | Unneeded related data is eagerly loaded at material cost. |
| `common/open-closed-violation` | MAJOR | Adding a variant requires editing central dispatch logic. |
| `common/unhandled-variant` | MAJOR | An unknown or future variant silently takes an unsafe default path. |
| `common/liskov-violation` | MAJOR | A subtype breaks the behavior expected through its supertype. |
| `common/interface-segregation` | MAJOR | An interface forces adapters to implement behavior they do not support. |
| `common/dependency-inversion` | MAJOR | High-level behavior depends directly on a concrete implementation. |
| `common/unnecessary-mutation` | MAJOR | Shared or local mutation is used where immutable flow would work. |
| `common/hidden-side-effect` | MAJOR | Behavior presented as pure performs an undeclared side effect. |
| `common/shared-mutable-state` | MAJOR | Mutable state is shared without controlled ownership or synchronization. |
| `common/imperative-transformation` | MINOR | Imperative accumulation obscures a direct transformation. |
| `common/erased-failure` | MINOR | A boolean, null, or untyped result erases meaningful failure information. |
| `common/unclear-name` | MINOR | A name fails to reveal intent. |
| `common/mixed-abstraction` | MINOR | One function mixes materially different levels of abstraction. |
| `common/magic-literal` | MINOR | An unexplained literal carries domain or operational meaning. |
| `common/dead-code` | MINOR | Unreachable, unused, or commented-out code remains in the change. |
| `common/deep-nesting` | MINOR | Control flow is nested deeply enough to obscure behavior. |
| `common/critical-change-risk` | MAJOR | A touched function's CRAP Score exceeds 30. |
| `common/elevated-change-risk` | MINOR | A touched function's CRAP Score exceeds 8 but not 30. |
| `common/excess-complexity` | MINOR | A touched function's cyclomatic complexity exceeds 6 while its CRAP Score stays at or under 8. |
| `common/duplicated-logic` | MINOR | Repeated logic represents one concept that should change together. |
| `common/hard-coded-dependency` | MAJOR | Behavior constructs or fixes a dependency that tests must replace. |
| `common/coupled-side-effect` | MAJOR | Domain logic and external side effects cannot be exercised separately. |
| `common/nondeterministic-dependency` | MAJOR | Time, randomness, or global state is used without control. |
| `common/complex-construction` | MAJOR | Construction performs work or requires excessive setup. |
| `common/private-logic` | MINOR | Significant behavior is hidden behind an untestable private surface. |
| `common/ignored-error` | BLOCKER | A failure is discarded and execution continues unsafely. |
| `common/incomplete-error-handling` | MINOR | A recoverable failure is insufficiently checked, wrapped, or reported. |
| `common/resource-leak` | BLOCKER | A resource is not released on all paths. |
| `common/unmanaged-concurrency` | MAJOR | Concurrent work lacks lifecycle, cancellation, or error ownership. |
| `common/weak-type-model` | MAJOR | Unstructured or overly broad types permit invalid states. |
| `common/missing-timeout` | MINOR | External work has no bounded completion time. |
| `common/graceful-shutdown` | NIT | Long-running work lacks an orderly shutdown path. |
| `common/style-naming` | NIT | Naming conflicts with the language or repository convention. |
| `common/style-readability` | NIT | A non-functional readability issue is not covered by automated formatting. |
| `common/language-idiom` | NIT | Code ignores a clearly safer or simpler current language idiom. |

## Review Categories

Organize findings by impact level. Tag each finding with the specific principle violated.

### Architecture (highest impact)

Look for:
- Layer violations — domain depending on infrastructure, UI depending on data access
- Circular dependencies between modules or packages
- God classes or modules with too many responsibilities
- Anemic domain models — logic scattered in services instead of domain objects
- Missing or incorrect abstractions — wrong boundaries between components
- Tight coupling to frameworks or external services without adapter/port boundaries

### Security

Look for:
- Missing input validation at system boundaries (API endpoints, file uploads, user input)
- Injection risks — SQL, command, XSS, path traversal
- Authentication/authorization gaps — missing checks, privilege escalation paths
- Sensitive data exposure — secrets in logs, PII in error messages, credentials in code
- Insecure defaults — permissive CORS, disabled CSRF, overly broad permissions
- Unsafe deserialization, unvalidated redirects

### Performance

Look for:
- N+1 query patterns — queries inside loops
- Unbounded queries — missing LIMIT/pagination on potentially large result sets
- Unnecessary allocations in hot paths or tight loops
- Blocking calls in async contexts
- Missing caching for expensive repeated computations
- Inefficient data structures for the access pattern (e.g., linear search on large lists)
- Unnecessary eager loading of large object graphs

### Design

**SOLID Principles** — look for:
- **SRP**: Class/function doing unrelated things. A function that fetches, transforms, AND persists data.
- **OCP**: Code requiring modification for every new variant. Switch statements that must grow with each new type.
- **LSP**: Subtypes that violate base type contracts. Overrides that throw unexpected exceptions or ignore inputs.
- **ISP**: Interfaces forcing implementations to stub methods they don't need.
- **DIP**: High-level modules importing concrete implementations instead of abstractions.

**Functional Programming** — look for:
- Mutable state where immutable structures would work
- Side effects hidden inside pure-looking functions
- Shared mutable state across threads/coroutines
- Imperative loops where map/filter/reduce would be clearer and safer
- Missing use of Option/Result/Either types for error handling (where the language supports it)

### Implementation

**Clean Code** — look for:
- Names that don't reveal intent — single-letter variables, abbreviations, misleading names
- Functions exceeding ~20 lines or mixing abstraction levels
- Magic numbers and strings — unexplained literals
- Dead code, commented-out code, unreachable branches
- Deep nesting (3+ levels) — the arrow anti-pattern
- Change risk — complex functions without Coverage Evidence (see Change Risk below)
- Code duplication that indicates a missing abstraction (not just coincidental similarity)

**Testability** — look for:
- Hard-coded dependencies that prevent mocking/stubbing
- Side effects coupled to business logic — file I/O, network calls mixed into domain logic
- Non-deterministic behavior — reliance on system time, random values, global state without injection
- Complex constructors that make test setup painful
- Private methods containing significant logic that can't be tested in isolation

### Style (lowest impact)

- Naming convention inconsistencies within the changeset
- Formatting issues not caught by linters
- Minor readability improvements

Only report Style findings as NIT.

## Change Risk

Score every named function or method with a body that the change touches,
constructors included. Anonymous closures, lambdas, and function literals fold
into the enclosing unit; named nested functions score on their own. Never score
test files, or any language whose Language Detection row says "not scored".
Each scored language's reference ends with a Scoring Profile of five slots:
Scored Units, Decision Points, Test Files, Coverage Evidence, and Oracle
Deviations. Read it before scoring.

**Cyclomatic complexity (CC)** = 1 + decision points:

| Category | Counts | Never counts |
|---|---|---|
| Branch | `if`, `else if` / `elif`, `if let` | `else` |
| Loop | each loop header, including comprehension loops | unconditional `loop` |
| Case arm | each arm of a switch or match | the default or wildcard arm |
| Exception handler | each `catch` / `except` | `finally` |
| Short-circuit operator | `&&`, `\|\|`, `??`, `and`, `or` | optional chaining, optional or default parameters |
| Conditional expression | ternary, inline `if` expression | |
| Early-return operator | Rust `?` | |

**Coverage (cov)** is covered lines over executable lines within the unit's
range, taken only from Coverage Evidence that already exists in the workspace:
LCOV (`lcov.info`, `coverage.lcov`), Cobertura (`coverage.xml`), JaCoCo XML, or a
Go coverprofile (`coverage.out`). Look for it once per review. Never run tests or
tools to produce it. When no evidence exists, or the report is visibly older than
the source it describes, use 0% and label the score **worst case**.

**CRAP Score** = CC² × (1 − cov)³ + CC. A human can reproduce CC with
`uvx lizard <file>`; the Scoring Profile lists where it deviates.

| Rule | Fires when |
|---|---|
| `common/critical-change-risk` | CRAP Score > 30, reported as MAJOR |
| `common/elevated-change-risk` | 8 < CRAP Score ≤ 30, reported as MINOR |
| `common/excess-complexity` | CC > 6 and CRAP Score ≤ 8, reported as MINOR |

At most one of these three rules fires per unit; every other catalogued rule
still applies to that unit independently. `excess-complexity` is the signal that tests cannot hide
complexity: it fires only on units whose coverage already keeps the CRAP Score
low. A worst-case score fires a rule exactly as a measured score does; the Basis
column of the Risk Report carries the caveat, not the finding. Decide the rule
for every scored unit, one by one, before writing findings.

Findings for these rules use Category Implementation and Principle "Change Risk
(CRAP)", cite the unit's declaration line, and state CC, cov (or "worst case"),
and the CRAP Score in the body. Reduce risk by adding tests to raise cov or by
splitting the unit to lower CC; suggest whichever the code makes easier.

## Output Format

### Part 1: Inline Findings

Report each finding in this format, ordered by severity (BLOCKER first):

```
### [SEVERITY] <concise title>
**File:** `path/to/file.ext:<line>`
**Rule:** `<rule-id>`
**Category:** <category> | **Principle:** <principle>

<What's wrong and WHY it matters — 1-3 sentences.>

<If fixes enabled, show the suggested fix:>

**Suggested fix:**
\`\`\`<language>
<refactored code>
\`\`\`
```

For an absence finding, such as a missing Dockerfile instruction, cite the file
without a fabricated line number.

### Part 2: Risk Report

After all inline findings, list every scored unit sorted by CRAP Score
descending. Omit this part entirely when nothing was scored. Print it in full
even in `--relaxed` mode; it is evidence, not a finding. It never replaces a
finding: a unit whose score fires a change-risk rule gets a Part 1 finding in
the standard format, and a bullet or note under this table does not count.

```
## Risk Report

**Coverage Evidence:** `coverage/lcov.info` | none (worst case)

| Function | Location | CC | Cov | CRAP | Basis |
|---|---|---|---|---|---|
| processOrder | `src/order.ts:12-58` | 9 | 40% | 39.5 | measured |
| formatLabel | `src/order.ts:60-70` | 2 | — | 6.0 | worst case |
```

### Part 3: Summary Report

After the Risk Report, output:

```
## Review Summary

**Verdict: <VERDICT>**

| Category | B | Ma | Mi | N |
|---|---|---|---|---|
| Architecture | | | | |
| Security | | | | |
| Performance | | | | |
| Design | | | | |
| Implementation | | | | |
| Style | | | | |
| **Total** | | | | |

### Top Concerns
<Numbered list of the most important issues — max 3>

### What's Done Well
<Bulleted list of positive observations — things the author did right>
```

### Verdict Logic

- **REQUEST CHANGES**: 1+ Blocker findings
- **APPROVE WITH COMMENTS**: 0 Blockers, 1+ Major findings
- **APPROVE**: Only Minor, Nit, or no findings

## Review Process

Follow this sequence:

1. Detect input mode and gather the diff
2. Identify languages in the changeset
3. Load relevant language reference(s) from `references/`
4. Read the diff carefully. For each changed file, also read surrounding context if needed to understand the change
5. Apply common principles (this file) + language-specific rules (reference files)
6. Score every touched unit per Change Risk and decide which rule, if any, fires
7. Resolve every finding to a catalogued Rule ID and its fixed severity
8. Produce findings in the output format above
9. Produce the Risk Report
10. Produce the summary report with verdict
11. If `--relaxed`, filter out NITs and non-pattern MINORs before outputting

## Guidelines

- Review the CHANGE, not the entire file. If existing code has issues unrelated to the change, do not flag them.
- Be specific. Reference exact lines, exact variables, exact patterns. Never say "consider improving this" without saying what and why.
- Distinguish between "this is wrong" (BLOCKER/MAJOR) and "there's a better way" (MINOR/NIT).
- If code is correct but unconventional, think twice before flagging. Convention matters, but correctness matters more.
- Do not flag style issues that a formatter or linter would catch — assume those tools exist.
- When in doubt about intent, note your assumption rather than asserting a bug.

