Monday, September 17, 2007

Ah, Sweet Success

I had mentioned a few weeks back my struggling with a difficult programming problem. At that point, I thought I was close to success. Well, I was closer than when I started, but not by much.

Eventually however, I cracked it! Actually breaking down and consulting my one computer science textbook helped. An even bigger step towards success was realizing I had bitten off more than necessary: by reducing the scope of my problem by a bunch, I could really make life simpler.

Once I had something working, a number of different urges set in. One is to clean up the code. This can range from just clearing out all the bits-and-pieces that didn't end in the final solution, to a 'refactoring' where you redesign the whole program organization to what you would have done if the successful approach had been apparent from the start. I opted for mostly housecleaning, as the worst thing is to redesign your code back into a non-functional state!

The other two urges are to optimize & to add functionality. Optimizing for speed can be a bit of a siren's song, as you can always tweak out a little more performance. A wise (and brotherly) sage once tutored me to optimize only where necessary, and I try to stick to that. The initial implementation ran so slowly it was exasperating to troubleshoot, so on went the optimizing gloves. Luckily, there was an obvious way to cache intermediate results which went a long way. Indeed, after one set of caching I realized I could toss out some troublesome code I still didn't trust -- speed & simplicity in one package!

Adding functionality is another matter. In my case, the algorithm is satisfying a complex constraint-satisfaction problem. My main challenge is discovering all the constraints: many are more or less company lore, meaning you have to climb the mountain and present your proposed solution to one of the gurus so that they can show you the error of your ways. But, one encouraging sign that I solved the program in a good way is that in most cases the constraints fit neatly into the current framework; I really haven't had to rethink the code in a huge way to fit any in. So I can do something right.

The whole process can be humbling on so many levels. For example, there were some off-by-one errors that were quite difficult to shake out of the code -- indeed, I finally realized that one wasn't an error in the code but rather my attempt to eliminate it represented an error in my thinking. Some others took a while to get out, and one little annoyance just popped back up again.

One humbler is the fact that the approach I finally took was one I had considered and rejected earlier as too difficult to get right -- but I ultimately realized it was really much more intuitive to implement than the others, and perhaps more importantly much easier to check & interpret intermediate steps towards the solution. Looking back, I see this as the programming analog to what Derek Lowe recently commented on for medicinal chemistry: sometimes the correct solution is right in front of you, but you have to travel a long ways to discover this.

But a real ego-trimmer is to discover that your code is smarter than you are. The program actually weaseled its way into certain clever solutions that were not only good, but seemed to violate the algorithm. Indeed, some of the earlier implementations might well have explicitly forbidden this. Amazing how smart a dumb programmer's code can be!

No comments: