-
Get a monthly update on best practices for delivering successful software.
On the cab ride into work this morning, I read a post on the very excellent Language Log regarding a talk by Jon Bentley on "Tiny Experiments for Computing and Life". While I have never had the opportunity to hear Jon give a talk, I did find some slides from an older presentation on "Tiny Experiments for Algorithms and Life" (ppt). The slides are worth the quick read. It gave me a few ideas for more tiny experiments of my own, but it also brought me to consider ways in which I perform these kinds of experiments when working with software-- and that led me to how I refactor.
Mapping The Problem
I paired with a developer a while ago who was surprised to find that, when tackling an especially ugly piece of code, I tend to "program/test/revert" several times within a matter of minutes before finally settling on the right approach for tackling the problem. The problem was a particularly nasty one, and I'm sure you've been there before-- adding a small feature and disliking everything about the experience. You quickly dig yourself into a hole shoring up every poorly named method, duplicate snippet of code, hard-coded configuration parameter and obsolete comment you encounter along the way.
Personally, I dislike juggling a large number of changes at one time. When faced with a difficult coding problem, I start with the assumption that I will dig myself into a few holes-- therefore I try to find these holes as quickly as possible. My real intent is to map the problem at hand by performing a few coding experiments. If successful, the result will be a sequence of independent refactorings that, applied sequentially, provide a path of least resistance for my new piece of functionality.
Write Once, Revert Often
Like a "Tiny Experiment", this phase of development is less refactoring than leading up to the process of refactoring. It usually does not last long either: anything from a few minutes to an hour at most. If it takes longer than an hour, you should stop to rethink your assumptions.
Since your first attempts at mapping the problem may break a large number of tests or highlight bigger issues in the architecture, it should be very cheap and easy to revert your entire workspace at a moment's notice and start over. Because you have not changed much code or stopped to add things like comments at this point, you are not heavily invested yet. Neither you nor your pairing partner should even blink at the thought of rolling back all your changes when you start encountering issues.
I can't say enough about being able to revert your changes quickly-- navigating menus take too much time. I have a two-keystroke shortcut configured in my IDE which is quick, painless and blissfully unforgiving. To show why this is so important, imagine that you have made some progress mapping the problem, and find that the solution is actually in reach. At this point, however, you find several unrelated tests failing after you have changed the code. Do you stop and fix the tests to conform to your changes (thereby increasing your investment in this particular solution), do you circle back to double-check your changes so far were correct (still increasing your investment in this particular solution), or do you revert the changes and investigate the tests first? I would revert all changes and investigate the "seemingly" unrelated tests and how the two pieces of code (your changes and the code under test) are coupled. Without understanding the dependencies in the pre-existing code, your entire work estimate may need to be reconsidered, and that could stop your progress right there, so it's better to know that now. Moreover, you might discover that decoupling the code is a separate task that might be worked on in parallel.
And don't underestimate how important it is to be able to revert on a dime-- to stop in mid-stream and be selective about what changes to revert takes too long and sucks any fun out of the process (the same goes for commenting out or uncommenting code to get back to a safe state-- who has time for that?) Make use of those keyboard shortcuts! Don't let your pair sit idle while you right-click your way to sub-menu purgatory. I'm serious!
Testing Your Hypothesis
Each refactoring itself should be thought of as a kind of tiny experiment. Form hypotheses around questions such as:
You get the idea. While some such refactoring experiments may fail, even these failed attempts can be considered constructive if they serve to highlight some facet of the code base which previously went unnoticed, or larger architectural changes which need to be formally scheduled and worked on in future iterations.
Related posts:
Topics: Agile Development
so if you’re reverting constantly, how do you remember what you’ve changed? don’t you lose track of mini experiments, or confuse one experiment with the next, or need to compare the results of various experiments? do you use a logbook to record (or, as you would say, “invest in”) changes?
sometimes i’ll tar up just the relevant source code and their respective output files, to compare later, if i’ve forgotten which changes produced good results. rarely do i find that source control is lightweight enough to support an experimental checkin — it requires a branch and that’s too much wasted thought.
Comment by greggT, Tuesday, March 4, 2008 @ 5:41 pm
There have been times where I found myself digging a rabbit hole and forget what I’ve changed and how I got there. However, the fact that I have gone so far without checking in that I can either not remember what I’ve changed (or not be able to hold those changes in my head) suggests to me that I am lacking clarify of thought. It’s usually the act of starting over that forces me to rethink what I did. Scribbling notes or talking through it with another developer for a few minutes, but the task at hand has to be manageable enough that I can remember exactly what changes I made.
I used to keep these experiments around (for posterity, I imagine), but eventually noticed all these things were just collecting dust. Others keep code snippets around much longer, but as for me, once I feel that I have learned some lesson through the code, the “experiment” has fulfilled its purpose and can usually go away.
Comment by Ivan Moscoso, Tuesday, March 4, 2008 @ 11:40 pm