Glossary · Change and test risk

What is the CRAP score?

The CRAP score (Change Risk Anti-Patterns) rates a method by combining its cyclomatic complexity with how little of it tests cover. The formula is comp(m)^2 x (1 - cov(m)/100)^3 + comp(m). Fully covered code scores its complexity; complex, untested code scores far higher. A score above 30 is the common threshold.

Last reviewed: November 2026

Also called Change Risk Anti-Patterns, CRAP index, Risky untested code

In depth Untested hotspots: what to test first: ranking files by churn, dependents and coverage, then writing the tests that matter.

How the CRAP score is measured

For a method m, with comp(m) its cyclomatic complexity and cov(m) its test coverage in percent:

CRAP(m) = comp(m)^2 x (1 - cov(m)/100)^3 + comp(m)

Coverage enters cubed, so the penalty falls fast as tests are added. At 100% coverage the first term vanishes and the score equals the complexity. At 0% coverage the score is comp^2 + comp.

MethodComplexityCoverageCRAP
format_name30%12.0
apply_discount8100%8.0
apply_discount860%12.1
route_payment1280%13.2
route_payment1220%85.7
reconcile35100%35.0

Two rows carry the lesson. route_payment at 80% coverage is fine; at 20% it is far past the threshold of 30. reconcile is above 30 even fully tested, because complexity alone exceeds it. The original authors proposed 30 as the line, and with zero coverage that means any method of complexity 5 or more reaches it.

Why the CRAP score matters

Complexity and coverage each mislead alone. A complex method with thorough tests is manageable, and a simple method with no tests is cheap to get wrong. CRAP flags the combination: code that is hard to reason about and has no tests catching mistakes. It gives a single number to sort a backlog and a way to decide between "write tests" and "split this up".

It is a per-method formula and inherits the weaknesses of its inputs: line coverage can be high without asserting anything, and cyclomatic complexity counts branches, not how hard code is to read. It also ignores whether anyone changes the code, which is why it works best next to churn and hotspots.

How to measure the CRAP score yourself

You need complexity and coverage per method, then the formula.

  • Java: crap4j, the original tool.
  • PHP: PHPUnit's HTML coverage report shows a CRAP index per method.
  • Python: radon cc -s src for complexity and coverage.py for coverage, then apply the formula in a short script.
  • JavaScript or TypeScript: ESLint's complexity rule gives per-function complexity, and Istanbul/nyc or c8 give per-function coverage.
  • Other languages: lizard reports complexity per function for many languages; pair it with your coverage tool.

Per-function coverage is the hard part. Line coverage reports are per file, so you must map covered lines back to each function's line range.

How repowise measures the CRAP score

repowise computes CRAP with the standard formula (crap_score in analysis/health/crap.py), and the threshold is 30. It is used in one detector, untested_complexity, which flags a function whose CRAP reaches 30: medium from 30, high from 50, critical from 100. It adds a function-level finding beside the file score in the code health analysis. The analysis is deterministic, with no LLM calls. See cyclomatic complexity for the input.

Coverage can be measured or estimated:

  • Measured: after repowise coverage add <report>, covered lines are mapped to each function's span and the percentage is real. The upper severity bands apply only to measured coverage.
  • Estimated: with no report, repowise places the function in a tier from the call graph (a test calls it, tests reach it, or neither) and says so. A tier never appears as a percentage.

Functions with complexity of 30 or more get a finding that asks for a refactor, since tests cannot bring them under the line. Test files and code that does not ship are exempt.

For a ranking at the file level, repowise also uses untested_hotspot (churn, dependents, coverage), covered in the guide to untested hotspots. See test coverage gap for the wider concept.

FREQUENTLY ASKED

CRAP score: questions

What is a good CRAP score?

Lower is better, and the lowest possible score for a method is its cyclomatic complexity. The threshold most often used is 30: above it, the method is considered risky to change.

Who created the CRAP score?

Alberto Savoia and Bob Evans, in 2007, alongside the crap4j tool for Java. The name stands for Change Risk Anti-Patterns.

How do you lower a CRAP score?

Add tests to raise coverage, or split the method to reduce its complexity. A method with complexity above 30 stays above the threshold even at 100% coverage, so it needs a refactor, not only tests.

Is CRAP the same as test coverage gap?

No. CRAP is a formula applied per method. A test coverage gap is the broader idea of code that matters but is not tested, and it can be judged by several signals, CRAP among them.

See it on your own repository.

Index a repository free and read the result for your own code and history. Rolling it out to a team? We will walk you through it.