Red Green Repeat Adventures of a Spec Driven Junkie

Understanding Assignment Branch Condition

Recently, I integrated Rubocop into my work flow and it’s been very humbling. I feel I’m a great coder with all the specs passing and doing a good job of refactoring, Rubocop always finds something wrong with my code.

I usually go back and make fixes based on Rubocop’s suggestions, but I keep running into the same few issues and solving them isn’t as easy as using: rubocop -a (which auto-corrects all the easy ones!)

The toughest offense for me so far: Assignment Branch Condition

This offense is harder to fix really make me think about my coding style in general. I want to learn more about this offense to help me understand. By understanding, my goals is to not make this offense so often and/or make it easier for me to fix in the future.

What is: ABC?

Rubocop message:

Assignment Branch Condition size for [method] is too high

The default is 15 and the first pass of my code is always way past this, probably on average twice this value. So cleaning up my code to meet the default value really takes a lot of work.

From the documentation:

This cop checks that the ABC size of methods is not higher than
the configured maximum. The ABC size is based on assignments,
branches (Method calls), and conditions. See
http://c2.com/cgi/wiki?AbcMetric

Really Understanding: ABC

Let’s understand what ABC is by checking out the definition of ABC:

ABC is strictly a software size metric, although it has often been
misconstrued as a complexity metric.
Size is computed by counting the number of assignments, branches
and conditions for a section of code. The counting rules in the
original C++ Report article were specifically for the C, C++ and
Java languages, and defined as:
Assignment -- an explicit transfer of data into a variable, e.g. =
*= /= %= += <<= >>= &= |= ^= >>>= ++ --
Branch -- an explicit forward program branch out of scope -- a
function call, class method call, or new operator
Condition -- a logical/Boolean test, == != <= >= < > else case
default try catch ? and unary conditionals.
A scalar ABC size value (or "aggregate magnitude") is computed as:

  |ABC| = sqrt((A*A)+(B*B)+(C*C))

The individual A, B and C counts provide distinct
information about the code that the overall size value
doesn't. For example, the B (branch) count is virtually
identical to "cyclomatic complexity".
Due to syntax differences, the ABC counting rules for a
specific language must generally be tweaked to fulfill the
goals of the metric. Futhermore, the metric is probably
unsuitable for non-imperative languages (e.g. Prolog).

Broken down:

  • assignments (anything with =)
  • branches (anything that jumps out of the current method)
  • conditionals (anything that tests logic if, case, unary ?)

SO, to reduce the ABC value, reduce assignments (use less intermediate variables), fewer branches (calling other methods), and conditionals (if/else statements).

Computing ABC

The ABC value is not just a counting of them, but a square root of the sum of their squares. If any one of them getting too high will spike the ABC count.

The Rubocop default for ABC metric is 15. What does 15 really mean?

Well, doing the math, to get an ABC score of 15, a method would have:

  • 8 assignments
  • 8 branches
  • 8 conditionals

(Just working backwards from 15*15 => 225; 225/3 => 75; Math.sqrt(75) ~=> 8.66)

Now that I lay it out that way, an ABC value of 15 is very reasonable. Having eight of each for a method is just enough to do a lot of work in a method, but a value of 15 keeps the method from spiraling out of control in assignments, branches, or conditionals.

Conclusion

Whenever I encountered Rubocop’s ‘ABC is too high’ message, I was annoyed with ABC metric because I didn’t understand how it was computed and I couldn’t refactor efficiently to lower the ABC value quickly.

Now that I spent some effort into researching what Assignment Branch Condition really means, I feel better about creating or refactoring code that has a better ABC score.