Ruby Software Complexity Metrics (Part three: Interdependence — Mathematical Analysis)

Cyclomatic Complexity, Perceived Complexity, and ABC Metric

Abhimanyu Singh
7 min readSep 29, 2019

The story has three parts:

Goal

In Part Two: Calculations, we have seen a couple of examples to understand the metrics calculations. In this part, we will analyze the interdependence of the metrics and figuring out the maximum values for the RuboCop analysis.

Cyclomatic Complexity and Perceived Complexity

The cyclomatic and perceived complexity values are determined by if, and, or, case, when, for, while, until, and, rescue nodes:

  • The if…elsif…else construct with n elsif branches, then, the contribution to cyclomatic complexity is n + 1, and perceived complexity is n + 2 because an else branch contributes to perceived complexity score.
  • Each of the and, or, for, while, until, and, rescue nodes, the contributions to cyclomatic and perceived complexity values are the same.
  • The case…when…else construct has n when branches, then, the contribution to cyclomatic complexity is n, and perceived complexity is 0.8 + 0.2 * n.

It is not possible to predict perceived complexity value given the cyclomatic complexity value, but we can derive an equation.

  • Let E be the total number of else branches for if only.
  • Let W be the total number of when branches.
  • Let C be the total number of case nodes.
  • Let CC be cyclomatic complexity.
  • Let PC be perceived complexity.

Then, PC = CC + E − 0.8 * (W − C)

Proof

  • Let a be the total number of if constructs without any elsif and else branch.
  • Let b be the total number of if…elsif constructs without any else branch, and each such construct have f1, f2, …, fb elsif branches respectively.
  • Let c be the total number of if…elsif…else constructs with else branch and each such construct have g1, g2, …, gc elsif branches respectively.
  • Let d be the total number of case…when…else constructs and each such construct have h1, h2, …., hd when branches.
  • Let e the total number of and, or, for, when, until, and, rescue nodes collectively.

Therefore,

CC = a + b + f1 + f2 + ... + fb + c + g1 + g2 + ... + gc + h1 + h2 + ... + hd + ePC = a + b + f1 + f2 + ... + fb + c + g1 + g2 + ... + gc + c + 0.8 * d + 0.2 * (h1 + h2 + ... + hd) + e=> PC - CC = c + 0.8 * d - 0.8 * (h1 + h2 + ... + hd)
=> PC = CC + c + 0.8 * d - 0.8 * (h1 + h2 + ... + hd)

Note that, c is the total number of else branches, d is the total number of case nodes, and, h1 + h2 + … + hd is the total number of when branches. So using E, C, and, W per the assumption:

PC = CC + E + 0.8 * C - 0.8 * W
=> PC = CC + E - 0.8 * (W - C)

It is easy to count the values of E, C, and, W from the code itself, so, we need not calculate the perceived complexity using the pseudocode from part two.

def hoge                          -- CC = 1
return :foobar if foo && bar -- CC = 3
baz =
case msg -- C = 1
when :qux -- W = 1, CC = 4
qux
when :quux -- W = 2, CC = 5
quux
else
:baz
end
plugh =
if corge -- CC = 6
:corge
elsif grault && garply -- CC = 8
:waldo
elsif grault -- CC = 9
:grault
elsif garply -- CC = 10
:garply
else -- E = 1
:plugh
end
quuz =
case xyzzy -- C = 2
when :foo -- W = 3, CC = 11
foo
when :bar -- W = 4, CC = 12
bar
when :thud -- W = 5, CC = 13
thud
end
if baz && (plugh || quuz) -- CC = 16
do_this
else -- E = 2
do_that
end
end
PC = CC + E - 0.8 * (W - C)
= 16 + 2 - 0.8 * (5 - 2)
= 16 + 2 - 2.4
= 16 - 0.4
= 15.6

Cyclomatic Complexity and ABC Metric

