Avoiding
Prof. Philip Koopman Spaghetti Code
“The go to statement as it stands is just
too primitive; it is too much an invitation to
make a mess of one’s program.”
― Edsgar Dijkstra, 1968 © 2020 Philip Koopman 1
Spaghetti Code
Anti-Patterns: http://www.hacker-dictionary.com/terms/spaghetti-code
Deeply nested
conditionals
“Switch” nesting
High Cyclomatic Complexity (too many paths through the code)
Unstructured code leads to bugs
Unstructured code is generally hard to understand, test, and review
– But, even structured code can be problematic if it is too complex
Want to limit complexity within each unit (e.g., subroutine, method)
– Complex code is difficult to review – you will miss bugs during review
– Complex code can be difficult or impossible to test
© 2020 Philip Koopman 2
Measuring Complexity
McCabe Cyclomatic Complexity (MCC)
Measure each module (subroutine, method, …)
Draw a control flow graph 1
– Graph has an arc for each path through the module
MCC=5
MCC is # of “holes” in graph + 1
– Worst case number of unit tests to cover all paths
2
– Might need more tests – it’s a guideline
Strict Cyclomatic Complexity (SCC)
For complex “if” tests, each condition counts 3 4
– “if ( (x == 0) || (y == 0)) …”
counts as +2, because need to test x!=0 and y!=0
5
– MCDC testing requires this type of coverage © 2020 Philip Koopman 3
High MCC Results in Tangled Code
Complexity beyond GOTO due to:
Nested conditionals
Overly complex lines of code
Multiple return points
Nested exceptions
Applying MCC
Want maximum MCC to be 10 or 15
– Above 30 is highly suspect
– Above 50 is untestable in practice
» Too tangled to reasonably test each path
» Exception for flat switch statements
– Above 75 predicts bug farms
» Each fix breaks something else
http://www.mccabe.com/pdf/mccabe-nist235r.pdf
© 2020 Philip Koopman 4
Strict Cyclomatic Complexity (SCC)
Problem: complex conditionals used to game complexity
If (x) { if (y) { if (z) {…} } } MCC of +3
If (x && y && z) {…} MCC of +1
– But same number of test cases…
… and same complexity
Solution: SCC (also known as CC2)
Every extra conditional Boolean term counts +1
– if (x) { if (y) { if (z) {…} } } SCC of +3
– if (x && y && z) {…} SCC of +3
Important notes on applying MCC/SCC:
This is a per-subroutine metric, not whole .c file
The point is to encourage breaking up complex code into small pieces
© 2020 Philip Koopman 5
Spaghetti Factor (SF) Metric
SF = SCC + (Globals*5) + (SLOC/20)
SCC = Strict Cyclomatic Complexity
Globals = # of read/write global variables referenced
SLOC = # source lines of code (e.g., C statements)
Scoring:
– 5-10 - This is the sweet spot for most code
– 15 - Don't go above this for most modules
– 20 - Look closely; possibly refactor
– 30 - Refactor the design
– 50 - Untestable; throw the module away and redesign
– 75 - Unmaintainable; throw the module and its design away; start over
– 100 - Nightmare; throw it out and re-architect
© 2020 Philip Koopman 6
Code Complexity Best Practices
Keep MCC below 10 to 15
Even better, keep SCC below 10 to 15
– Exception: easy to test flat switch statements are OK
This enables thorough unit test
Additional signs of complexity issues
“If” statements nested more than 2 or 3 deep
Nested “if” and “switch” statements
Excessive “break,” “continue,” multiple “return”
If your module is too complex, it’s time to break it up!
Focus on worst offenders & break pieces of logic out into helper functions
The point of this is to enable good peer review and good unit test
Complexity pitfalls:
Creeping complexity over time … at some point, refactor!
© 2020 Philip Koopman 7
https://xkcd.com/292/
© 2020 Philip Koopman 8
https://goo.gl/pvDMHX CC BY-NC 2.0