Yesterday, I wanted to setup a quick feedback loop for writing some production code. But, I wasn’t entirely sure what that code should look like. Further, I hadn’t worked in the code base for several months, so I wasn’t sure how the new code would need to interact with other objects.
This is a scenario that often gets me in trouble. I end up spending too much time contemplating how things might be without the feedback of what’s possible and plausible.
What I ended up doing:
- Start a new test case for the code. I hardly know what it should do, so I ended up with
it "should do things".
- Write the kind of code I would run in a REPL to get started writing the production code. This was a service client, so I did things like setup a connection, invoke a request, and handle a response.
- Write a test assertion about what I think the side-effect or result of the production code should be. For this service client, I wanted to make sure a useful thing was returned.
- Run the test, see it fail, learn some things about the REPL-y code I wrote. Since I’m writing Ruby, my feedback rate goes way up here. Running a single test, which I can do with a keystroke in my editor, is way faster than switching over to an IRB, reloading code, and finding the right incantation to run again.
- Iterate on the code in the test case until I have something that passes. Think, type, feedback. Observe, orient, decide, act. Coding is fun.
- Once I have passing code, start rearranging so its closer to what I want as actual production code. Replace dummy values with real ones. Figure out how to remove any cheats I may have put in place for expediency.
- Once I know what the messages are, extract them into helper methods on the test case class. Now I’m starting to do actual design. After each change, I run the tests. If I break the test, I go back and fix it before I proceed. This is Legitimate Refactoring. Feels good.
- Once I know what the objects are, I go ahead and create their file in the production code, move the code out of the test and into the production code, and make sure the original test still passes. Now I switch back to a more typical test-driven design cycle; write a test case, watch it fail, make it pass, refactor, repeat.
This is sort of like the Saff Squeeze, but for exploratory coding rather than defect isolation. It kept me from spinning my wheels on design and organization before I even knew what the forces are that affect said design and organization. It also makes a new, strange, or messy code base less intimidating; I can incrementally integrate with the existing code as I’m ready to dive further into it.
Give this a try some time. It’s a great confidence booster.