- Consulting / Coaching
- Jeff’s Blog
I started doing TDD in 1999. I decided to tweet twenty random thoughts on TDD, roughly one per business day starting 2019 February 14, using the hashtag #20YearsTDD. Each tweet represents a good topic for an article that I should write (and might already have written).
Here are the tweets from #20YearsTDD. They appear in the order posted:
The Value of Failing Tests (30-Oct-2018)
“A test that passes with no change to your classes is always cause for humility and pause.”
We learn red-green-refactor for a reason–it’s not just “green-refactor.” In this blog post for Ranorex, I talk about the various reasons why you might see tests pass (green) when expecting them to fail. You’ll learn why it’s important to stick to that TDD rhythm.
Insights from the TDD Community (21-Aug-2018)
Check out We Do TDD for numerous great stories about test-driven development from people, teams, and companies that test-drive their software.
In this blog post for Ranorex, I talk about Dave Schinkel’s site that features interview questions and answers from a good number of TDD practitioners. I highlight some answers to questions about interesting TDD topics:
Find the Right TDD Approach for Your Testing Situation (16-Aug-2018)
No, there’s no one “right” way to practice TDD, though there are plenty of ways that technically are not TDD.
In this blog post for Ranorex, I talk about some of the variant ways of approaching test-driven development (TDD). I first introduce two distinct ways of describing TDD. I then discuss “assert-first,” “one assert per test,” and how tests are best named (or not).
Hardik Shah presents an article, “How Unit Testing Improves Code Quality in TDD.” The first part of the post, hosted at Simform’s site, describes test-driven development (TDD) and unit testing, and also demonstrates a brief example of creating code using TDD.
After presenting some recommendations for practicing TDD, Shah provides three “case studies,” which seem closer to interviews about industry experts’ perceptions of TDD. The three folks involved include J.B. Rainsberger, Frederico Goncalves, and myself.
Take a moment and head on over to WeDoTDD.com: a site that posts detailed information about companies, people, and teams who practice and teach TDD. You’ll pick up some great ideas about what works with TDD in real teams.
You’ll find an in-depth interview with me talking about my TDD experiences as an individual and also with respect to working with my customers. I enjoyed answering the questions, and some of them forced me to think hard about why I do things the way I do.
Test-Driven Development: A Love Story? (22-May-2018)
In this blog post for Gurock, I wax rhapsodic about TDD… well, maybe not. I don’t love TDD itself; I love the things that TDD enables me to do. I talk about what those things are, along with a bit of my personal history about how I got here.
Convincing Your Team to Adopt TDD (15-May-2018)
In this blog post for Ranorex, you’ll pick up a number of ideas about how to approach introducing TDD into your team. While TDD is a simple practice to learn (though a challenge to master), getting developers to think they might want to even consider it is not an easy task. I’ll also talk about why resistance is to be expected.
Why I Practice TDD: Speed and Need (2-Mar-2018)
Why do TDD at all?
In this blog post for Ranorex, I explain that my primary reason to do TDD is because it allows me to go faster, particularly as a system grows. I talk about some of the reasons why, and iterate some of the high cost of defects along the way.
The unit tests you produce in TDD represent an investment. You’ll want to ensure they continue to return on value and not turn into a liability.
It’s true, do TDD and you’ll likely have as much test code as production code, if not more. However, most sizable non-test-driven systems contain over twice the code needed. In other words: if
C represents the ideal amount of production code for a system’s implementation, and
TC represents test code, then
2C ≈ C + TC. That’s a wash, to me. I’ll take the version that comes with the tests, thank you.
2C? Twice the code needed? How’s that? The short answer: In non-test-driven systems, there’s no high-confidence way to keep code cruft from building up.
I worked with a customer in the Great Lakes area some years ago. They’d deployed a production application prior to my arrival; I was told its initial non-test-driven release was around 180,000 lines of Java code. The team, dedicated to TDD, had slowly but surely added unit tests as they test-drove new features, meanwhile cleaning up what they could in the legacy code. By my arrival, they’d shrunk the codebase to around 70,000 lines; by the time I’d left, they’d shrunk it perhaps another 10,000 lines.
Granted, much of the bloat was a large swath of dead and generated code they’d spotted. But a significant amount was purely unnecessary code.
I’d take the TDD Bet on almost any sizable legacy codebase: Given enough time to add tests and clean things up, it could be reduced to under half its size. A codebase with 100,000 lines of bloated code can transform into a codebase with 50,000 lines of production code and 50,000 more of tests.
I’m guilty of producing bloated code on my first pass of implementing some small solution. It’s much like writing (for example, these blog entries): I’ll slam out a few sentences in an attempt to spew what’s in my head onto the computer screen. Then I’ll read what I wrote, trying to put myself into the shoes of another reader: Does it make sense? Is it as punchy as it could be? I usually find numerous opportunities for cleanup!
There’s nothing wrong with slapping out a sloppy paragraph as long as you clean it up later. The same approach works for coding, with the caveat that you don’t want to wait too long–“later” might never come because something new demands your attention. When doing TDD, we ensure we clean up the code every few minutes, using the confidence that a passing test suite gives us.
Without TDD, we don’t do nearly enough of these little bits of clean-up. It’s far too unsafe: The chance of breaking other stuff is way too high. By definition, our non-test-driven code stinks a little bit more every few minutes.
Duplicate code rears its ugly head all the time. Duplication can happen in many forms–small bits of logic appearing dozens of times throughout the codebase, different algorithms to accomplish the same purpose, rote mechanisms where more abstract concepts would suffice.
I watched a developer copy-and-paste an entire 100+ line function in order to introduce a small, 5-line-long code variance. The right thing, of course, would be to use delegation, polymorphism, whatever, as long as it didn’t mean duplicating logic. And the developer knew that, as we might hope. “But I don’t want to change that function. I don’t want to be the one who crashes production because I changed some working code in order to make the system ‘cleaner.'”
Rather than introduce design concepts that minimize redundancy when they must deal with behavior variances, developers make what they think is the least impactful change out of fear. For these programmers, the notion of cleaner provides no immediate, obvious value. We know clean will make their job easier over time, but fear now intimidates more.
So, yes, with TDD, you’ll have as much test code as production code to maintain, but overall the amount of code will not be significantly different.
More importantly, you won’t fear your own code.