code-metrics-thresholds
verified9c69409a-b6a4-4ee3-8411-553400062309
Track code-quality metrics (complexity, length, maintainability) and set failing thresholds so quality doesn't silently decay. Use to put a number on "this is getting too complex".
Metadata
Skill file
# Code Metrics & Thresholds
Use when you want an objective, failing signal that code is getting too complex — instead of relying on reviewer vibes. Track complexity, maintainability, and length, and gate on them in CI.
## Measuring
### Radon (Python)
```bash
pip install radon
# Cyclomatic complexity (cc) — per function
radon cc . -a -s # -a = average, -s = sort by complexity
# Maintainability index (mi) — per file, A=best to F=worst
radon mi . -s
# Raw metrics (loc, comments, blank lines)
radon raw .
```
### Reading the Output
```text
F 15:0 Foo.bar - B (8) # function bar, cyclomatic complexity 8 → grade B
C 42:0 Baz.qux - F (32) # complexity 32 → grade F (needs refactoring)
```
| Grade | Complexity | Action |
|---|---|---|
| A | 1–5 | Fine |
| B | 6–10 | Acceptable |
| C | 11–20 | Watch it |
| D | 21–30 | Refactor soon |
| E | 31–40 | Refactor now |
| F | >40 | Emergency — untestable |
### Other Languages
```bash
# JavaScript/TypeScript — complexity via ESLint
npm install --save-dev eslint-plugin-complexity
# .eslintrc: { "rules": { "complexity": ["error", 10] } }
# Go — cyclomatic complexity via gocyclo
go install github.com/fzipp/gocyclo/cmd/gocyclo@latest
gocyclo -over 15 .
```
## Setting Thresholds and the CI Gate
```bash
# Fail CI if any function exceeds complexity 15 (grade C)
radon cc . --min C --max B --total-average
# --min C = flag anything graded C or worse
# Fail if maintainability drops below B
radon mi . --min B
```
```yaml
# .github/workflows/metrics.yml
name: Code Metrics
on: [pull_request]
jobs:
metrics:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install radon
- name: Cyclomatic complexity gate
run: radon cc . --min C --max B --total-average -nC
- name: Maintainability gate
run: radon mi . --min B
```
### Excluding Generated / Vendor Code
```bash
radon cc . --exclude "*/migrations/*,*/vendor/*,*/generated/*,*/node_modules/*"
```
```toml
# pyproject.toml — radon config
[tool.radon]
exclude = ["migrations", "vendor", "generated", "node_modules"]
```
## Triaging High-Complexity Hotspots
1. **Rank by complexity × change frequency** — a grade-F function nobody touches is lower priority than a grade-C function in active development.
2. **Add to a refactor backlog**, don't auto-fix. Complexity reduction is a behavior-preserving refactor (see `safe-refactoring-mechanics`).
3. **Link the metric to a specific refactor**: "Extract the validation block from `Baz.qux` (complexity 32 → target <10)."
```bash
# Find files with highest average complexity
radon cc . -a -s | sort -t'(' -k2 -rn | head -20
```
## Guardrails
- **Never** treat metrics as the goal — chasing "all functions <5 complexity" produces weird, over-split code. Metrics are a *signal*, not a target.
- **Never** auto-fix complexity violations — you can't mechanically simplify code. A human (or careful refactor) must do it.
- **Never** gate on metrics without excluding generated/vendor code — you'll fail on code you don't own.
- **Always** pair a failing metric with a concrete refactor plan, not a blanket "make it simpler."
## Pitfalls
- **Gaming the number**: Splitting one 30-complexity function into 6 trivial functions that pass the gate but are harder to understand. The *cohesion* matters, not just the count.
- **Thresholds set too tight causing churn**: A complexity limit of 5 will flag legitimate functions and generate endless refactor churn. Start permissive (15), tighten gradually.
- **Metrics without baseline**: Enabling a gate on an existing codebase with 200 violations fails immediately. Measure first, fix the worst, then set the gate at the current level and ratchet down.
- **Ignoring maintainability in favor of complexity**: A function can be low-complexity but unreadable (bad names, deep nesting, no structure). Use both metrics.
- **One-size-fits-all thresholds**: A router/dispatcher function is naturally higher complexity. Use `# noqa`-style exclusions sparingly but deliberately for legitimate cases.
## Verify / Checklist
- [ ] Baseline measured (`radon cc . -a -s`) and current state recorded
- [ ] Threshold chosen at a level that passes today but catches future decay
- [ ] Generated/vendor code excluded from the gate
- [ ] CI job fails the build on metric violations
- [ ] Worst offenders triaged to the refactor backlog with linked issues
- [ ] Metrics are reviewed as a signal during code review, not auto-applied
- [ ] Threshold documented with rationale (why 15 and not 10)
Attached files
No attached files.