Understanding CRAP and Cyclomatic Complexity Metrics

(CRAP = Change Risk Anti-Patterns)

14. Aug 2024
ยท

Two common code quality metrics are CRAP (change risk anti-patterns) and Cyclomatic Complexity, both incredibly important to ensure testable and understandable code. Example of CRAP and Complexity in Pull Request Comment

Cyclomatic Complexity

This is one of the oldest complexity metrics (more than 35 years old), and it still holds up today as a great way to measure code complexity. It describes how complex your code is with a number starting from 1 and scales up the more paths your code has. You want a low cyclomatic complexity score.

Some of the easiest indicators of increased complexity can be:

  • case
  • match
  • elseif
  • foreach
  • if
  • ??, ?:, ternary
  • while
  • for

Essentially the more paths in your code, the more complex it is. An example can be an if statement, since it splits your code in to two paths: the true path, and the false path.

Some general rules for complexity scores:

  • 1-6 is considered low complexity
  • 7-9 is moderate complexity, but still understandable
  • 10-20 is high complexity
  • +20 is very complex and hard to understand

Change Risk Anti-Patterns (CRAP)

Change Risk Anti-Patterns is a metric from 0 and up which ranks files based on Cyclomatic Complexity and Code Coverage. Meaning if your code is not too complex, and it has a high code coverage, the CRAP score will be low.

You want to have a low CRAP score, which can be accomplished by adding more covering tests, as well as reducing Cyclomatic Complexity.

In OtterWise we generally work with two types of CRAP, Combined and Average, this is done because neither is perfect by itself to represent quality of a pull request. First let's cover the difference.

Combined

This is the combined CRAP of all tracked files. The metric ranges from 0 and up. So if you have 2 files, one with 33 and another with 10, that makes your CRAP 33+10 = 43.

Average

This is the average of your CRAP across all tracked files. Using the above example of 2 files, one with 33 and another with 10, your average would be 21,5.

Why both Combined and Average?

Now that we have established the meaning, we can start digging in to why they are both important. We will continue with the above example (2 files; CRAP of 33 and 10 respectively).

Our average is 21,5 and our combined is 43, now let us imagine a team member submits a Pull Request that adds a new file that has a CRAP score of 0, bringing our total files to 3, but not affecting our combined CRAP. This will bring our average CRAP to 14,3. If we only reported average, this would like this collaborator actually brought down the project CRAP, but in fact they had no impact on the overall (combined) CRAP.

One could think that only using combined CRAP would be better then, however that also has some shortcomings. The most obvious one is that whenever a new file is added (with a CRAP score > 0), you always increase the combined CRAP, even if your file actually reduces the average. So despite your file being less CRAP'py than the average in your project, the pull request would still be marked as 'bad' since it increased CRAP, if we don't consider average CRAP as well.

Improve code quality today_

With OtterWise, you can track Code Coverage, test performance, contributor stats, code health, and much more.