Nathan Gilbert


Understanding & Linting for Cyclomatic Complexity

Background

There are many ways to measure the complexity of a piece of software: number of lines, Big O,
and so on. Cyclomatic Complexity attempts to do this by enumerating all linearly
independent paths through a segment of code. Linearly independent means that each path has at
least one edge that is not in another path (if viewing the code as a graph).

McCabe defined CC using graph theory as:

M = E - N + 2P

Where:

If a program had no control flow statements (if, switch, goto, etc.), then the CC would be 1. If
a program has 1 if statement, then its CC is 2.

A program that has 50 lines consisting of 25 consecutive "if/then" constructs will have over 33
million (2^{25}) distinct paths through the program. There's no way all those paths are
getting tested!

Let's analyze the following snippet of Python code:

def cc_example(b, c):
  a = 0
  if b > c:
    a = b
  else:
    a = c
  print(a, b, c)

What is the cyclomatic complexity of this method?
We can draw the graph of this method:

Cyclomatic Complexity Graph

The graph has seven nodes and seven edges, so its cyclomatic complexity is:

7 - 7 + 2 = 2

Suggested Levels of CC

There doesn't seem to be an agreed-upon threshold for what amount of CC is too much, though
generally the higher your code's CC, the harder it is to maintain. This naturally grows with the
number of lines in your software.

According to a random StackExchange post, the following is a rubric for CC scores and their
meaning:

Other websites have slightly different tolerances, but I like the StackExchange metric for
methods.

Pros of Using CC in Your Project

ESLint Settings for Cyclomatic Complexity

If you're using JavaScript or TypeScript in your project, you've already got a Cyclomatic
Complexity checker at hand. You just need to configure it to run when you're linting.

The following additions to your .eslintrc file will activate the complexity rules and several
other related checks. The complexity rule below measures Cyclomatic Complexity specifically.
Here, it is set to raise a linting error if the complexity of a method is above 5.

Many of the other rules measure things like clause depth and the number of lines in a method.

{
  "rules": {
    "complexity": ["error", 5],
    "array-callback-return": "error",
    "max-statements": ["error", 20],
    "max-statements-per-line": ["error", { "max": 1 }],
    "max-nested-callbacks": ["error", 3],
    "max-depth": ["error", { "max": 3 }],
    "max-lines": ["error", 200],
    "no-return-assign": "error",
    "no-param-reassign": "error"
  }
}