Stop & Think - The Art of Thoughtful Programming
My current programming project is proceeding rather slowly, due to lack of time. Most days, I get literally a few minutes in a coffee break actually at a terminal. But there's always plenty of thinking time--on the walk to work, in the shower, or while washing up. Rarely with pen & paper to hand, unfortunately. I can't say that this is my favourite way to program, but it does help correct my tendency to spend too long tapping the keyboard, with insufficient cogitation.
Old-timers sometimes praise the batch-job method of working: submit your punched cards one day, and collect the output the next. Good for thoughtful programming, I'm sure, but I don't think I could stand it.
I think Haskell--my favourite programming language--encourages more thoughtful programming: first by lifting you out of the trees so you can see the wood; and secondly by catching so many errors at compile-time. (In some other article, one of these days, I'll try to describe the experience of programming with Haskell's type system. For now, I just have to urge you to try it.)
In any case, this project is in C, where "random" tinkering with the code is seductive, if seldom fruitful. Last week, the project reached quite a milestone: the proof-of-concept implementation actually worked well enough to prove the concept! This milestone was only achieved by a deliberate "stop & think" policy. I'd been tinkering for too long with a version that worked well enough on trivial test cases, but fell over with my simplest nearly-real-world example. In fact, the problem requires building a tree and walking it. I'd been trying to get away with a list... "just need to get it in the right order". Once I was barking up the right tree, the proof-of-concept was completed over a single coffee.
So where next? Now there's so much tinkering to be done which will be useful. I should implement that final part of the concept where the algorithm seems sound enough in my head, but is not yet instantiated in code. Or should I make that task easier by cleaning up the data structures first? How about implementing some other features, that are not controversial, but may pull the implementation in a slightly different direction? Or replacing the quick hacks with production code--for example, fixed size arrays with properly memory-managed dynamic structures?
Fortunately, life imposed some extra thinking time on me to choose the next direction. Naturally, proof-of-concept code is not expected even to be built by anyone but me. Running each simple test involves editing the source to twiddle a #ifdef or two, typing make (there is a Makefile, although it's not completely competent), then invoking the program with several different command-line arguments. And considering this, it became clear that the very next direction had to be a test harness. Another coffee break hacking session produced a simple shell script that runs through all my current test cases in... let me see... 3 seconds. That harness will itself need to be expanded, improved (and probably ultimately replaced) as the project progresses and interfaces change. But it's there now, and it will start saving me time and frustration immediately.
It's been said before, but every significant project needs an automated test suite. And the best time to have it is, well, as soon as there's anything to test! Dijkstra famously said that testing can only demonstrate the presence of bugs, and not their absence. I understand his point (and while I haven't touched formal program development methods since college, I do exploit tools like assert() and gcc -W -Wall which can help the quest for correctness from the direction opposite to testing). But of course testing does give us increased confidence--not certainty--that a program is doing what it should. If we are fastidious about regression tests, then testing can demonstrate that all previously known bugs are absent. That's well worth having!
So--test harness in place--where next? Data structure clean up... complete that last tricky corner... more features... code review & polishing...?
Let me stop & think!