Pearls of Wisdom: Ancient and Modern
In the ancient days, programming was a free-for-all. But as software got
bigger, older, and more complicated, some serious problems started to
arise. Certain programming principles were discovered, which, when
followed, alleviated a lot of headaches. But this all happened a
generation ago. While today's "Software Engineers" are descended from
those original "programmers", good old instincts don't always bridge the
gap. Here's a collection of useful principles, some old, and some more
- Keep the code simple. Writing code isn't a cleverness competition. Your primary audience is the people who will need to maintain this code later.
- Design to avoid special cases. Each special case doubles the headaches later on. Although writing code isn't a cleverness competition, designing for simplicity and minimal special cases can be! Show off here.
- Comment the "why" but avoid other comments. They get out of date quickly, and the compilers don't notice. Write the code so you don't need comments.
- Use meaningful names. With modern IDEs, there is no excuse to name a variable "x". Long names are okay, and if someone complains about having to type it out, encourage them to use their IDE. Adopt a standard, such as CamelCase. Names of objects and variables should be nouns, names of methods and functions should be verbs. Use scope to communicate intent.
- Prefer smaller, well-named, classes and methods. This helps organize the code, can make it easier to read the code, and can provide nice testable bites.
- Don't embed magic numbers (or magic strings) in your code. Pull them out of the deep dark basements, give them meaningful names, and put them at the top, so you can easily see what assumptions this piece of code is based on. If it isn't obvious why a value was chosen, put in a comment. That way the next guy can know what he's dealing with when he considers changing a value.
- Don't Repeat Yourself. If the same string, number, or chunk of code appears in multiple places, factor it out. There is a reasonable case for relaxing this rule when dealing with test code, for clarity of tests.
- You want low coupling and high cohesion: Minimize the number of connections between lumps, with each lump only containing stuff that pertains to that lump.
- Delete code often. Got an object not being used? Delete it, along with the tests, as soon as you can. If you don't, eventually you'll find yourself drowning; spending time maintaining a mess of code that isn't even being used anywhere. You can dig it up from the version control system later, if you really need it. Or just rewrite it; odds are that what you need later will be a bit different anyway.
- Avoid state. This means only use member variables when you need them, and prefer little static methods. The fewer states a piece of software can be in, the less likely it will be in the wrong one at any given time. Plus, this practice makes things easier to test.
- Limit everything. Going to pass an integer into some method, where the integer represents one of a set of eight values? Don't use an integer, use an enumerated type. That way you can't ever get a "9", so you don't have to test for it.
- Isolate complexity, wrap it tightly in tests, and put it out of the way, so that it doesn't spread and contaminate everything around it.
- In case of error, blow up fast and violently. Don't build the code so that it just keeps going should it get into a situation that "should never happen". Why? Because doing so will hide bugs that probably have other ramifications. Find the bugs fast, don't give them places to hide.
- Unit tests are your best friend! This is the single greatest measure of quality of a piece of software: How well is it tested. It doesn't matter if it works perfectly, if there are no tests, it's junk, because you can't confidently maintain it. Consider the many wonderful benefits of Test Driven Development.
- Don't anticipate. Just write it simple at first, and then, later on, if more features are desired, change it. Why? Because it's vastly more likely that the thing you anticipated will never materialize, so why waste the time? Besides, programming is fun, save some for later.
- Ignore performance optimization unless it really matters. Make it work, then make it work right, then, if you really need to, make it work fast. But always make it clear. Computers are fast enough now that optimizing for faster execution and smaller memory footprint is rarely needed. (This fact makes old programmers cry themselves to sleep at night; pity them.)
- Finally, it's okay to break the rules! Violating various principles of good software development is sometimes the right thing to do.