Taming software complexity
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it."
Folk wisdom, as reflected in Brian Kernighan's clever riff on clever code, is that insight we all know but generally ignore. But how can we measure "cleverness?" Supreme Court Justice Potter Stewart, trying to characterize pornography, memorably lamented that it somewhat defies definition, but "I know it when I see it." Likewise, most of us can sense excessively clever code but may disagree when examining specific code chunks.
We do know complicated functions are tough to understand, an agony to debug, and usually require excessive amounts of maintenance.
One of the tragedies of software engineering is that, in industry at least, we've successfully avoided the metrics-based quality revolution that transformed manufacturing. Few companies know their pretest bug rates in, say, defects per line of code. Or productivity in lines of code per hour. Or which are the worst functions in a project. In an experiment I conducted surreptitiously last year, only a third of correspondents could answer the seemingly simple question "how big is your code" in any metric they wanted to use (MB, lines of code, lines they developed, value in dollars, or, heck, the weight of the listings).
A management mantra states "if you can't measure it, you can't manage it." Though surely true for building widgets, it's a lot harder to apply to engineering. But some objective measures provide valuable insight into the art of developing firmware. Since we know complex functions are problematic, it seems logical that we measure and control complexity in functions.
And it turns out that's pretty easy to do.
But first, there are two kinds of complexity: essential, which is a quality about the problem being solved, and incidental, which is the complexity introduced by our engineering approaches. Here we're talking about incidental complexity, as that's the only kind we as developers can manage.
Thirty-one years ago, Thomas McCabe proposed a metric called "cyclomatic complexity" to attach a quantitative measure to a function's complexity. It's one of many such measures but is by far the one most used. It measures the number of "edges" and "nodes" in a function, where a node is a computational statement and an edge represents the transfer of control between nodes. Cyclomatic complexity is then:
Note that many different formulations of this equation are in the literature; this one is the simplified calculation that's usually used and was given in McCabe's original paper ("A Complexity Measure," Thomas J. McCabe, IEEE Transactions on Software Engineering, Volume SE-2, No. 4, December 1976, p 308-320).