Bad Programming 101 - Part 2

Low Comprehensibility

Your browser needs to be JavaScript capable to view this video

Try reloading this page, or reviewing your browser settings

This segment highlights some ways for reducing subroutine understandability including poor naming, side effects, increased complexity, and duplicated code.

Keywords

  • Names
  • side effects
  • complexity
  • decision points
  • McCabe
  • cyclomatic
  • complexity

About this video

Author(s)
Karl Beecher
First online
22 July 2021
DOI
https://doi.org/10.1007/978-1-4842-7153-7_7
Online ISBN
978-1-4842-7153-7
Publisher
Apress
Copyright information
© Karl Beecher 2021

Video Transcript

A program can be made more comprehensible overall by being restructured into a series of smaller, easily understood subroutines. This segment discusses how to neutralize that benefit by making the subroutines themselves incomprehensible.

In the previous video, I explained how a sensible variable name risks revealing too much about a variable’s purpose, which makes things far too easy for the reader. Subroutines need names, too, so you should reuse the advice I gave you about poor naming. And yes, I know “reuse” is a dirty word in this video, but reusing bad ideas is OK. If your colleagues insist on better ones, you should try names which are merely vague, rather than incomprehensible. Labels like “doProcess” or “runComputation” are wonderfully weak, because they contain no specific information.

You can also get up to mischief by giving a subroutine a name which fails to fully describe everything it does, enabling you to sneak in unseen side effects. For example, someone might use your subroutine name searchInFile, which reports whether a string appears in a given file. The programmer looking for gold didn’t look too deep into your subroutine.

However, they subsequently noticed something strange– the files they search through sometimes suddenly go missing. After a laborious debugging session, they finally take a look at this searchInFile method. Now look at that. You’ve managed to make your humble subroutine not only search for text in the file, but also delete the file if the search term wasn’t found.

Poor names in the codebase generally lead to development that is lengthier and more problematic. Colleagues will thank you for using subroutine names that are clear and complete, instead of, say, ProcessNumbers, a more explicative name would be something like calculateMedian.

You can think of a subroutine as something which stitches together blocks of code. The result can be simple or complex, depending on your stitching skills. The previous video showed us how to make overly complex conditionals and right-brain bending loops. Thankfully, much of the same bad advice carries over to subroutines. Unsurprising, since the subroutine typically contains a mixture of things like conditionals and loops. That means when you give your subroutine complex loops or conditionals, the subroutine suffers in turn.

Every time you stitch an extra loop or another conditional into a subroutine, you add more possible pathways through that subroutine, because constructs like if statements and while loops include decision points. Every pathway is another possibility you have to verify, and by “you,” I mean “your co-workers.” Piling in more pathways burdens your reader’s mental capacity, and adds to the risk they overlook problems in your code.

This is why complexity serves as more fertile ground for growing problems than subroutine size alone. In fact, you can write relatively small subroutines which are nevertheless overly complex. For example, this subroutine is only about 15 lines long, but the number of decisions means that it has eight pathways through it, all of which have to be verified as working correctly. The number of pathways actually approaches the maximum level recommended by some of those goody-goody authors of software best practices.

You can tame code complexity using a number of means, but let’s focus on counting the number of possible pathways through a piece of code. This actually has a name– cyclomatic complexity. Calculating cyclomatic complexity gives an indication of how complex a subroutine is.

To do it, begin with the number one. Every routine has at least one pathway. Then count up the decision points in the routine. Each adds one to the subroutine’s complexity. A decision point is either a conditional or looping construct which makes a comparison, denoted by a key word like “if,” “while,” “for,” “case,” or similar, or a binary operator in an expression. For example, this adds one, whereas this adds two.

Keeping complexity low means the subroutine requires less effort to understand and verify that it works as intended. The recommended upper limit varies, but typical values hover around 10 to 15.

Some simple steps towards reducing a subroutine’s complexity include simplifying the expressions and decision points, refactoring duplicated code into a single reusable routine, moving one complex part of the code into its own subroutine. The last example could be rewritten like this. Some of the code has been moved into new subroutines. This reduces the subroutine’s own complexity to two. The new subroutines each have a complexity of five or less.