Test-Driven Development (TDD) is a tool of learning

It is an efficient learning workflow that conserves our cognitive resources and mitigates the illusions of knowing, which ironically limits its adoption.

Test-Driven Development (TDD) has roots in early software development (Siddiqui, 2021) and gained popularity through influential works like Test-Driven Development: By Example (Beck, 2002) and methodologies like Extreme Programming (XP), Agile, and Lean.

While TDD has been around for years, it remains somewhat niche and controversial, with limited adoption across the industry.

Research continues to grapple with measuring TDD's impact and understanding the barriers developers face in adopting it (Bakhtiary et al., 2020).

In this article, I present TDD as a tool of learning and examine why research struggles to quantify its effectiveness, why developers encounter obstacles in adopting it, and why many businesses choose to forego it.

TDD is a process in which test code is written before production code, which is then refactored until all tests are passed. It consists of three phases collectively known as the Red-Green-Refactor (RGR) cycle:

  1. Write a test that fails or doesn't compile.
  2. Write just enough code to make the test pass.
  3. Refactor to remove duplication, improve readability, and clean up the code.

As Kent Beck advises, "Never write a line of functional code without a broken test case" (Beck, 2001).

TDD focuses more on analysis and design than testing alone (Beck, 2001). Analysis makes you do the right thing, while design makes you do the thing right. Both demand iterative learning, as complete information, knowledge, and understanding are rarely available upfront. Ultimately, knowledge work produces decisions.

We operate with limited cognitive resources: attention, short-term memory, and willpower (Ahrens, 2017). Perceptual illusions, cognitive biases, and memory distortions can skew our perception of reality and our abilities, leading us to overestimate our knowledge and make poor decisions (Brown, Roediger, & McDaniel, 2014). Adopting effective learning strategies and objective methods to measure our progress and competence growth is crucial for success.

Software development is about capturing and encoding knowledge, which essentially is a process of learning. TDD offers an efficient learning workflow that helps conserve our cognitive resources and mitigates the illusions of knowing, which ironically is often what limits its adoption.

TDD counters the pitfalls of unintentional multitasking

Many people, especially younger generations, think they’re good at multitasking. However, studies show multitasking reduces productivity and quality (Ahrens, 2017). Multitasking isn’t about handling multiple things simultaneously but quickly switching attention between tasks. Each switch drains mental energy and diminishes focus due to attention residues (Leroy, 2009). Research shows frequent multitaskers are less productive than those who stick to one task at a time, and their ability to handle multiple tasks weakens over time. This misconception often stems from a lack of objective feedback and the Mere Exposure Effect, where repeated exposure makes them feel more skilled without actual improvement (Ahrens, 2017).

Writing code goes beyond typing on a keyboard. It involves understanding the problem, considering solutions, choosing data types and constructs, structuring logic, organizing files, testing, and refactoring. Without consciously separating these tasks, we risk unintentional multitasking, which reduces productivity and quality.

TDD introduces a structure that helps manage these shifts smoothly without the drain of attention residue, primarily through the Red-Green-Refactor (RGR) cycle.

TDD helps prevent ego depletion

Once thought to be a character trait, willpower is now understood as a limited resource, like a muscle that tires with use (Hollins, 2017). This concept, known as ego depletion, means that activities requiring self-control, decision-making, and active choices consume willpower. Replenishing takes time, and ego depletion reduces our capacity for further volitional tasks, even if unrelated.

While willpower can be strengthened, strategies like reducing the number of decisions are crucial to conserving it and preventing depletion. TDD supports this by breaking programming into small, manageable tasks within a structured Red-Green-Refactor cycle, reducing the number of decisions and preserving willpower.

TDD also provides immediate feedback in the form of passing or failing tests. This feedback loop sustains motivation and creates a sense of accomplishment with each passing test, helping to combat ego depletion.

Moreover, TDD fosters a disciplined routine. Repeating the same development pattern gradually turns coding into a habit, requiring less mental effort. Routine tasks rely more on automatic thinking, which reduces reliance on willpower and decreases the risk of ego depletion.

Following TDD helps avoid overengineering or overthinking by focusing solely on writing the minimal code needed to pass tests, streamlining development, and reducing cognitive load. This keeps the work focused, conserving mental energy for more challenging tasks.

Additionally, TDD removes the stress of designing the entire system upfront. Concentrating on small, incremental steps avoids the mental strain of long-term planning, which can lead to ego depletion.

Finally, a robust suite of tests boosts confidence in your code’s functionality, reducing stress and uncertainty. This confidence helps developers stay relaxed and mentally refreshed.

TDD mitigates illusions of knowing

We have two modes of thinking: System 1 and System 2 (Kahneman, 2011). System 1 is fast, intuitive, and automatic, drawing on experience and emotions for quick decisions. System 2 is slower, more deliberate, and more analytical, requiring a conscious effort to evaluate situations and make thoughtful choices. While System 1 is vital for rapid responses, it can be prone to biases and errors. System 2, though slower, is essential for complex reasoning and correcting impulsive judgments. Effective decision-making comes from balancing these systems—applying intuition when appropriate and careful reflection when necessary—to achieve more informed and reliable outcomes.

System 1 drives perceptual illusions, cognitive biases, and memory distortions, all of which can lead to errors in thinking. While we can’t eliminate these automatic processes, we can become more aware of them through metacognition—the practice of thinking about our thinking (Brown, Roediger, & McDaniel, 2014).

TDD enhances metacognition by providing the structure to consciously analyze and design each step of the development process. This allows developers to actively reflect on their understanding of the problem, the requirements of the code, and the effectiveness of their solutions.

Let’s review a few of the automatic processes TDD helps bring to light.

Fluency illusion

