Why does TDD work? (Or why test driving your code is not a matter of "opinion")

Test Driven Development. "TDD". Some love it, some hate it. But many studies now suggest that it is highly effective in reducing bugs (some studies suggest up to 90% of bugs can be eliminated) and improving code structure. But how does it work? Why is there such an effect from such a simple approach? 

But before I start I must point out that a lot of what follows is summarising a great talk by Keith Braithwaite called "Measure for Measure: quantifying the effect of TDD" from 2008.  This is just a distillation of his somewhat mathematical analysis and conclusions, simplified, and in my own words aimed at a new audience who may have missed this particular gem.

So our story starts with complexity. Cyclometric complexity to be precise. Cyclometric complexity simply explained is a measure of the number of paths through a piece of code. So it depends on the number of decision points - if, while, and switch statements etc. The more paths, the more complex the code is, and how difficult it is to understand or test.

Back in 2008, there was a software quality plugin company called Enerjy (now defunct). They created an Eclipse plugin that provided metrics useful for code quality. And they ran analyses over tens of thousands of source code files, and came up with this interesting graph:

The graph plots the cyclometric complexity against the probability of finding a bug in the code. And it shows a very interesting trend - the higher the complexity, the greater the chance of there being a bug hidden in that complexity (remember what I said about being harder to understand and test?). The sweet spot appears to be around a complexity of 11, while up at 74 the probability of a bug is approaching a certainty!

So we can probably safely say

complexity  probability of code being buggy

So far so good. But what does this have to do with Test Driven Development?

It turns out Test Driven Development drives down complexity. It exerts a downward pressure on complexity by nudging developers towards simple, easy to test classes and functions, and also towards better separation of concerns (Single Responsibility Principle).

That's it. 

TDD => Simpler code => Fewer bugs

~~~~~~~~~~~~~~~~~~~

Postscript

To wrap up,  this post is referring to "Test Driven Development". This is a much misused technique, so here's a brief revision of what it is. This definition is important, and is what works the magic. Accept no substitutes.

TDD follows just three rules. From Robert C. Martin:

  1. You are not allowed to write any production code unless it is to make a failing unit test pass.
  2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
  3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.
Putting it another way. 
  • Write a failing test
  • Write code to make the test pass
  • Refactor
That's it. Write a test. Make it pass. Tidy up. Rinse and repeat. Seemples!

Comments