Another follow up to “Test Small, Before Testing Big“
We’re most productive when we are coding “in the flow,” a mental state described as being fully immersed in an activity with a feeling of energized focus and success. It’s the state where code effortlessly flies onto the IDE screen and new unit tests pass with ease. Frank Sommers questioned TDD as a possible impediment to the flow. My experience with TDD has been much more positive than Frank’s, but this quote sums it up for me:
Writing code in a certain way can help us get in a state of flow. For flow to occur, we must work on a chunk of problem at the right complexity level – a problem that’s neither trivial, nor overly complex. We must have a clear objective for what we’re working on — for instance a specific, well-defined use case. To receive immediate feedback, we must unit test the code as we write it. Finally, it should be possible for these small pieces of functionality to work on their own, without having to go through major integration steps.
TDD “flows” when you can cycle very rapidly from writing a unit test to coding to success to writing the next unit test. Small tests that are easy to setup and make pass result in effortless coding. Big tests that take a long time to setup and lead to copious amounts of debugging can destroy the flow.
I’d say that there are two primary challenges to TDD flow:
- Creating right-sized unit tests
- Determining the next test to write
One of the most pernicious problems that people have with Test Driven Development is “Writer’s Block.” Difficulty in finding the first unit test to write for a coding task or finding the next unit test to write will slow TDD down to a crawl. You can easily fall into an Analysis Paralysis trap where you’re afraid to start coding because you don’t yet understand how the whole picture fits together. My advice? — forget about the whole and code from the bottom up. Pick an easy task that you do understand and write an isolated unit test for only that small task. Then pick another easy task and repeat. The interfaces and usage of the small pieces can give you insight into the structure of the remaining pieces.
Okay Jeremy, how do I get started if I don’t even know what the tasks are? What do I do when I get stuck? Yours will vary, but here’s an incomplete list of my bag of tricks:
- Start with some upfront thinking. The single most effective design technique for me is to simply make a list of the things that have to be built to code a user story. It’s too simple to be any kind of “Something Driven/Oriented Design,” but it’s often enough. The key is to use the upfront design thinking to break down a user story into approachable tasks, and switch to TDD before you hit the point of diminishing returns. Having the list somewhere close can help prod you to the next unit test.
- Use Responsibility Driven Development to come up with some candidate classes and methods to start with
- Doodle some UML sketches.
- Do a throwaway spike. It’s far more productive than staring at the screen. Sometimes the best thing to do is just write some code and see where it starts going, contingent of course on throwing that code away later
- Um, note to self, talk it over with one of your colleagues. It’s silly how many developers, myself included, simply will not think to ask somebody else or talk it over.
Pair programming brings it’s own set of challenges for establishing a TDD flow between the pair. I’ve found that a pairing session is more successful when you can do Ping Pong Programming. I write a unit test, then my pair takes over the keyboard and completes the code to make the unit test pass. He will then code the next unit test and slide the keyboard back to me. It takes a shared understanding of the task at hand and a little bit of time to get into that state, but when you do get there it’s a very satisfying feeling. One thing pairing should do is to help fill the gaps in one person’s flow. The backseater, i.e. the guy without the keyboard, should really be thinking about the next unit test while the other fellow is typing. The best thing to say, and I’m not a poster child for this by any means, is to keep talking as you go.*
Eric Hodel takes on the same subject from a different angle in TDD and Creative Flow.
I’ve got yet another follow up on Test Small, Before Testing Big queued up about using mock objects to avoid context switching, writing smaller tests, and maintaining the flow.
*Then again, it bugs me when my pair partner is talking a mile a minute to the point where I can’t hear myself think. You might want to go somewhere else for pairing advice;)