There once was a master programmer who wrote unstructured programs. A novice programmer, seeking to imitate him, also began to write unstructured programs. When the novice asked the master to evaluate his progress, the master criticized him for writing unstructured programs, saying, “What is appropriate for the master is not appropriate for the novice. You must understand the Tao before transcending structure.” (from The Tao of Programming)
Some time ago, I worked on a little command-line tool, when all of a sudden I stopped. Did I really just use a ‘goto’ statement?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
int main(int argc, char *argv[]) { if (argc < 3) { ARG_ERROR: cerr << "Usage: " << argv[0] << " 0|1 <iterations>" << endl; exit(1); } int mode = atoi(argv[1]); int iterations = atoi(argv[2]); if (mode < 0 || mode > 1 || iterations < 0) goto ARG_ERROR; ... } |
What surprised me most about this code was that I didn’t feel any sense of guilt; somehow, it was just natural to use a ‘goto’ in this context.
Isn’t it considered harmful to use ‘goto’ statements? Or global variables? Or public attributes instead of using accessor methods? Or to duplicate code and data structures? In general, I think it is (of course) but there are circumstances when it is perfectly OK to write such “imperfect” code: If you know what you are doing and if you sacrifice perfection for a higher-level goal. This is very similar to a gambit in chess-playing: you trade a piece for a strategic long-term advantage over your opponent.
In my case, the higher-level goal was to actually do write a little tool for my personal use that allowed me to do things automatically — things I used to do manually (and due to the pain of manual work, frequently omitted). Granted, it would not have taken much effort to put the error reporting code into a routine of its own. But I guess that if I had spent enough time thinking about all software quality aspects like readability, testability and documentation, I would have never written this otherwise useful tool. It’s always better to have a tool that saves you from manual labor but uses goto statements than no tool at all. So my use of a ‘goto’ is just one particular manifestation of my “go-as-fast-as-possible-or-it-will-never-be-done” mindset.
You could also call this style of programming “deliberate hacking”: you know that you do something suboptimal but you do it on purpose for good (sometimes noble) reasons. This style of hacking should not be confused with bad hacking, where you write bad code either unconsciously or consciously for a bad reason (like pure laziness or obscuring your code to make yourself indispensable).
It is a good idea to keep software quality gambits in the private implementation details, where as little people as possible are affected. As a C programmer, do you care about any hacks in the ‘make’ program’s source code? Most likely not! However, many ‘make’ users complain about the fact that ‘make’ is whitespace sensitive: you have to use TAB characters to indent build commands — spaces may result in obscure errors.
For sure, a software quality gambit increases technical debt, but it is important to note that you get an asset with a much higher value in return. Ideally, the value of your asset doesn’t decrease (or only slowly decreases) over time, so that the technical debt isn’t noticeable or at least doesn’t harm. I’m convinced that the decision to use TAB characters in ‘make’ was a good hack at the time, but its asset value has long become zero. One possibility would be to change ‘make’ such that it works with TABs and spaces, but is it really worth it? For instance, how confusing and annoying would it be if makefiles that use spaces (and work with the latest version of our imaginary ‘make’) result in obscure errors on systems where only older versions of ‘make’ are present? It’s probably the best to embrace this historical beauty mark as a given fact.
In general, I think we must fight perfectionism in software development to avoid losing track of our main goal: shipping working software with good enough quality. Next time you find a hack, try to understand its nature and motives. Was it a good hack or a bad hack (frequently good hacks are marked with explanatory comments, like TODO comments). Maybe you can even develop a sense for appreciating such good hacks. This is akin to “Wabi Sabi“, a Japanese art concept. Wabi Sabi is sometimes described as seeing beauty in things that are imperfect, impermanent and incomplete. Wabi Sabi art acknowledges three simple realities: nothing lasts, nothing is finished, and nothing is perfect. I think we should accept these realities for software as well.