DEV Community

Nathan
Nathan

Posted on

What is REAL TDD? (It's NOT just Red-Green-Refactor)

When we talk about TDD, we often focus on technical aspects such as "writing tests first" or the "Red-Green-Refactor cycle." Given the name Test-Driven Development, this focus is only natural.

However, I want to discuss the core values that are often overlooked but are essential to practice alongside the TDD cycle.


Breaking it Down Small

In Kent Beck's book, Test-Driven Development: By Example, the most emphasized skill is the 'ability to break down tasks into small steps.' This isn't just about creating small methods or classes; it means decomposing the development process itself into tiny, granular steps—so small they might even feel "excessive."

Do these steps seem too small to you? Remember, TDD is not about taking teensy tiny steps, it's about being able to take teensy tiny steps.
- Test-Driven Development: By Example

In my view, TDD is more than just a "test-first" methodology. Its true value lies in receiving real-time feedback on your current progress. This is fundamentally different from "after-the-fact testing," where you merely verify if a feature works after the entire implementation is finished.

Complex code is never completed in a single breath. When implementing intricate logic, we instinctively repeat a cycle of running code, debugging, and checking results. TDD replaces this manual feedback loop with automated tests, providing confidence at every moment. If you don't feel this confidence, it is a sign that you are working in units that are too large.

Actually, we already try to develop in "small" increments by instinct. TDD simply adds an explicit verification tool—the "test"—to that natural process. Therefore, working in small steps is not an option; it is a foundational requirement of TDD.

This doesn't mean you must mechanically split everything into tiny bits every time. The point is to possess the ability to work small and adjust your pace accordingly.

Kent Beck refers to this as "shifting gears."

  • When you are confident, move fast with large strides (shift up).
  • When faced with unexpected errors or complexity, immediately decompose the steps and proceed with caution (shift down).

There is a fundamental difference between having the 'ability to break things down' and choosing your pace, versus working in large units simply because you don't know how to break them down.

By shifting gears fluidly, you escape the cycle of manual debugging and watch your test suite grow naturally.

..., so we have to change the test. That's Okay. Our guesses about the right interface are no more likely to be perfect than our guesses about the implementation.
- Test-Driven Development: By Example

Tests created this way feel different from "traditional tests." They signal that you have begun to view tests as a "Guide for Design" rather than just a "Bug-finding tool"—a clear sign that you are truly practicing TDD effectively.

To-do List

The second core value is the To-do List. This tool appears consistently throughout Beck's book. While we all use task lists, a TDD To-do List is unique for two reasons:

  1. You actually record it rather than keeping it in your head.
  2. It enables you to focus 100% on the single task at hand.

During development, it’s common to suddenly think of another fix or a brilliant new idea. The temptation to "just fix this real quick" is strong, but it leads to loss of focus. TDD is wary of such indiscriminate context switching. Every new thought—a fix, a feature, or an issue to check—should be added to the To-do List immediately, allowing you to return to your current task.

In other words, you focus only on what you are doing now.

Using a To-do List in this way significantly reduces the cognitive complexity of your work. This list isn't a grand project roadmap; it consists of the granular, specific items discovered while coding—such as implementing a specific method, removing a redundant variable, or even trivial reminders that would otherwise clutter your mind.

// Todo
// Implement 3 + 2 = 5
// Implement 'equals' method
// Check if 'bar' should be removed
// Extract 'foo' method
// Decide what to eat for lunch (???)
Enter fullscreen mode Exit fullscreen mode

This is closely tied to "Breaking it Down Small." If your steps are too large, you will try to handle too many things at once, and the To-do List will lose its effectiveness.

Small step. We'll make a note of the stinkiness and move on.
- Test-Driven Development: By Example

If you can break tasks down and perform TDD entirely in your head without issues, you may continue to do so. However, as you focus and start recording new tasks, you will realize the work is often more extensive than it seemed.

Crossing items off a physical list provides a level of certainty that unrecorded thoughts cannot. The list serves as a milestone for direction and progress, and you can be truly sure the work is finished only when the list is empty.

The To-do List directs you to pour all your energy into one thing at a time. In this light, the "T" in TDD might feel more like "To-do" than "Test."

Test = Behavior

What does "Test" mean in TDD? While interpretations vary, I believe it is closest to "Behavior."

This concept naturally leads to BDD (Behavior-Driven Development). BDD and TDD share the same roots. While BDD is often associated today with high-level business scenarios, its origin was an attempt to better communicate the true essence of TDD. The "behavior" in BDD wasn't limited to scenario testing.

Dan North felt that the word "Test" hindered people from practicing the real value Kent Beck intended. He redefined it as "Behavior," which gave birth to BDD. I agree with this perspective.

For more on this:

Ironically, just as TDD was misunderstood due to the word "Test," BDD has been somewhat pigeonholed into "scenario testing" because of the strong impression of the word "Behavior."

When I first encountered TDD, I mistook it for simply writing "unit tests" first. This was a natural misunderstanding caused by the shared terminology. However, as I grew to understand TDD, I realized it is fundamentally different from traditional testing. That difference is rooted in "Breaking it Down Small" and the "To-do List."

Kent Beck wrote:

I call them “unit tests,” but they don’t match the accepted definition of unit tests very well.
- Test-Driven Development: By Example

The examples in the book focus on 'behavior' rather than concrete implementation. The core rule is that tests should focus on what the code is supposed to do, not how it does it, and they should remain resilient to changes in internal logic. This is what distinguishes TDD tests from typical after-the-fact unit tests.

You may also find these resources helpful:

While it may be challenging to write every test this way in practice, focusing on behavior while using tests to gain confidence in your code is a significant step forward.


Conclusion

The principles discussed here might seem like common sense. But within the context of TDD, they take on a special significance.

If you already work in small increments, TDD may feel natural. If not, simply writing a test first won't make you a TDD expert.

TDD is not just a "testing skill"; it is a "way of developing" and a "change in how you think." By defining TDD as a process of stacking small successes to gain certainty, you can begin this journey with a much lighter heart.

Top comments (0)