I want to muse today about Small Steps and how they affect me. I owe GeePaw Hill a pat on his cute little head for helping me focus on them.
The idea of small steps is not new to me, nor, I’d guess, to you. Most of us are getting pretty well into continuous integration, working at HEAD, committing code frequently, and so on. But my long exposure1 to GeePaw Hill and his “rice ‘n’ garlic” advice, Take Many More Much Smaller Steps, has had a delicious effect on how I work and how I feel about it.
As a programmer, you’re probably able to envision quite a few lines of code well enough to type them all in, get them to compile, and make them work. If you’re only as good as I am—I hope you are much better—you’ll make a few typos, and sometimes even a real mistake, like a plus that should have been a minus, or an incorrect name. But, even if you do make a few mistakes, like me, you probably make the thing work quite quickly, almost every time.
If you’re like me—and I hope you are much better—once in a rare while, the code you type in just doesn’t want to work, or conflicts with something else in some strange way, and you get involved in a longer session, more focused on debugging than on coding. And, like me—or even better—you’re good at debugging too, so soon enough, the program submits to your will and works as you intended.
My most natural cycle of work these days seems to be that I program and write the contemporaneous article in chunks of ten or fifteen minutes, after which I reach a point where I can commit the code. If I go longer without committing, more than about 20 minutes, the odds are that I am stuck. Invariably stuck means that there is a bug in what I’ve set out to do, and I’ve made enough changes that I can’t see what it is, and honestly I would be better off reverting and starting over but dammit I’m so close give me a few more minutes and I’ll get this [DELETED] working.
That is never, I mean not once, a good thing. When I do get it working, I am stressed and angry and the code looks angry too. And, most times, there are print statements lying around that I’ve forgotten to remove. GRR. I feel angry just thinking about it.
If I do make the decision to revert, it seems that it invariably goes better the second time. Whatever the mistake was, it seems that I usually don’t make it the next time around. And, of course, it helps if I take a break, because there is some distance from the computer that has an idea-suppression field and once I get outside that range, it often becomes obvious to me what I should do.
The charming GeePaw Hill pretty much commits his code every time the tests run green, and he works in such a way that they run green very very frequently, it seems like every other minute.
I’ve been trying to work that way for months now, and let me tell you what happens when I manage it.
- I seem always to have working code that can readily go live;
- I seem to encounter far fewer occasions where I need to print something or debug;
- I make fewer mistakes that persist for more than a moment;
- I feel far less stress;
- I get lots of little hits of joy;
- I get my new features done faster.
Frequent readers know that I try never to give advice. I tell you what I do, show you what happens, try to explain why it happened, and leave it to you to decide what, if anything, to do about it. So I am not here to advise you to take Many More Much Smaller Steps. I would, however, bet that if you were to learn to do it, you’d experience similar benefits to those listed above.
Not Actually Easy
It’s harder to do this than I would have thought, for at least two reasons. The first one is just that after a long habit of tolerating 15 or 20 or even 30 minutes between commits, my normal workflow doesn’t remind me to commit more often.2 It’s hard enough to break the habit of not even thinking about committing, but the second reason is even worse.
My standard coding approaches all seem to break the code and then slowly bring it back to working. When I’m not working with tests, which sometimes I am forced to do3, it tends to take a fair amount of new or significantly changed code to get things working again. It takes a long time to get there. When I am working with tests, I seem often to write a fairly long one that has to work all the way through before I feel done.
And, third,4 even if my tests are green, I’m not certain that I’m on the right track, and I’m somehow afraid to commit code that might have to change or be deleted. Which is odd, when you think about it, because every line of code we ever write is likely to change or be deleted sooner or later.
The point is, working in a way that allows me to commit my code every minute or two means that my changes have to be small—of course—but they also have to leave the tests green, and I have to trust my process to bring things out OK or to allow me to flush it all if it doesn’t work out.
Things That Help
Here are a few things that help me work in tiny steps.
- Very Small Objects
- When I can break my design into very small objects, by the nature of things, I can commit them quite often. Sometimes very small objects are easy to do, but I find that I have to be careful not to be to far away from actual need, because it’s easy to write a small object that isn’t helpful. That said, I love small objects anyway, so it’s worth it to me.
- Grow Objects Incrementally
- If my story is going to require some object to do five things, can I split the story (at least at my desk) so that I can make just one thing work, then another, then another? If so, I can commit after each bit. For example, the Player’s missile must die when it goes off the screen, or when it hits an invader, or when it hits the saucer. That can readily be done in three steps.
- Trust My Refactoring
- YABBUT, if you grow the object in three steps like that, you might wind up with a lot of duplicated code. Yes, I might, and I have to trust myself not to stop after step three, but to do the work necessary to bring all the code into a clean compact state. And that’s actually useful, because we can always commit after every single refactoring step. Really brings those numbers down where you want them!
- Test-Driven Development
- TDD helps me with small steps. I’ve developed the habit of writing just enough test to require a tiny bit of code. Call the constructor: write the class definition. Put in a construction parameter: write the
__init__. Call a method: code the method shell. Check the result: return a canned result. I can commit after every one of those steps, and, when I’m on my game, I do.
In so many cases, we have discovered that if something in our work is painful, we can often fix it by doing it more often. Releasing new versions is painful? Get to where we can release every day, or every hour, or any time at all, and it becomes no longer painful.
In my experience, when we shorten the time it takes to go from green back to green again, things go better. And I don’t mean shortening it from an hour to thirty minutes. I mean shortening it down and down and down, until we are committing our code every couple of minutes. I’m not all the way there yet, but the closer I get, the better I like it.
Too much rice, not enough garlic, and Many More Much Smaller Steps. That’s not my advice: I try not to give advice. But if I did, those would be right up there.
Maybe give it a try, see what you think. And let me know what happens, if you feel like it.
See you next time!
Not quite like iocane powder, where you spend years building up an immunity. It’s good NOT to be immune to Hill. ↩
Check out Kent Beck’s
Test && (Commit || Revert)notion. Scary and very interesting. ↩
I should say “When, because it seems like too much trouble to do tests, I choose not to write them”. It is quite often the beginning of a long painful mistake. ↩
This always happens. Why do I ever predict two of anything? ↩