August 4, 2012

...Learn TDD with Codemanship

Back To Basics #6 - Prevention Is (Usually) Cheaper Than Cure

This is the sixth in a series of ten posts aiming to cover basic principles for people who want to learn how to be software developers (or better software developers), but who might be put off or confused by all the hype and hoopla that we've built around them over the years.

Life is full of examples of how it can pay dividends later when we take more care up front to avoid problems.

Taking a moment to check we've got our keys tends to work out cheaper than paying a locksmith to let us back into our house. Taking a moment to taste the soup before we send it out of the kitchen tends to work out cheaper than refunding the customer who complained about it. Taking a moment to check the tyres on our car before we set off tends to work out much, much cheaper than the potential consequences of not doing so.

In software development, we often find the same thing applies. If we misunderstand a customer's requirement, finding that out and fixing it there and then tends to work out much, much cheaper than discovering the problem in a released product and fixing it then.

We can grossly underestimate how much time - and therefore, how much of the customer's money - we spend fixing avoidable problems.

Decades of studies like this one have shown beyond any reasonable doubt that the effort required to fix errors in software grows exponentially larger the longer they go undiscovered.

A simple misunderstanding about the customer's requirements might cost 100 times as much to fix if it only comes to light after the software has been released to the end users as it would have done had it been spotted while we were pinning down that requirement.

A basic programming error might cost 10-20 times as much to fix after the software's released as it would had the programmer caught it as soon as they'd written that code.

The upshot of all this is that we've discovered that the best way for most teams to write good working software economically is to take more care while they're writing it (see McConnell, Software Quality At Top Speed)

Or, to put it more bluntly, teams that take more care tend to go faster.

This is one of life's rare instances of allowing us to have our cake and eat it. We can produce more reliable software (to a point), and it can actually cost us less to produce it.

The trick to this is to look for mistakes sooner. Indeed, to look for them as soon as we possibly can after making the mistakes.

For example, if we think there may be misunderstandings about the customer's requirements, we should test our understanding of those requirements while we're discussing them with the customer.

We looked in a previous post at the potential power of using examples to pin down a shared understanding between us and our customers. When we use examples effectively, we can test that we've really understood what they've asked for.

By beating out a precise, unambiguous and testable understanding of what the customer wants from the software, we not only significantly reduce the potential for requirements misunderstandings, but we also arm ourselves with a powerful tool for checking that we built the thing right.

Those examples can become tests for the software that we can use as we're writing it to make sure it conforms to what the customer expects.

Similar checking can be done by developers at the code level to spot programming errors in functions and modules as the code's being written. You can test a function or a module by itself, or in concert with other functions and modules, to better ensure that every line of code you write is error-free and to catch problems there and then.

A similar effect occurs when teams are working on different - but connected - parts of the software. I may indeed write my part of the software so that it has very few errors in it, but when I wire it up to seemingly error-free code written by someone else, we may discover that the two pieces, when they "speak" to each other, have different expectations about how to work together.

In order for software to be correct, not only have all the individual pieces got to be correct by themselves, but all those pieces need to work together correctly.

This is why good teams tend to integrate their individual pieces very frequently, so any slip-ups on the way different pieces interact with each other are caught as early as possible.

This principle of testing throughout development to catch problems as early as possible is entirely compatible with the other principles. (Phew!)

When we treat software development as a learning process, and use short feedback cycles associated with more effective learning, these short cycles give us ample opprtunities to test what we're creating (be it software or specifications) sooner. The shorter the feedbacl cycles, the sooner we can test things.

If we're using examples to pin down customer requirements, these examples can be used to test not only our shared understanding but also the test the software as it's being created to assure ourselves that what we're writing conforms to this shared understanding.

If we test as we code, and break our code down into the smallest, simplest pieces, it becomes possible to work in very short code-and-test cycles of just a few minutes each. This level of focus on the reliability of potentially every individual function (or even every line of code) can lead to software that has very few errors in it by the time it's ready to be put into use.

And studies have shown that such a focus throughout development can allow us to create better software at no extra cost. Hurray for our side!

Finally, to the "(usually)" in the title: we find there are limits to this principle. Yes, it pays to check the tyres on you car before you set off. It can significantly reduce the risk of an accident. But it cannot remove the risk completely. Kicking the tyres to check the air pressure and using a coin to measure the depth of the tread is good enough in most cases.

But if the dangers presented by potential tyre failures are much higher - for example, if that car is about to race in the Grand Prix - you may need to go further in ensuring the safety of those tyres. And in going further, the costs may start to escalate rapidly.

It makes little sense economically to apply the same kinds of checks to your car's tyres before, say, popping to the supermarket that they use on McLaren F1 tyres.

So, yes, you can take too much care...

But here's a warning. Too many teams misjudge where they lie in the grand scheme of care vs. cost. Too many teams mistakenly believe that if they took more care, the cost would go up. (Too many teams think they're McLaren, when, in reality, they are Skoda.)

But, even if we're Skoda, reliability is still almost as important as it is to teams like Mclaren. we may not need to go all of the way, but we probably need to go most of the way. "Perfect" and "good enough" are closer than you think in software.

It's very unlikely that your team is already doing enough to catch mistakes earlier, and that you wouldn't benefit from taking a bit more care. Most of us would benefit from doing more.

Posted 9 years, 2 months ago on August 4, 2012