This is the fifth article from the series "Tips from The Clean Coder". Here we gathered and summarized the main tips from the fifth chapter.
It has been almost twenty years since Test-Driven Development (TDD) made its debut in the industry. It came in as a part of the Extreme Programming (XP) wave, but has since been adopted by Scrum, and virtually all of other Agile methods. Even non-Agile teams practice TDD.
How can you consider yourself to be a professional if you do not know that all your code works? How can you know all your code works if you don't test it every time you make a change? How can you test it every time you make a change if you don't have automated unit tests with very high coverage? How can you get automated unit tests with very high coverage without practicing TDD?
That last sentence requires some elaboration. Just what is TDD?
You are not allowed to write any production code until you have first written a failing unit test.
You are not allowed to write more of a unit test than is sufficient to fail - and not compiling is failing.
You are not allowed to write more production code that is sufficient to pass the currently failing unit test.
These three laws lock you into a cycle that is, perhaps, thirty seconds long. You begin by writing a small portion of a unit test. But within a few seconds, you must mention the name of some class or function you have not written yet, thereby causing the unit test to fail compile. So you must write production code that makes the test compile. But you can't write any more than that, so you start writing more unit test code.
The tests fit the production code like an antibody fits an antigen.
If you adopt TDD as a professional discipline, then you will write dozens of tests every day, hundreds of tests every week, and thousands of tests every year. And you will keep all those tests on hand and run them any time you make any changes to the code.
Whenever you make a change to any part of your code, you simply run the unit tests. If they pass, you're nearly certain that the change didn't break anything. How certain is "nearly certain"? Certain enough to ship!
There have been several reports and studies that describe significant defect reduction. From IBM, to Microsoft, from Sabre to Symantec, company after company and team after team have experienced defect reductions of 2x, 5x, and even 10x. These are numbers that no professional should ignore.
Why don't you fix bad code when you see it? Your first reaction upon seeing a messy function is "This is a mess, it needs to be cleaned." Your second reaction is "I'm not touching it!" Why? Because you know that if you touch it you risk breaking it; and if you break it, it becomes yours.
But what if you could be sure that your cleaning did not break anything?
This is one of the most powerful benefits of TDD. When you have a suite of tests that you trust, then you lose all fear of making changes. When you see bad code, you simply clean it on the spot.
When programmers lose the fear of cleaning, they clean! And clean code is easier to understand, easier to change, and easier to extend.
The unit tests are documents. They describe the lowest-level design of the system. They are unambiguous, accurate, written in a language that the audience understands, and are so formal that they execute. They are the best kind of low-level documentation that can exist. What professional would not provide such documentation?
When you follow the three laws and write your tests first, you are faced with a dilemma. Often you know exactly what code you want to write, but the three laws tell you to write a unit test that fails because that code doesn't exist! This means you have to test the code that you are about to write.
The problem with testing code is that you have to isolate that code. It is often difficult to test a function if that function calls other functions. You have to figure out some way to decouple the function from all the others. In other words, the need to test first forces you o think about good design.
If you don't write your tests first, there is no force preventing you from coupling the functions together into an untestable mass. If you write your tests later, you may be able to test the inputs and outputs, but it will probably be quite difficult to test the individual functions.
TDD it's a discipline that enhances certainty, courage, defect reduction, documentation, and design. With all that going for it, it could be considered unprofessional not to use it.
TDD is not a religion or a magic formula. Following the three laws does not guarantee any of these benefits. You can still write bad code even if you write your tests first. Indeed, you can write bad tests.
By the same token, there are times when following the three laws is simply impractical or inappropriate. No professional developer should ever follow a discipline when that discipline does more harm than good.