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
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?
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).
- assignments (anything with
- branches (anything that jumps out of the current method)
- conditionals (anything that tests logic
SO, to reduce the ABC value, reduce assignments (use less intermediate variables), fewer branches (calling other methods), and conditionals (if/else statements).
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.
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.