Test-Driven Development (aka TDD) is a software development method that relies on creating automated tests before writing the code that needs to be tested.
This approach was first introduced in 2003 and has been gaining a widespread reputation for providing a much better quality of delivered software ever since. In fact, TDD can reduce the number of bugs in production by 40-80%.
At Sombra, we’ve been applying TDD in various projects. Our engineers have learned a lot about true TDD: what’s working well, what’s not working at all, or what’s only working with specific exceptions. And we’ll gladly share this knowledge with you.
What “TDD best practices” articles will never tell you
If you Google “TDD best practices,” you’ll get hundreds of standard articles on how to implement TDD. But you won’t believe how misleading most of them are. The truth is, TDD can’t be done following articles only – that’s what our partner Jack Crews, CTO at LegalMonkeys, has taught us.
We started working with Jack almost two years ago. Our engineers were assisting his team with the development of LegalMonkey’s software products for law firms. You see, Jack has been an advocate of TDD for a long time. Over the years, he picked up lots of details and practices that make a big difference in terms of software development quality.
We’ll share Jack Crew’s story that can help you understand the meaning of TDD better.
“Many years ago, my dad was building a bedroom in the basement, and I decided to help him. My dad explained that the project would go faster if we rented a nail gun instead of using a hammer. That sounded like fun to me, so we went to the store to rent one. Then, we bought special nails for the gun. Afterward, we went to a friend’s house to borrow an air compressor. And guess what? Two hours later, we hadn’t put together a single board. Still, I understood the idea of the preparations – we would make up time in the long run.
Finally, my dad and I were shooting nails and building walls. But after an hour of productive work, I accidentally placed a nail that split the board, and it went right into my dad’s hand. That’s right, I shot my dad with a nail gun! The project stopped for another couple of hours while I took my dad to get medical care. Eventually, we got the room framed late that night, but we had a really long day.
I think of TDD as that nail gun. As you see, my first experience with it wasn’t positive. In fact, I could argue that “it’s more expensive and a waste of time.” But I’m certain that if I walked up to a professional home builder who’s used to working with a nail gun and told him that he’s wasting his time, he would just laugh.
The same is with TDD. It takes a little while to learn how to use it properly, but once you do, you can work much faster. Many developers “try” TDD and have a bad experience because they don’t understand the right way of doing it. They usually tell me it’s a waste of time, and it slows them down. But I just laugh, pick my “nail gun,” and keep working.”
This compelling (and somewhat relatable) story must have you wondering:
- Should I develop my next software product using TDD?
- Should I immediately change the software development process to TDD on a project(s) I’m responsible for?
- If this approach was invented more than 15 years ago and has such a profound impact on quality, why isn’t it applied in each and every software project?”
- The outcomes of TDD sound too good to be true. What’s the catch?
If you have any of these questions on your mind, read further. we’ll help you understand more about different aspects of TDD.
Benefits of using TDD on a project
― Benefit 1. Increased code quality in the long-term perspective
In a TDD project, our team rarely needs to get back and refactor the methods for writing code. In some cases, we had only one day of refactoring a year. The reason is, TDD allows creating reliable production-ready code all the time. So as a manager, you won’t have to cease development for several weeks just to refactor the poorly written code.
― Benefit 2. Fewer bugs – better product quality
After introducing TDD, the number of development errors dropped to almost zero, which is a fantastic achievement. Using TDD drives a more profound understanding of acceptance criteria by all team members. This, in turn, leads to grasping business requirements way better by asking more questions and reduces the number of bugs that stem from misunderstanding business requirements.
― Benefit 3. 95% of the time is spent developing new functionality
Fewer bugs mean less time spent fixing them during sprints, which, in turn, can help you optimize the project budget. Contrary to the traditional development-testing ratio of 50%/50%, projects that use TDD allow engineers to develop new functionality 95% of the time and spend the remaining 5% on manual testing.
― Benefit 4. Better knowledge sharing inside the team
Pair programming is one of the important complementary techniques of TDD. More than that, it’s the only useful way of introducing software developers to TDD. The value of pair programming is twofold: it improves the code quality and fosters knowledge sharing between engineers. Working in pairs allows developers to collaborate with every other developer on the project during sprints. This way, each engineer eventually learns more about each module of the system from the person they’ve been cooperating with.
― Benefit 5. Onboarding new team members becomes very effective
In pair programming, all engineers have the same level of knowledge about the project. So, when a person takes a sick leave, a vacation, or leaves the team, other members can pick up their work almost instantly because at least one person has already worked in a pair with this engineer.
Thanks to the effective knowledge sharing inside a team, new members can start building production-ready functionality immediately. For instance, an engineer who has only been working on our project for four days released a fairly complex feature to production, and it was well tested, without bugs.
― Benefit 6. TDD reduces the time and money spent on unexpected problems in project management
The time and money you usually spend on fixing unpredictable bugs and refactoring pieces of code before implementing new functionality are already included in the development time using TDD.
Risks associated with TDD implementation
Though TDD seems like a silver bullet, out of our experience, it’s rarely done right and provides expected results. So, we summarized the most common following risks associated with introducing TDD into software development.
― Risk 1. Implementing TDD incorrectly
Like with everything new, managers may want to try to introduce TDD gradually and scale it if the approach turns out to be successful. For example, switch four engineers to TDD and leave the remaining six to work as usual.
The problem is that the best approach to start TDD is the “all-in” approach, meaning it can only be truly beneficial when applied fully. Otherwise, it will backfire. The so-called “partial TDD” often leads to issues like:
- No visible results in product quality. When you adopt TDD partially, you risk getting only slightly better unit testing or no result at all.
- Worse code quality. Incorrect TDD implementation can harm the code quality: poorly written tests can lead to hardly maintainable code tied together by the number of these tests.
- Increased turnover of engineers. If one part of the team produces code with unit tests and another one without them, you’ll get one messy codebase. This may frustrate the engineers who invested time in unit testing but got nothing out of it.
- Increased budget spent on the software product. With partial TDD implementation, engineers still have to spend time learning and adapting. As a result, the budget increases without immediate and visible quality improvements.
- Increased timelines. Incorrectly written unit tests lead to code quality drops, and engineers end up fixing bugs more often than before.
Our engineers have worked on TDD projects in other companies, so they’ve seen their fair share of TDD implemented incorrectly. All the pitfalls suggest that the standard “Code-Test-Refactor-Test” cycle would be faster and more productive than “partial TDD.”
To be 100% correct, it’s actually possible to implement “partial TDD” by creating clear boundaries in the project. For example, module A has a published interface and module B is a client. You can do TDD on A even if B does not. However, this should be driven by engineers very experienced in TDD, otherwise, it will easily go wrong.
― Risk 2. Slower development in the beginning
As with any new approach, TDD slows down the development at the beginning. The team has to adapt to changes, so the functionality will be developed slower than before. The duration of the adaptation period depends mostly on the team size and the engagement of team members skilled in TDD. The bigger the team, the longer it takes for everyone to adopt TDD, so for large teams of 12+ engineers, it could take as much as 6 months.
It’s critical to be ready for the adaptation phase and take it into account when planning the project timeline.
Other factors that impact TDD adoption and stretch the adaptation period are the:
- Development skills of engineers
- Motivation to adopt TDD
- Availability of such engineers
- Number of teams adopting TDD
― Risk 3. Slowing down the overall development
TDD increases the amount of code each engineer produces drastically. If writing one simple function previously took 5 lines of function code and additional 0-20 lines of unit testing code, with TDD, it will be 5 lines of function code + 50 lines of unit testing code (1 unit test with 10 lines of code for one line of function code). Complex features and functions require even more test code for good coverage. So for each function engineer now has to write from 2 to 10 times more code.
Also, consider that with the pair programming approach, features will be delivered by two engineers in pairs. That’s why managers with no TDD experience observe a decrease in productivity in terms of new functionality. By implementing TDD, the time-to-market of the first release is longer and requires more engineers, but its quality will be way higher.
However, out of our experience, there were cases when the productivity of the development team using TDD increased and the team started producing more functionality than before. This depends on the skill of the development team and how quickly business requirements are changing. If there are no drastic changes in the requirements and change requests are gracefully added to the backlog and planned ahead, you can expect higher development productivity after TDD adaptation.
Chart 2. How TDD affects team productivity according to change requests frequency.
On the other hand – if there are often change requests and features change, there’s always a risk that introducing TDD to the existing dynamic development process may slow down the development. But it’ll anyway increase quality.
― Risk 4. The long learning curve for engineers with no TDD experience
The TDD methodology is pretty complicated, and the only way to learn it is to appoint an engineer skilled in TDD to be a mentor for other team members. It usually takes 1-2 months of full-time work with an experienced engineer for a team member to get used to the new methodology.
According to research, junior and middle engineers usually need a couple of iterations to practice before effectively applying TDD. Though they start with low TDD skills, they become more effective as the adaptation progresses. Senior engineers, however, show a high average TDD conformance from the very beginning.
Is it worth the effort?
Judging from our experience, we can say it highly depends on your needs.
If you, as a manager, see that:
- The flow of requirements from the business and users is moderate, and the backlog doesn’t change often,
- The product quality demands are very high, i.e., you have almost zero tolerance for bugs,
- You are ready to invest more from the budget (pair programming will seem like 2x the budget in the beginning),
then TDD is definitely for you (provided you implement it correctly, of course).
If you see that:
- The backlog changes often, the requirements are changing mid-sprint, and it’s not uncommon for a business to ask you to deliver a change in 2-4 hours, skip refactoring and unit testing,
- You have a limited budget,
- The business is ready to sacrifice quality and tolerates bugs,
Then TDD will probably not solve your problems.
Are you thinking of implementing TDD in your project? Get a consultation with Sombra’s CTO for a step-by-step on doing TDD the right way.