We've all done it: snagged a cookie when mom wasn't looking, gone around Deadman's Curve a bit too fast. We've even let the car sit in a parking spot after the meter expires. Yes, we've all violated any number of the cardinal rules of programming, the ones that everyone agrees are bad. And we secretly liked it.
We've thumbed our nose at the rules of good programming, typed out code that is totally bad -- and we've lived. There were no lightning bolts from the programming gods. Our desktops didn’t explode. In fact, our code compiled and shipped, and the customers seemed happy enough.
That’s because bad programming isn't in the same league as, say, licking an electric fence or pulling the tail of a tiger. Most of the time, it works out. The rules are more often guidelines or stylistic suggestions, not hard-and-fast rules that must be obeyed or code death will follow. Sure, your code might be ridiculed, possibly even publicly, but the fact that you’re bucking conventions adds a little bit of the thrill to subverting, even inadvertently, what amounts more often than not to the social mores of pleasant code.
To make matters more complex, sometimes it's better to break the rules. (Shhhh!) The code comes out cleaner. It may even be faster and simpler. The rules are usually a bit too broad, and an artful programmer can improve the code by breaking them. Don’t tell your boss, but sometimes it makes sense to code your own way.
What follows is a list of nine rules that some may consider unimpeachable, but many of us break often, with both success and pleasure.
Programming habit No. 1: Using
The prohibition on using
goto dates to the era before many of the tools of structured programming even existed. If programmers wanted to create a loop or jump to another routine, they would need to type
GOTO followed by a line number. After a few years, compiler teams let programmers use a string label instead of a line number. That was considered a hot new feature back then.
Some called the result “spaghetti code.” It was impossible for anyone to read your code later and follow the path of execution. It was a jumble of threads, forever tangled. Edsger Dijkstra banned the command with a manuscript drolly titled "Goto Statement Considered Harmful."
But absolute branching isn't the problem. It's the tangle that results. Often an artful
return will offer a very clean statement about what the code is doing at that spot. Sometimes adding
goto to a case statement will produce something that's simpler to understand than a more properly structured list of cascading if-then-else blocks.
There are counterexamples. The "goto fail" security hole in Apple's SSL stack is one of the best instances. But if we're careful to avoid some of the gnarly issues of case statements and loops, we can insert good, absolute jumps that make it easier for the reader to understand what's going on. We can put in a
break or a
return that is cleaner and more pleasing for everyone -- except perhaps the
Programming habit No. 2: Eschewing documentation
One friend of mine worked for a hard-nosed boss who never wrote any code but understood just enough to know that every function must include documentation. If the programmers didn’t include a comment, they must be punished. So my friend wired together an Eliza-like AI to his editor, and voilà, every function had a few lines of "documentation." The boss wasn't smart enough to understand that the lines meant nothing, so my friend was off the hook. His code was officially documented. I think he even got a promotion!
Many functions and even some classes are more or less self-documenting. Functions with names like
deleteAll don't need another line or three to explain what's going on. Choosing the right names for the function is often good enough. In fact it’s better than writing long documentation because the function names appear in other places in the code. The documentation is in only one place. Self-documenting function names improve every file where they appear.
There are cases when it's worse to have documentation. When the code is rapidly changing and the team is refactoring like crazy, documentation can diverge. The code says one thing, but the documentation is explaining what happened four or five revisions ago. This often happens at the top of the code where someone wrote a nice summary of what's supposed to happen. The refactoring team may be careful enough to fix the comments on the functions they change, but they may not even see the comments at the top of the file.
When the code and the text diverge, the comments become worthless and sometimes even dangerous. In cases like these, good, self-documenting code is better without comments.
Programming habit No. 3: Jamming too much code on one line
One boss along my path to nirvana sent out a nasty email to the team. Suddenly, all of us must rewrite our code to follow very strict rules of style. The most dramatic requirement: Each action or step or clause must be on its own line. You couldn’t chain function calls together with dot syntax. You couldn’t have two or more clauses to the boolean in a branch statement. If you define a variable, put it on its own line. If you're doing a complex calculation, don't use parentheses. Put each fragment on its own line.
He had a point. His edict would make debugging easier. As you stepped through the code, the debugger would step from action to action. It wouldn’t get stuck on one line. It was easier to follow.
But boy did the code get long. The Return key on my keyboard wore out as I kept inserting lines. And I’m sure he bragged about how many lines of code his team was writing.
Alas, sometimes it makes it easier to declare a bunch of variables in one line. Sometimes it’s simpler to put all of the boolean clauses together -- everything is more compact. That means we see more logic on the screen without scrolling. It's easier to read, which means understanding comes faster. It’s that simple.
Programming habit No. 4: Not declaring types
The folks who love typed languages have a point. We write better, more bug-free code when we add clear declarations of the data type of each variable. Pausing a moment to spell out the type helps the compiler flag stupid errors before the code starts to run. It may be a pain, but it helps. It’s a belts-and-suspenders approach to programming that stops bugs.
Times have changed. Many of the newer compilers are smart enough to infer the type by looking at the code. They can work backward and forward through the code until they can be sure that the variable must be a
string or an
int or something else. And if these inferred types don't line up, then they can raise an error flag too. They don’t need us to type the variables any more.
This means it's now easier to save a few bits by leaving off some of the simplest declarations. The code becomes a bit cleaner, and the reader is usually quite able to guess that the variable named
i in a for loop is an integer.