Coding Style & Design Guide

Keep it simple, stupid (and short)

This page is a collection of guidelines, advices, notes, tips and recommendations that I found over the years, in books and on the internet; they are not hard and fast rules or laws set in stone!

Some items are (re)written by be, some are paraphrased and many are just copy-&-pasted (forgive me if the attribution is missing; this was originally just a text file where text snippets got dumped into).

The entries are primarily meant to be a reference for me, by me – but maybe someone else may also find a few interesting or thought-provoking items here…

Revision history: 2006-2007 (Creation and growth); 2010-04; 2021-05.

General Remarks


On Design

Follow the principle of KISS: Keep it simple & stupid/short/sufficient!

Other mnemonics on design and simplicity:

Good source code should aim to be (perfection being the ultimate goal):

Encapsulation – A component should know nothing about its environment that it cannot deduce from formal interfaces which are independent of any one particular incarnation of that environment.
An object should only know about itself (other information could be accuired by a query).
An object should only know how to fullfill/solve its own task/problem.

When you’re writing code that’s going into a system library, make darned sure that it’s written to be as performant as humanly possible, because you never know if someone’s going to find your one-off piece of code and put it in their innermost loop. (Larry Osterman, 2005)

The open closed principle of object oriented design states: Software entities like classes, modules and functions should be open for extension but closed for modifications.

The Liskov Substitution Principle of object oriented design states: In class hierarchies, it should be possible to treat a specialized object as if it were a base class object.

Seperate the logic from presentation, manipulation. Follow the paradigm of MVC (split between model, view, and controller).


On Comments/Documentation

Changelog entries and commit messages


On Naming

General naming conventions:

Type Description
Classes Start with a capital letter, divide into parts starting with a capital letter again, e.g. VideoDataBuffer
Member variables and parameters Lower case letters, parts may be divided by underscore, e.g. next_edge, colorspace.
Member functions First part being a verb, starting with a lower case letter, then divided into parts starting with capital, e.g. getVideoDataBuffer()
Event Handling Examples: onMouseButtonL, atMaxValue(), onMaxValue()
Macros and Constants All capital, parts divided by underscore, e.g. CLAMP, DEFAULT_SIZE.
Enums Like classes, e.g. QueueMode; no hard naming rules for the entries

Notes


On Building and Testing

Use at least two different compilers! Some problems are just a quirk of the tool, not a flaw in your logic.

Use the maximum warning level your compiler has.
If possible set compiler warnings as errors, this ensures accidental warnings need to be fixed.
Have a daily build process that builds the entire system and if possible have some automated regression tests.

Frequently perform project builds on a clean machine with limited network access by following development environment configuration instructions, and then getting and building from source control. During this process many teams discover hard coded paths, missing dependencies, and solution defects. It’s better to detect it early than for development to grind to a halt the day before a big release when the corporate IT department decommissions that old server that supposedly wasn’t in use for the past year.

Don’t guess where your code is slow, use a profiler. Either use the one that comes with your compiler or try Numega’s TrueTime.


Appendix A: From What Makes Good Code Good?

By Paul DiLascia, taken from MSDN Magazine, July 2004 issue

When MSDN Magazine asked me to write a page on something I care about, I said, “You mean, like abolishing taxes, car phones, and SUVs?” Alas, they meant something to do with programming. Well! After pondering, I realized that something I care about is writing good code. Books and magazines spare no space explaining how to program the latest API or marshal objects from Redmond to Zwaziland, but how to write good code? What is good code, anyway?

A good program works flawlessly and has no bugs. But what internal qualities produce such perfection? It’s no mystery, we just need some occasional reminding. Whether you code in C/C++, C#, Java, Basic, Perl, COBOL, or ASM, all good programming exhibits the same time-honored qualities: simplicity, readability, modularity, layering, design, efficiency, elegance, and clarity.

Simplicity means you don’t do in ten lines what you can do in five. It means you make extra effort to be concise, but not to the point of obfuscation. It means you abhor open coding and functions that span pages. Simplicity – of organization, implementation, design – makes your code more reliable and bug free. There’s less to go wrong.

Readability means what it says: that others can read your code. Readability means you bother to write comments, to follow conventions, and pause to name your variables wisely. Like choosing “taxrate” instead of “tr”.

Modularity means your program is built like the universe. The world is made of molecules, which are made of atoms, electrons, nucleons, quarks, and (if you believe in them) strings. Likewise, good programs erect large systems from smaller ones, which are built from even smaller building blocks. You can write a text editor with three primitives: move, insert, and delete. And just as atoms combine in novel ways, software components should be reusable.

Layering means that internally, your program resembles a layer cake. The app sits on the framework sits on the OS sits on the hardware. Even within your app, you need layers, like file-document-view-frame. Higher layers call ones below, which raise events back up. (Calls go down; events go up.) Lower layers should never know what higher ones are up to. The essence of an event/callback is to provide blind upward notification. If your doc calls the frame directly, something stinks. Modules and layers are defined by APIs, which delineate their boundaries. Thus, design is critical.

Design means you take time to plan your program before you build it. Thoughts are cheaper than debugging. A good rule of thumb is to spend half your time on design. You need a functional spec (what the programs does) and an internal blueprint. APIs should be codified in writing.

Efficiency means your program is fast and economical. It doesn’t hog files, data connections, or anything else. It does what it should, but no more. It loads and departs without fuss. At the function level, you can always optimize later, during testing. But at high levels, you must plan for performance. If the design requires a million trips to the server, expect a dog.

Elegance is like beauty: hard to describe but easy to recognize. Elegance combines simplicity, efficiency, and brilliance, and produces a feeling of pride. Elegance is when you replace a procedure with a table, or realize that you can use recursion – which is almost always elegant:

int factorial(int n)
{
    return n==0 ? 1 : n * factorial(n-1);
}

Clarity is the granddaddy of good programming, the platinum quality all the others serve. Computers make it possible to create systems that are vastly more complex than physical machines. The fundamental challenge of programming is managing complexity. Simplicity, readability, modularity, layering, design, efficiency, and elegance are all time-honored ways to achieve clarity, which is the antidote to complexity.

Clarity of code. Clarity of design. Clarity of purpose. You must understand – really understand – what you’re doing at every level. Otherwise you’re lost. Bad programs are less often a failure of coding skill than of having a clear goal. That’s why design is key. It keeps you honest. If you can’t write it down, if you can’t explain it to others, you don’t really know what you’re doing.

There’s so much I’ve left out, but there’s one more thing I hesitate to add. Use it sparingly and only in desperation: the clever hack. The clever hack is when you sacrifice your principles to expedience. When you hardcode some condition or make a call up the layer cake–or commit some other embarrassment–because it works and there’s no time to do it right. But remember: it must be clever! It’s the cleverness that redeems the hack and gives it a kind of perverse elegance. And if the hack doesn’t work, don’t blame me! Happy programming!


Appendix B: From Basics of the Unix Philosophy

Taken from “The Art of Unix Programming”, Chapter 1, by Eric Steven Raymond.

Doug McIlroy: “This is the Unix philosophy: Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface.”

  1. Make each program do one thing well. To do a new job, build afresh rather than complicate old programs by adding new features.
  2. Expect the output of every program to become the input to another, as yet unknown, program. Don’t clutter output with extraneous information. Avoid stringently columnar or binary input formats. Don’t insist on interactive input.
  3. Design and build software, even operating systems, to be tried early, ideally within weeks. Don’t hesitate to throw away the clumsy parts and rebuild them.
  4. Use tools in preference to unskilled help to lighten a programming task, even if you have to detour to build the tools and expect to throw some of them out after you’ve finished using them.

Rob Pike (in “Notes on C Programming”):

  1. You can’t tell where a program is going to spend its time. Bottlenecks occur in surprising places, so don’t try to second guess and put in a speed hack until you’ve proven that’s where the bottleneck is.
  2. Measure. Don’t tune for speed until you’ve measured, and even then don’t unless one part of the code overwhelms the rest.
  3. Fancy algorithms are slow when n is small, and n is usually small. Fancy algorithms have big constants. Until you know that n is frequently going to be big, don’t get fancy. (Even if n does get big, use Rule 2 first.)
  4. Fancy algorithms are buggier than simple ones, and they’re much harder to implement. Use simple algorithms as well as simple data structures.
  5. Data dominates. If you’ve chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.[9]

Rule of…

  1. Rule of Modularity: Write simple parts connected by clean interfaces.
  2. Rule of Clarity: Clarity is better than cleverness.
  3. Rule of Composition: Design programs to be connected to other programs.
  4. Rule of Separation: Separate policy from mechanism; separate interfaces from engines.
  5. Rule of Simplicity: Design for simplicity; add complexity only where you must.
  6. Rule of Parsimony: Write a big program only when it is clear by demonstration that nothing else will do.
  7. Rule of Transparency: Design for visibility to make inspection and debugging easier.
  8. Rule of Robustness: Robustness is the child of transparency and simplicity.
  9. Rule of Representation: Fold knowledge into data so program logic can be stupid and robust.
  10. Rule of Least Surprise: In interface design, always do the least surprising thing.
  11. Rule of Silence: When a program has nothing surprising to say, it should say nothing.
  12. Rule of Repair: When you must fail, fail noisily and as soon as possible.
  13. Rule of Economy: Programmer time is expensive; conserve it in preference to machine time.
  14. Rule of Generation: Avoid hand-hacking; write programs to write programs when you can.
  15. Rule of Optimization: Prototype before polishing. Get it working before you optimize it.
  16. Rule of Diversity: Distrust all claims for “one true way”.
  17. Rule of Extensibility: Design for the future, because it will be here sooner than you think.

Appendix C: From Practice of Programming (Collected Rules)

Note: I haven’t read the book. The following summary was compiled by someone else and is floating through the net.

Several chapters of “The Practice of Programming by B.W. Kernighan and R. Pike” contain rules or guidelines that summarize a discussion and are listetd in the Appendix of that book. Kernighan and Pike warn that the points were collected for easy reference and that each was presented in a context that explains its purpose and applicability.

“Simplicity, Clarity, Generality."
– Kernighan and Pike

“Each truth that I discovered became a rule that served me afterwards in the discovery of others."
– Rene Descartes, Le Discours de la Methode

Style

Interfaces

Debugging

Testing

Performance

Portabillty


Appendix D: From The Pragmatic Programmer Quick Reference Guide

This page summarizes the tips and checklists found in the book “The Pragmatic Programmer” by Andrew Hunt and David Thomas.


END