The fluency illusion occurs when people mistake ease of understanding for true mastery, often because the information is simple, clearly presented, or repeatedly encountered (again, the Mere Exposure Effect). People also tend to accept information that aligns with their beliefs (Confirmation Bias), reinforcing this illusion of understanding and leading to overconfidence as familiarity is mistaken for deep comprehension. This can result in misjudging one’s practical readiness.

Overcoming the fluency illusion requires cognitive effort. Techniques like summarization, explanation, and elaborative interrogation use the generation effect to deepen understanding by actively engaging with the material.

TDD aligns well with this learning approach by requiring developers to write tests that clearly define expected behavior, ensuring they fully articulate the problem before moving forward. This structured process allows developers to implement features while refining their understanding of requirements, reinforcing programming concepts, and proactively preventing errors. TDD thus transforms coding from a passive task into an active learning process that promotes deeper comprehension and results in more robust software.

Confirmation Bias

Confirmation bias is our tendency to notice, favor, and trust information that aligns with our existing beliefs, often leading to distorted perceptions and poor decisions.

TDD mitigates confirmation bias by enforcing the habit of writing test code that defines expected behavior before creating production code. Without this, developers may only test what they believe will work rather than considering potential issues.

Confirmation bias also arises during refactoring, where developers may assume their changes won’t introduce new bugs. TDD provides a safety net, verifying that refactoring hasn’t disrupted existing functionality. This approach benefits developers, especially in complex systems where even minor changes can have significant impacts.

Hindsight Bias

Hindsight Bias is our tendency to see past events as more predictable than they were. It happens when knowing an outcome affects how we evaluate past decisions, leading us to overestimate our ability to predict future events.

In TDD, the developer starts by writing tests before implementing the solution. This clarifies the system's initial expectations, helping to prevent the “I knew that all along” mindset afterward.

Because tests are written before coding, they serve as a clear record of what was initially expected. If the code’s behavior differs, the gap between the test and the implementation is evident and can’t be dismissed as hindsight bias.

TDD is valuable for all developers, especially those working on long, complex projects where it’s easy to claim that solutions seemed obvious only after fixing a bug or completing a feature.

Conclusion

Software development is about capturing and encoding knowledge, which essentially is a process of learning. Since humans operate on limited cognitive resources and are prone to biases and errors by nature, learning is the bottleneck.

In contrast to the Waterfall model, Agile development emphasizes discovery and removes obstacles to continuous learning by shortening and multiplying feedback loops. TDD aligns with this approach by requiring developers to clarify and specify expected behaviors before writing code. By writing tests first, developers are encouraged to think deeply about requirements and edge cases, fostering a more comprehensive understanding of the problem. This approach also creates a safety net for iterative learning, allowing developers to refactor code confidently, knowing that passing tests validate their understanding.

Psychologists Elizabeth and Robert Bjork describe desirable difficulties as short-term challenges that slow down learning but lead to better retention (Brown, Roediger, & McDaniel, 2014). TDD embodies this concept by introducing a productive challenge: it’s not hard to adopt due to its workflow, but because it exposes gaps in the developer’s understanding of both the problem and the solution.

Research continues to face challenges in measuring TDD’s impact and identifying adoption barriers, as TDD is a learning workflow. A developer’s openness to learning and prior knowledge heavily influence both the effectiveness of TDD and the obstacles to embracing it. Guiding software development toward continuous improvement by exposing code smells and refactoring opportunities based on sound design principles demands recognizing these issues and applying effective design patterns. This skill varies significantly among developers.

TDD has limitations. It doesn’t fully address complex security or concurrency issues, which often demand human judgment and can be difficult to validate solely through tests. Additionally, TDD may feel impractical under tight deadlines; however, such cases are often exaggerated. Developers and businesses sometimes prioritize speed over quality, borrowing time from the future without realizing the long-term impact due to cognitive biases.

Despite these limitations, TDD is a valuable approach for enhancing software quality and managing complexity. Since many digital transformations still experience partial or complete failure (Boston Consulting Group, 2020), TDD, while not widely adopted, could be worth exploring as an old solution to old problems.

References

Ahrens, S. (2017). How to take smart notes: One simple technique to boost writing, learning and thinking – for students, academics, and nonfiction book writers (Kindle ed.). Unknown.

Bakhtiary, V., Javdani Gandomani, T., & Salajegheh, A. (2020). The effectiveness of test-driven development approach on software projects: A multi-case study. Bulletin of Electrical Engineering and Informatics, 9(5), 2030-2037. https://doi.org/10.11591/eei.v9i5.2533

Beck, K. (2001). Aim, fire. IEEE Software, 18, 87–89.

Beck, K. (2002). Test driven development: By example. Addison-Wesley Professional.

Boston Consulting Group. (2020, January 16). Flipping the odds of digital transformation success. https://www.bcg.com/publications/2020/increasing-odds-of-success-in-digital-transformation

Brown, P. C., Roediger, H. L., III, & McDaniel, M. A. (2014). Make it stick: The science of successful learning. Harvard University Press. (Kindle Edition).

Hollins, P. (2017). The science of self-discipline: The willpower, mental toughness, and self-control to resist temptation and achieve your goals (Live a Disciplined Life Book 1). PH Learning Inc. (Kindle Edition)

Kahneman, D. (2011). Thinking, fast and slow (Kindle edition). Penguin Books Ltd.

Leroy, S. (2009). Why is it so hard to do my work? The challenge of attention residue when switching between work tasks. Organizational Behavior and Human Decision Processes, 109(2), 168-181. https://doi.org/10.1016/j.obhdp.2009.04.002

Siddiqui, S. (2021). Learning test-driven development. O’Reilly Media, Inc.

Subscribe to Adriano Di Giovanni

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe