October 18, 2007
A few hard-won lessons regarding software and coding.
This is a short list of Rules to Live By, as generated by some twenty years of experience writing code that, at one time or another, undoubtedly broke all of these rules. This is about programming in C and mostly in the kernel, so add spices as needed for taste.
Always bear in mind that it might be YOU who in five years has to come back and maintain the code you're writing. As anyone who has done this knows, if you haven't looked at a piece of code in a few years, it may as well have been written by someone else.
In general, no matter what your favorite indentation scheme might be, use the indentation scheme of the code that you're changing. Mixed indentation just makes the code more confusing and harder to read. Many, many bugs have been overlooked because the screwed-up indentation made the code look right when it in fact was broken.
Only change indentation when doing a wholesale overhaul of a file or routine. If it's your new file, use whatever scheme suits you, but if it's existing code, don't gratuitously change it. Gratuitous whitespace changes just increase the size of the changeset and make it more difficult to track the history of the code. Only change indentation when you're essentially replacing a routine or when the indentation is already screwed up and you're making it consistent while you replace most of the code.
USE COMMENTARY! C is not self-documenting. I had a disagreement not long ago with someone who claimed that well-written code doesn't need commentary. This is someone who codes almost exclusively in object-oriented languages, but even there I think that he's just wrong. Commentary is absolutely necessary. Of course, if you write commentary of the form
a++; /* Add one to a. */
then you're asking for me to come slap you. The commentary should be concise and should provide the information that the code itself does not provide. The code itself provides the "what," good commentary provides the "why." Decent commentary also enumerates the assumptions made by the person writing the code. If code has side effects, commentary should list them and point to the code affected. If there are races, the races should be described and, if it's a relatively complex race, commentary should give the solution rationale. If there are invisible assumptions behind the code (along the lines of "the value of 'x' is filled in by another thread" or "this race doesn't matter because we don't care who wins" or even "we have to do things in this particular way because our algorithm depends on doing so, for these reasons..."), COMMENT THOSE ASSUMPTIONS. This is probably the single most important kind of commentary one can write.
If you change the code and, due to your change, the commentary no longer matches what the code does, fix it! If the commentary is broken, the code is broken.
Never reinvent the wheel. If there's already a routine that does the job, use it. If there isn't, look again before writing your own. It may be that a very minor change will generalize an existing routine. The less code you write, the fewer bugs you'll introduce.
"Measure twice, cut once." No amount of after-the-fact bug fixing can compensate for insufficient design. I have found that by spending most of my time on design, the actual implementation goes amazingly quickly and the only bugs tend to be minor ones. If you find yourself constantly adding and removing code to cover cases you originally didn't handle, you didn't spend enough time on the design. If you do it right, even if you don't actually code all the cases up front, adding those cases when necessary is pretty trivial.
Don't try to write C++/FORTRAN/<your favorite language> when you're actually writing C. Use C constructs, don't try to force the shape of another language on it. One can certainly write object-oriented code in C even though the language doesn't support all the C++/Java/etc. bells and whistles. Just because something doesn't obviously look object-oriented doesn't mean that it isn't. (This is another place where good commentary comes in, btw.)
Your code will be around long after you've gone on to other things. It might even outlive you. Try to make sure that those who have to look at it after you find reason to appreciate your ability to write clean, well-commented and clear code that's easy to understand and maintain. Otherwise, you could have those people cursing you loudly and at length. Remember, that person cursing you could, in fact, be YOU, five years from now.
If you want a very, very good negative example, just look at the xfrm code in Linux 2.6. I did. I had to change it. I can only hope that the person who wrote it is condemned to maintain it.