by Matt Slot
It's not that I expect every piece of code to be perfect -- far from it. All code is based on assumptions: assumptions about the user demographic, assumptions about the hardware and software requirements, and assumptions about whether the developer will ever use that code again. I'm always grateful when someone releases code to demonstrate an involved piece of technology, but I'd prefer to use something that I can read and understand in a single sitting.
The problem is that people who are paid to write sample code sometimes focus too much on making the sample code functional, and not enough on making it readable. Adapting code from a working product or writing sample code to do something interesting is fine, but many times things the message is drowned by the noise.
Sample code should introduce the reader to between 1 and 3 new concepts, and each concept should be described in a text document (or lengthy header comments) so that each point is explained and reinforced. Adding more concepts to a tutorial increases the likelihood of confusion, as the reader has to understand how each bit of code fits into the big picture.
Not only should code be focused, it should also be easy to follow. Break the task down into manageable subtasks, each contained in a separate function with a suitably descriptive name and an associated flow chart for clarity. More importantly, a given task or function should be implemented in one location and should not depends on bits of code or inline glue from several other files. The reader should be able to spread out the documentation, the code, and the flow chart in front of them -- and trace through the logic in all of them at the same time. More on this below.
Finally, and obviously, the code should be well commented. A comment should indicate not only what a block of code does, but why it does it. Documenting special cases, such as coding around a known bug or edge case, warn the reader of common pitfalls and describe possible solutions.
Code that is written for a product is generally written under a deadline. Of course, the same can be said for almost anything, but the point is that once the code is functional and stable, it's only revisited to add features and fix bugs. This leads to code that, while it was planned out at one point, grows and evolves into something larger. In contrast, sample code should aim to be illustrative more than functional.
Next, working code has to deal with failure at every turn. Checking result codes and failed memory allocation is a necessary chore, but tends to clutter up "pretty" sample code. Instead, I recommend adding comments to identify possible errors, then let the reader copy and paste relevant snippets and fill in his preferred method of detecting and handling errors. Perhaps the only exception to this "no error handling" rule is an obvious one: when the purpose of the sample code is to demonstrate proper error handling.
The other goblins of working code are platform-specific or language-specific extensions. Avoid undocumented or obscure functions from system APIs, in favor of well-known library routines (such as ANSI utilities) or write up simple stub routines that can be filled in easily (AllocateMemory(), WriteFile(), etc). Avoid bleeding edge language features, or those features that make it difficult to read the code.
C++ is particularly nasty in this regard, since polymorphism, templates, and overloaded operators often hide implementation details in the name of convenience or efficiency. Object-oriented programming is also a problem because it spreads design decisions across several layers, while promoting "data hiding or encapsulation." In this way, important bits of information or state are spread across several functions, or even files, so that they are easy to miss and hard to keep in context. This simply doesn't work well for sample code, where the purpose is to provide a step-by-step recipe for solving a problem.
The real problem is that people try to balance the simplicity of sample code with the stability of working code by bundling it all into a library. Such a library is easy to drop into a project and start using, with the added benefit that it can be modified and improved over time. While the public interface may be rather straightforward, making the code usable means properly handling edge cases and proprogating errors -- the very thing that litters code.
Sample code is a staple of learning to program, and this is not an attack on people who devote their time. Instead, it's a reminder that sample code doesn't have to compete feature-for-feature with a top-selling game -- just demonstrate in simple words and simple code how something is done. Solve a few problems today, then tomorrow start solving the rest of them.
Matt Slot, Bitwise Operator