I've been ignoring Quora for...years! (Once it started becoming a cesspool of silly questions and flawed answers.)
But just yesterday the platform did something that I don't recall it ever doing before: it sent me e-mail outlining a request someone had made, asking me, specifically, to answer a question about Test-Driven Development. It was about (1) my experiences and (2) initial challenges with TDD.
So here's my reply in its entirety (and I thought I'd be writing a brief 100-word reply!):
To question 1:
To any “classically trained” developer, TDD can feel entirely upside-down for the first month or so. Turns out that this feeling is entirely cultural or habitual, because every person involved in programming or testing knows that you have to know what the system is supposed to do in certain circumstances.
For me, after a while, TDD overcame those earlier lessons and became natural. Yet it would still take me two decades to finally realize why TDD was easier than test-after: we developers already and always “think in tests” or perhaps it’s safer to say we think in terms of examples. You cannot write a single line of code without intending some outcome. TDD simply asks us to encode that intention in a test before adding the code to make it pass.
At first, this feels stupid. But as the system grows, all the things that a single block of code might be doing would otherwise have to be kept in the developer’s head, and that fear of breaking an existing behavior slows down the non-TDD developer without them even knowing it. WIth a comprehensive (and lightning-fast) suite of tests holding all of those prior behaviors in check, the developer is free to confidently add code or even alter the design without fear of breaking anything.
On to the 2nd question:
My biggest challenge was likely all my previous training and experience with “object oriented” design which—prior to learning about patterns and the OO attributes such as encapsulation, cohesion, and coupling—was thoroughly incorrect. (Not entirely my fault: many OO training courses took the “reuse” [e.g., entity beans] or “better mousetrap subclassing” approaches, which are both fatally flawed notions.)
Because of those notions, the idea of being able to test a single behavior of an object was a challenge. Letting all that go and accepting unit[-of-behavior]-testing as a first principle led to astoundingly clear and maintainable emergent designs (through client-focused interface design and diligent refactoring).
Another thing we ran into very early on was the problem with external dependencies (HDDs and network devices, for example). Thankfully our XP coach in 1998 was the brilliant Fred George who showed us how and why we wanted to isolate those dependencies behind some kind of wrapper interface (an Adapter, Facade, or Proxy, for example) and then have the test include a test subclass and inject an instance of that “test double” (as they were eventually named) into the object under test.
And, though I never had to face this last challenge, during my 25 years (so far?) teaching TDD courses the greatest challenge people expressed *after* learning how to do TDD correctly (often during post-course technical coaching) was the tens-of-thousands of lines of untested code already written into their system. “How do I test a 4000 line stored proc?” and “How do we test this ball of poorly-written mainframe assembler code?”
In fact, I recall the exact day that it struck me that my TDD course was missing something: A Microsoft developer came up to me after class and said “That was great! I hope to use TDD on a future project. Unfortunately, I’m currently on a team managing 400,000 lines of untested C# code.”
So in 2008 I added a whole 2–4 hour “module” including an exercise where teams learned how to carefully add “legacy characterization tests” to their ugly ball of legacy spaghetti code.
So as of 2008, my TDD course, which is now 3 days long (~20 hours) instead of the 2-day I taught at Microsoft, includes a module on Test Doubles and a module on Characterization Tests. When these two ancillary techniques/practices are incorporated into a team’s skills, TDD becomes much easier to adopt and develop into a discipline.
My many clients have reported (well, those that stuck to it for a month, and those who bothered to report back when I asked) that within about a month the upside-down feeling was gone, and within about 4–6 months the TDD discipline had paid them back (both developers and product leadership) in ways they hadn’t imagined.