Cyclomatic Complexity

Cyclomatic complexity is a complexity metric developed by Thomas McCabe in 1976 that measures complexity based on a graph approach. cyclomatic complexity is the number of linearly independent paths through a function, module, method, class or program. A linearly independent path is a path through the function, module, method, class or program that adds one or more node that was not included in any other linearly independent path.

To measure cyclomatic complexity source code can be visualized as directed graph with each branch statement drawn as a node and following source code until the next branch statement as edge.

To explain how to calculate cyclomatic complexity we start with an example. It's a safe implementation of the JavaScript add operator that sets an operand to 0 if the operand is not a number.


module.exports = function add(num1, num2) {
  if (isNaN(num1)) {
    num1 = 0;
  }

  if (isNaN(num2)) {
    num2 = 0;
  }

  return num1 + num2;
};

This code example can be visualized as a control flow diagram

Flow Graph
Control Flow Diagram of Function add

Calculation

Every function starts with cyclomatic complexity 1. For every branch statement cyclomatic complexity is increased by 1. In the above function we can identify 2 if statements that are branch statements, and thus we can calculate a cyclomatic complexity for the above add function.

Cyclomatic Complexity and Testability

Since cyclomatic complexity basically counts independent "branches" in a function, class, method or program cyclomatic complexity can also be seen as the upper bound for the number of tests needed to reach 100% branch coverage. Read more in Cyclomatic and NPath Complexity as Measurements of Testability.

Dealing with high Cyclomatic Complexity

The best strategy to decrease the cyclomatic complexity of a function and to increase the testability of a function is, to split up functions into smaller parts. In the source code example above we could extract a function getNumberOrZero to return the number passed as argument if the passed argument is a number or 0 if the passed number is not a number. So the above add function can be rewritten as


module.exports = function add(num1, num2) {
  return getNumberOrZero(num1) + getNumberOrZero(num2);
};

function getNumberOrZero(value) {
  if (isNaN(value)) {
    return 0;
  }

  return value;
}

This results in a cyclomatic complexity of 1 for function add and a cyclomatic complexity of 2 for function getNumberOrZero.