The conditions component of the vector <A, B, C> is the sum of the total number of comparison operations except for the spaceship operator and cyclomatic complexity. No direct relation between cyclomatic complexity and ABC metric is possible.

Let a — the largest component, be the total number of assignments, b be the total number of branches, and, c be the total number of conditions. For 0 ≤ x, y ≤ a, b = a − x and c = a − y. So, a² + b² + c² = a² + (x − a)² + (y − b)². Consider a square of length a, and quarter-circles on the bottom-right and top-right vertices with a radius of (a − x) and (a − y) respectively, then the area of these shapes describes the behavior of a² + b² + c².

ABC Metric: Components Contribution

Note that “technically we should have used squares but geometrically that would look a bit strange, so for the sake of demonstration I decided to use circles”.

From the image:

  • maximizing x and y, i.e., x = y = a, minimizes a² + b² + c², i.e., min a² + b² + c² = a².
  • minimizing x and y, i.e., x = y = 0, maximizes a² + b² + c², i.e., max a² + b² + c² = 3 * a².

If the value of the largest component of <A, B, C> is a, then the minimum and maximum values of ABC metric are given by a and sqrt(3) * a respectively.

Rubocop: Maximum Cyclomatic Complexity

There is no direct way to determine the maximum configured value for cyclomatic complexity, without performing some analysis and figuring out a sweet spot. Some references are suggesting the maximum values should be less than 5 or 10 for the code to be readable, maintainable, and, risk-free. Suppose, 95% of the methods have a cyclomatic complexity value less than 10 and 5% of the methods have a value greater than 30. It never indicates that these 5% methods are not maintainable or less readable.

Rubocop: Maximum Perceived Complexity

Consider the cyclomatic complexity is n. Also, PC = CC + E − 0.8 * (W − C), the minimum and maximum values of perceived complexity:

  • The minimum value is when E = 0 and W = n, i.e., C = floor(n / 2), So, PC = 0.6 * n (when n is even) and PC = 0.6 * n − 0.4 (when n is odd).
  • The maximum value is when E = n and W − C = 0, so, PC = 2 * n.

Considering the average-case scenario, PC = (0.6 * n + 2 * n) / 2 = 1.3 * n. So, if the configured maximum cyclomatic complexity value is n, then perceived complexity value could be 1.3 * n.

Rubocop: Maximum ABC Metric

Consider the cyclomatic complexity is n. As the components <A, B, C> could be used interchangeably, assuming conditions is the largest component, the minimum and maximum values of ABC metric:

  • The minimum value of the conditions is when there is no comparison operator associated with any of the decision points, i.e., C = n. Therefore, the ABC metric has values in the range [n, sqrt(3) * n] inclusive.
  • The maximum value of the conditions is when there is one comparison operator associated with each of the decision points, i.e., C = 2 * n. Therefore, the ABC metric has values in the range [2 * n, 2 * sqrt(3) * n] inclusive.

Considering the average-case scenario, ABC metric = (sqrt(3) * n + 2 * sqrt(3) * n) / 2 = 1.5 * sqrt(3) * n. So, if the configured maximum cyclomatic complexity value is n, then the ABC metric could be 1.5 * sqrt(3) * n.

Rubocop: Default Configured Maximums

The default configured maximums for:

  • Cyclomatic complexity is 6.
  • Perceived complexity is 7.
  • The ABC metric is 15.

From the analysis, the configured maximum for the perceived complexity is 1.3 * 6 = 7.8 and for the ABC metric is 1.5 * 1.732 * 6 = 15.59.

Summary

We have seen how one metric could affect the value of another — developed a relationship between cyclomatic complexity and perceived complexity. Also, mathematically derived the maximum values of the metrics to use with the RuboCop analysis.

📝 Read this story later in Journal.

👩‍💻 Wake up every Sunday morning to the week’s most noteworthy stories in Tech waiting in your inbox. Read the Noteworthy in Tech newsletter.

--

--