"Let’s Talk About TDD"
Oh dear, my eyes hurt. I’ve just seen yet another bullshit post about TDD:
Let’s talk about TDD. Yeah. That one. Now before someone tries to Uncle Bob or Kent Beck me in the comments, let me clarify — I absolutely support writing unit tests. Lots of them. High coverage. Solid safety nets. No argument there. But TDD — the “tests-first, logic-later” doctrine — is something else. I've heard a lot of lofty, near-religious praise from its evangelists. “Write the test first, and the design will follow.” Sounds nice on a slide. In practice? Not so much. In my entire career, I’ve met exactly one person who actually practiced TDD consistently. And guess what? Even he admitted he had to force himself into it like it’s a gym routine he secretly hates. Here’s my beef: TDD shifts your focus to the wrong thing. It idolizes the test — not the system, not the domain, not the architecture. You're supposed to build a well-structured, modular system… but instead you're writing a failing test for a method you just imagined into existence, praying that this micro-step will magically evolve into a beautiful system. Spoiler: it doesn’t. TDD doesn’t encourage thinking in terms of cohesion, coupling, design boundaries, or long-term maintainability. Instead, it traps you in a rabbit hole of tiny, myopic tasks. “One failing test at a time.” Great. Now you have a bunch of tests for methods that shouldn't even exist in a sane design. What would actually help? Taking a step back. Thinking in modules. Understanding business flows. Designing clear, purposeful abstractions. Not handcuffing your brain to a red-green-refactor treadmill and hoping architectural clarity falls from the sky. Sorry (not sorry), but making TDD a mandatory ritual is like solving architecture with duct tape — just with more IDE shortcuts. Curious to hear from folks who’ve actually built systems, not just pet projects and conference demos.
Sure, let’s talk about it. But are we talking about the same thing?
Paragraph 1: “Let’s talk about TDD…Uncle Bob or Kent Beck…” Citing Kent Beck or Uncle Bob isn’t some cliché name-dropping ritual. It’s an appeal to foundational voices in software craftsmanship. Kent Beck created TDD, and Uncle Bob has practiced and taught it for decades. Discrediting these figures upfront, as if their contributions are mere ideological noise, sidesteps serious engagement with the practice.
Paragraph 2: “But TDD — the ‘tests-first, logic-later’ doctrine…” “Tests-first, logic-later” is a mischaracterization. TDD isn’t about delaying logic. It’s about discovering design through a sequence of small, testable behaviors. If this is what you’ve “heard,” it might be time to listen to different people. TDD done right integrates logic and design, it doesn’t postpone it.
Paragraph 3: “In my entire career, I’ve met exactly one person…” Meeting “only one person” who practices TDD consistently says more about the environments you’ve been in than about TDD itself. It’s akin to saying, “I’ve only met one person who practices yoga every morning”: anecdotal, not evidence of inefficacy. Popularity or adoption rate isn’t a reliable proxy for value.
Paragraph 4: “TDD shifts your focus to the wrong thing…” This paragraph misrepresents what TDD emphasizes. TDD does not idolize tests. It uses them as a design and feedback tool. Suggesting that writing a test before implementation is somehow detached from good design is like saying naming a function before writing it makes you care more about the name than the function. The test represents intent. If you can’t name a behavior in a test, perhaps you haven’t clarified your design yet.
Paragraph 5: “TDD doesn’t encourage thinking in terms of cohesion…” On the contrary, TDD disciplines thinking about cohesion and coupling. Writing small, targeted tests forces you to design for clarity and decoupling, otherwise your tests become brittle. TDD doesn’t trap you; it reveals the traps already in your design. And no, tests are not for methods, they’re for behaviors. Systems are built one small behavior at a time, and that’s not a flaw. That’s how complexity becomes manageable.
Paragraph 6: “What would actually help? Thinking in modules…” “Thinking in modules” sounds nice but is vague without context. The OP implies TDD practitioners don’t do this, which is untrue. Good TDD practice starts with understanding domains and business flows, and tests act as a tool to capture and evolve those insights. No serious practitioner expects architecture to “fall from the sky.” They iterate toward it with feedback, which is the very principle TDD embodies.
Paragraph 7: “Making TDD a mandatory ritual…” Who exactly is making TDD mandatory? This feels like a straw man. No methodology should be dogma, and TDD is no exception. But dismissing TDD as ritualistic misses its practical benefit: it structures thinking. Just as design patterns guide architecture, TDD guides incremental, testable development.
Closing Thoughts TDD is not a silver bullet, but it’s far from duct tape. It’s a discipline that encourages thinking in terms of expected outcomes before implementation. This is a professional trait, not a constraint. It does not exclude architectural or domain-level thinking; rather, it complements it by grounding implementation in concrete, testable steps.
Critics of TDD often reject it without offering a viable, disciplined alternative. When you strip away the rhetoric, what many of them propose (whether implicitly or explicitly) tends to fall back on ad hoc, messy, or intuition-driven coding. That might work in the short term, or for small projects, but it’s not a sustainable approach for professional software development.
TDD, on the other hand, is a natural fit for professionals who care deeply about quality. It doesn’t exist to slow us down; it exists to sharpen our focus on doing the right thing before we worry about doing it well. That distinction is crucial. In software, it’s deceptively easy to build the wrong thing (with clean code, proper syntax, and passing tests) and still fail because the solution doesn’t meet the real need.
That’s where TDD excels. It forces us to express our intentions upfront, in the form of concrete, testable expectations. It provides continuous feedback and acts as a safeguard against drifting away from requirements. When practiced with discipline, TDD keeps us honest. It aligns implementation with understanding, and prevents us from writing clever code for problems that don’t exist.
Ultimately, most arguments against TDD crumble under scrutiny because they end up challenging the very principles that underlie sound software engineering: clarity, feedback, incremental progress, and validation of intent. TDD isn’t just about tests. It’s about thinking clearly, designing with purpose, and delivering with confidence. Dismissing that isn’t just rejecting a technique — it’s turning away from the kind of discipline that quality software demands.