One of the first questions I ask when working with a developer for the first time is, “do you do TDD?” The answer recently seems to be the same: “I try to when I have time, but not very often.”
There are two problems with this statement. Firstly, TDD code takes longer to write than non-TDD code. Secondly, tests are either an optional part of the code or they are faster to write after the fact.
The idea that coding takes longer with TDD is a fallacy.
The productivity of a programmer is not related to the number of lines he or she can churn out in a certain amount of time. If it was, we’d be focusing on learning to type faster. Often, less code is preferable to more code.
So what takes all our time up when programming? Most of our time is taken figuring out the problem space and figuring out how the current codebase works (or doesn’t). If we can reduce the time needed to understand the problem and domain, and we can get to grips with the current codebase faster, then we will produce solutions faster.
This is literally the main benefit of TDD. TDD doesn’t just mean test first, but “Test Driven Design.” Writing our tests before the production code helps us understand what shape the code should take and how it maps to the problem space. Without TDD, you are effectively guessing. You’re writing code and kind of hoping it comes out about right and does the job. There are obvious smells that result from this, like giant methods in large classes which suffer from low cohesion. These things simply don’t happen when doing TDD properly. Martin Thompson was supposed to have said that he can tell if code has been TDD’d from six feet away just by the shape of the lines of code, and I completely agree.
The regular code challenge column I run is a great example of this. Check out any of the solutions and you’ll be able to tell just by looking at the production code if it was written TDD, as the design will always be completely different.
The fact the code is cleaner is only part of the benefit. Writing tests first allows you ask all the questions up front about the design of the solution and the code. It helps you realize what your dependencies are and reveals what you don’t know yet about the problem space and need to ask questions about (if you can’t write a test, then you know you need to go and clarify the requirements).
This also results in significantly less rewriting and refactoring at the end of the solution, as the code is written with the design in mind up front. Testing after (or not testing) results in code that is effectively patched and hacked together to get something together.
Additionally, tests are not optional. The fact that there are good developers who still check code in without tests makes me incredibly sad. You simply cannot ramp up to releasing multiple times a week, let alone multiple times a day, if you’re relying on (A) manual testing and (B) hopes and prayers that no one will introduce a regression. Tests are a must have for any developer in 2016. I’ve already espoused the great benefits of test first, but by having tests written up front you have a clear line in the sand that tells you when your feature is complete (because the test goes green). The best part is that this test will then live on in perpetuity, being run on every check-in, guaranteeing the system is still functionally correct.
Coming back to the weekly code challenge examples. If you are not writing tests the only way you know if you’ve succeeded is by manually running the system, putting in some known inputs, and checking that the outputs are correct. This is a slow process even on the most simple of systems, and once you’ve finished the solution those checks will never be run again. If instead you write tests first, you know for certain, programmatically, when your work is done. And those checks and balances will then be run on every build.
Testing after the fact brings some of these benefits, although if you’ve never had a failing test then you have no idea if the test is correctly exercising the solution properly. Plus, the design will probably be weak.
If you do not have tests, then you have a brittle system and you will get burnt with major issues and bug fighting.