We all know that we should be doing test-driven development, don't we? So we all do it, all of the time right? Right? No?
Don't worry, you're not alone.
I know how to do TDD. Hell, I even teach TDD. But in my daily life I tend to work on rapid prototypes, demo's and proofs of concept. The main requirement here is getting something to show quickly. It doesn't need to be robust, it doesn't need to expect the unexpected and it doesn't even need to work all of the time. So here TDD isn't needed. In fact it gets in the way. And this is a bad habit to fall into.
Recently I had to create two Angular projects to exist inside the same environment. The first one I did without TDD, and due to various frustrations and stress-inducing moments of "build and hope" the second one I did with TDD.
And that is the background to this article. Some benefits of TDD I knew, some were real "light bulb" moments.
Let's start with a quick look at the downsides of TDD.
Downsides of TDD
Let's address the obvious. It's feels quite a bit slower in the early stages. You don't have things you can look at, see and play with as soon. You're almost building back-to-front, rather than front-to-back, so you don't have those early progress indicators. This is why TDD is not necessarily a good fit for rapid prototyping, when your final goal is those early progress indicators. This is why I don't use it for a lot of my work.
In this way TDD takes some getting used to, especially if you're out of the habit. But we can flip this downside round to be a benefit, in the context of a real application.
Benefit 1: No false sense of early progress
How many times have you built a front-end with lots of hard coded data or shortcuts in functions? This can be useful as a developer to envisage the final application and see how it flows. You understand that all of the hard work is still to be done, you're maybe 20% through. But show it to a non-developer and because they can see something working think that it's more like 80% done.
With TDD you tend to have less of this stuff up front, because you're focusing on writing the actual application logic and making sure that it all works.
You'll probably need to re-educate the non-developers who are involved in the project, but you're not going to be presenting something giving a false sense of progress in the early stages.
Benefit 2: Better sense of progress
This ties into the first benefit quite nicely. By writing skeleton tests and architecture you can have a real good feel of how much work is left to do, as well as how much you have done.
This helps you to have a much more accurate sense of where you are in the project, which in turn you can feed back to the non-developers in the project. If your tests are organised well and names properly you can even give this to others to demonstrate the progress.
Benefit 3: Continuous feeling of achievement
Constantly toiling on one massive application can become a real grind, moving from one part to another, being overwhelmed by the enormity of the project. Looking at the thing as a whole it's easy to get disheartened and feeling like you're not making any progress.
With TDD you get little wins all along the way. Every single test, subset of tests and component completed is a win! This is great for motivation and morale, which in turn improves productivity.
Benefit 4: A natural to-do list
Having a list of tests gives you a natural to-do list. The ones that pass are checked off, the ones that fail still need to be done. This assumes you're doing your TDD properly and writing all of your tests (or skeleton tests) to fail until you've completed them. If you have this setup in place then it's really easy to see what you have done and what to work on next.
And, unlike most to-do lists, this one is always up-to-date!
Benefit 5: Documents the code
We all like coding but hate writing documentation right? Even I hate documenting code, and I write books!
With TDD your test scripts give a great outline of what you expected each part of your code to do. This is useful for you coming back to it, but also for peer review, QA and for others coming to work on it some time in the future.
Sure, the tests take longer to write than the documentation probably would, but it feels more like coding so that's all good :)
Benefit 6: Lower impact from interruptions
You may well have seen this excellent image Why you shouldn't interrupt a programmer.
This is what life is like if you don't use TDD and your application grows. To make changes to the code you often have to run through the entire code flow in your mind, essentially compiling and running the application in your head! You have to know what is talking to what, the data type of each variable, the execution flow and so on. This is why interrupting a programmer is said to have a huge impact on productivity.
TDD greatly reduces the impacts of interruptions, as you are focusing on one specific function at a time, and often even one aspect of that function at a given time. Yes there are things you have to load into the RAM of your mind, but there's a lot less.
Benefit 7: Peace of mind when making changes
Your application is live and somebody reports a bug. If you're lucky you already know how to fix it. But do you know if your fix going to break something else in an unexpected place? It's hard to be confident!
Unless of course everything is tested. You change your code and your tests still pass then happy days! If they fail then that's just fine too. Perhaps the tests, mocks or harness need to be updated to match the new code, again relatively easy. Maybe this causes some other tests to fail and you need to modify another part of the code that you had forgotten was related.
I can tell you, it is much much nicer to find this stuff out in a test runner than back up in production.
Benefit 8: Easier to come back to after a break
Because your code is now self-documenting, and you have a natural to-do list it is much easier to come back to your code after a break. It doesn't matter whether it was vacation, sickness, another project or whatever, it is generally difficult to get back into a code base after much more than a week.
This doesn't go away entirely with TDD, but it is made somewhat easier. If you're only responsible for specific parts then it's really easy and all laid out for you, but if you're responsible for the entire application you'll probably have to refresh yourself of what it's doing, and why it's architected that way. So having documentation and a to-do list here can really help.
Benefit 9: Develop in smaller block of times
This was a big one for me, and ties back to a few of the earlier points. I've always found that coding is best when you have time to "get into it". At least two hours, preferably four.
But my life is such that I find two hours very hard to come by, let alone the mystical magical four hours of uninterrupted coding time.
If my project is TDD I find I can actually get useful work done in half an hour. I have a to-do list, I have less context to load into my brain and I can knock out a few things in those 30 minutes. Without TDD I'd probably spend 30 minutes figuring out how the thing works, where I was up to and what would be the best thing to work on next.
I can't highlight this enough, this has had a huge impact on my work and output.
Benefit 10: Remove dependency on external factors
The two projects I mentioned at the start of this post are both based in the same environment. The development version of this environment can only be accessed via VPN, and each part is owned by a large number of different teams. So if someone happened to be working on a dependency for my project, my project might not actually work at all. But is it my application, the VPN, a network issue, the end-point, the service, the security layer? Arrrgghhh!
With TDD you can - for the most part - avoid these types of headaches and hours of wasted time. Sure, you'll need to do end-to-end tests at some point, but for the bulk of your development hours you can just mockup the dependencies and code in blissful isolation.
Does your code work? Good! Is the dependency working? Who cares!
Benefit 11: Improved focus
The nature of TDD keeps you focused on the part you are currently working on, so you spend less time jumping around between different parts of the application. Again this is good for productivity as it reduces the context switching overhead of always jumping around the application.
This "jumping around" might be following a particular data or user flow, and this makes perfect sense if you don't have the "script" of TDD to follow. The problem with jumping around - as well as the context switching - is that you also tend to leave pieces of code unfinished.
Benefit 12: More robust code
The final benefit of TDD is the most obvious one. Assuming you're doing TDD correctly then your code should be much more robust, with fewer bugs and potential exploits. This one is pretty much a given, but worth including!
So, unless you're building demo-ware or rapid prototypes then there's a huge number of benefits to using test-driven development in your coding practices. If you were going to categorise them then they would probably all fit in a venn diagram of productivity, quality and confidence.
I love being productive. I love writing quality code. I love delivering with confidence.
I love TDD!