Test-Driven Development (TDD) is one of the Extreme Programming (XP) core practices, re-discovered in 2003 by Kent Beck, that relies on very short feedback loops: you write the test (expectation), make it fail, write the minimum amount of code to make it pass (redundancy is accepted at this point). Write a new test, it fails, make it pass… keep running the same flow until you realize that your objective/goal is done. Then is time to improve the code applying refactoring (improve the code without changing the behavior).
Usually make sense to apply refactoring after the second test because after the second expectation you see the value to improve the code – most important point here is to move forward step by step making the progress simple and concise (more about following this article).
TDD, or not TDD: that’s the question
Some of you might think that applying TDD wouldn’t be good because we want to get things done; we want to validate and ship our product as soon as possible and we can not “afford tests”. TDD or not TDD is a dilemma, there is a long discussion whether it’s valuable or not. We’ve seen successful products with or without TDD (and vice-versa).
In fact we have to deliver value faster and faster. Our products must add value to our business, it must be flexible and reliable. If you think better, test is not the key factor of your success – actually it will help you when the cost of change in your project is really high.
So, is TDD always applicable? Does it save our life?
We need to find the balance: move fast and grow up with confidence. I can tell you is…
“There is no silver bullet”
Frederick Brooks, in 1986, wrote a winner paper called No Silver Bullet – Essence and Accidents of Software Engineering where he claimed that there is no single development, technology or management technique which by itself can improve within a decade in productivity, reliability and simplicity.
Many of the classic problems of developing software products derive from this essential complexity and its nonlinear increases with size.
Additional reads about silver bullet  just go to the bottom.
Cool! Considering we don’t have something that acts as a magical weapon that solves a long-standing problem, we can start to think about what we have now…
The two kinds of code
Temporary code: also called as spike where the idea is just to check hypothesis, explore and, once you have something meaningful, we just throw it away.
Sustainable code: on the other hand we have the sustainable code – a code where we can grow up and keep the evolution under control.
Below I’ve drawn a chart based one these two kinds of code (Nagappan et al., 2008). Also I’ve added a third one which is the non-TDD. As you can check the orange line has a better starting, however, as the project grows we lost velocity and quality consecutively. Why? Usually we have a more inflexible (coupling) code, we spend more time debugging and fixing potentially bugs in production.
On the other side, with TDD, we have a starting point where we spend more time creating our automated test environment and our tests as well (green line). But as we grow, after few iterations, we can easily get our ROI (black bullet) when we spend less time debugging, fixing bugs in production and we can see the real value of an evolutionary design.
The blue line sounds promising, isn’t? That is the reason I’ve added a deliver line showing how deliverable is each one. There is no deliverable with spikes; we have a good deliver without TDD but at certain point we struggle with the cost of changes and, with TDD, we should be able to parallelize our development through the evolutionary design.
If you are still thinking to use the “blue-line” always remember that easily our code can shape something like this:
TDD in practice after 273.75 days
I’ve been working at Pivotal Labs for the last 9 months. This is my really first experience working with pure TDD. Even being a TDD company we do spikes and spikes don’t have tests… as I said before spike are hypothesis, once we have a reasonable answer we switch to the sustainable mode, in our case: TDD.
Let me share some highlights base on my experience after a while using TDD my whole day:
Always implement things when you actually need them, never when you just foresee that you need them.” – Ron Jeffries
TDD gives me the ability to think more about simplicity. That mean focusing on writing only the code necessary to pass tests. YAGNI is one of the XP styles where we just write the code to achieve the story – as a traditional developer at the beginning I was focused on the most flexible solution and that flexibility actually was a trap. Basically because we try to predict the future. We work with IDEs and not with crystal ball.
TDD helps me to avoid design up front, over-engineering, to find the “good enough” and evolve my design as the project evolve. By the way, what does simple mean? Run all the tests; has a clear intention; no duplication and fewest number of classes or methods – Kent beck’s definition.
Confidence to change faster
Every step forward we make we have tests in place and they gives us confidence to change because, potentially, side effects are covered. We don’t feel afraid to add a new feature and break four existing one – unfortunately side effects happens nevertheless shortly we will have a feedback – we wont see them in PROD!
Refactoring is a joy!
Refactoring is a important and enjoyable part of our flow. We can improve the new or the existing code (if it’s part of my story) confident that when we change we will have a fast feedback if we’ve broken something – TDD results (ideally) with smaller code and due that, refactoring of existing code are smoother.
From my previous experiences applying refactoring without test are extremely hard and many times impossible to achieve considering time X budget. They became a technical debit to the eternity. Code with test after are not impossible but painfull due potential tangled.
Less time debugging and fixing bugs. TDD makes me more productive, straight to the point reducing the debugging time and giving me consecutively less bugs.
Reduced defect density at IBM 40% and Microsoft 60% – 90%
Increase of time taken to code feature (15% – 35%) – Nagappan et al., 2008
Using the above result and using some fictional examples:
- Production bugs without TDD: 100;
- Production bugs with TDD: 40 (60% fewer, middle ground between 40% — 80%)
- Avg time to fix bug at implementation (TDD) phase: 1 hour
- Avg time to fix a production bug: ~15 hours
Playing with any of those variables will obviously alter the results. What is pretty certain is that TDD saves time & money.
There’s a reason why we write test first: writing test first is a design process. If we write your tests carefully, negative cases for each positive case this will tease apart dependencies between objects in order to make them testable in isolation. We will create interfaces to define interactions and describe behavior.
TDD gives a better sense to code low coupling and high cohesion. When I write my test first I don’t have a lot mocks or mocks returning mocks in order to achieve low level unit tests. In fact with TDD this is obviously more difficult so I don’t do it. I will end up extracting classes, creating more well-defined narrow interfaces…
Test-Driven Development is not about writing tests, it’s about driving the creation of the code to improve it’s quality.
Notice: all the opinions here are my own.
discovered – If you look for “test first” before 2003 you will find articles like: Report of The Nato Software Engineering Conference (1968); The Humble Programmer, Edsger W. Dijkstra (1972) and Craig Larman and Victor Basili in early 1960s IBM. So that’s the reason Kent Beck said he had re-discovered Test-First Development and renamed later as Test-Driven Development and “Digital Computer Programming D.D. McCracken, 1957”.
The original description of TDD was in an ancient book about programming. It said you take the input tape, manually type in the output tape you expect, then program until the actual output tape matches the expected output. After I’d written the first xUnit framework in Smalltalk I remembered reading this and tried it out. That was the origin of TDD for me. When describing TDD to older programmers, I often hear, “Of course. How else could you program?” Therefore I refer to my role as rediscovering TDD.
http://proceedings.informingscience.org/InSITE2012/InSITE12p165-187Bulajic0052.pdf – Overview of the Test Driven Development Research Projects and Experiments
https://zeroturnaround.com/wp-content/uploads/2016/04/10.1.1.104.6319.pdf – A Longitudinal Study of the Use of a Test-Driven Development Practice in Industry
http://www.nomachetejuggling.com/files/tdd_thesis.pdf – Quantitatively Evaluating Test-Driven Development by Applying Object-Oriented Quality Metrics to Open Source Projects
http://www.usc.edu/dept/ATRIUM/Papers/Software_Productivity.html – UNDERSTANDING SOFTWARE PRODUCTIVITY
https://repository.lib.ncsu.edu/bitstream/handle/1840.16/2431/etd.pdf?sequence=1&isAllowed=y – Analysis and Quantification of Test Driven Development Approach
http://jultika.oulu.fi/files/nbnfioulu-201505211608.pdf – Usage of Test-Driven Development in Open Source Projects
http://projekter.aau.dk/projekter/files/204129305/Report_swd903e13_.pdf – Test–Driven Development: 15 years later (2014)
http://library.iyte.edu.tr/tezler/master/bilgisayaryazilimi/T000412.pdf – TEST DRIVEN SOFTWARE DEVELOPMENT