- ≤100 lines/function, cyclomatic complexity ≤8
- ≤5 positional params
- 100-char line length
- Absolute imports only — no relative (
..) paths - Google-style docstrings on non-trivial public APIs
Fix every warning from every tool — linters, type checkers, compilers, tests. If a warning truly can't be fixed, add an inline ignore with a justification comment. Never leave warnings unaddressed; a clean output is the baseline, not the goal.
Code should be self-documenting. No commented-out code—delete it. If you need a comment to explain WHAT the code does, refactor the code instead.
- Fail fast with clear, actionable messages
- Never swallow exceptions silently
- Include context (what operation, what input, suggested fix)
Evaluate in order: architecture → code quality → tests → performance. Before reviewing, sync to latest remote (git fetch origin).
For each issue: describe concretely with file:line references, present options with tradeoffs when the fix isn't obvious, recommend one, and ask before proceeding.
Test behavior, not implementation. Tests should verify what code does, not how. If a refactor breaks your tests but not your code, the tests were wrong.
Test edges and errors, not just the happy path. Empty inputs, boundaries, malformed data, missing files, network failures — bugs live in edges. Every error path the code handles should have a test that triggers it.
Mock boundaries, not logic. Only mock things that are slow (network, filesystem), non-deterministic (time, randomness), or external services you don't control.
Verify tests catch failures. Break the code, confirm the test fails, then fix. Use mutation testing (cargo-mutants, mutmut) to verify systematically. Use property-based testing (proptest, hypothesis) for parsers, serialization, and algorithms.