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, 2010-04, 2021-05..07.

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 and Documentation

Changelog entries and commit messages


On Naming and Formatting

Inspired amongst others by e.g. 1.

Misc. notes

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

Functions, methods, procedures

Here’s a very brief list of such verbs:

Verb Remark
get Accesses data immediately. For internal data/members, see the note on accessors below.
set Declaratively sets a variable or a member to a value. For internal data/members, see the note on mutators below.
reset Sets a variable or a member back to its initial value or state.
fetch Requests data, which takes time; e.g. establishing a connection, contacting a server, waiting for a reply, downloading data…
remove Removes something from somewhere, for example an object from a collection. The objects thembselves may continue to live, but outside of the collection.
delete Completely erases something from the realm of existence.
compose Creates new data from existing data.
handle Handles an action. Often used when naming a callback method.
on Not really a verb, but I also often use (and see elsewhere) the on* prefix for signal handlers, e.g. onClicked(…), and think it fits.

Getters/Setter vs. Accessors/mutators

Some data members should be private and not reachable from the outside, so external/direct access is disbled or at least discouraged.

Or because the only correct way to get a valid result is to combine from/calculate with multiple elements, so simply reading or writing the value of an int straight to the member may not be OK.

The proper way then is usually encapsulate the access by public getter and setter functions, like int getData(void) and void setData(int), where the noun is the name of the property that they will effect.

An alternative, which I’ve seen more often over the years, is to save the verb completely: Without parameters, it’s the accessor, and with (at least) one parameter, it’s the mutator:

int getStatus()           // Variant A: Getter
int Status()              // Variant B: Accessor

void setStatus(int value) // Variant A: Setter
void Status(int value)    // Variant B: Mutator

I haven’t actually made my mind up yet on which I prefer; both have their pros and cons:

Functions returning a boolean value

A function that returns a boolean value (i.e. true or false) should be named with a modal/auxiliary verb, which brings it into an interrogative form; i.e. it should read like a short polar question (yes-or-no-question); just imagine a question mark at the end.

A few examples:

AuxVerb Remark
is/was/will Describes the existence of a characteristic or state. Depending on the context, past or future tense versions (was, will) may be more appropriate.
isAvailable(), isValid(), wasInterrupted(), willBlock()
has/can Describes whether the current context possesses a certain attribute (value, state):
hasEnoughEnergy(), hasMembers(), canFly()
does Describes whether the current context is capable of a certain [positive] action:
doesReturn()
should Reflects a [positive] conditional statement (usually a boolean), coupled with a certain action:
shouldUpdate(), shouldContinue()
not Generally avoid inserting not between the words to define negative logic; instead express that in code: use the positive logic names prefixed with the language ! operator.

But don’t go overboard with the aspect of “phrase it like a question”:

bool has_entries();         // OK
bool are_entries_present(); // Why so verbose?

As for any function name: Save redundant components (like repeating the same stuff in a class method):\

List foo;
bool x = foo.isListEmpty(); // Hmm...
bool y = foo.isEmpty();     // OK

We could shorten that one even more so – but then we’re getting on thin ice with some words:

bool z = foo.empty()
  1. Breaks the convention (like accessor/mutator, see above) that method names should begin with an verb.
  2. Leads to ambiguity and misunderstanding: Does it answer the question Is the list empty? or does it perform the action Do empty the list! (and if it also returns a bool for ‘success’ or ‘failure’, not even the prototype will help us understand).

The terms Property and Attribute

I’ve noted in different languages and systems the varying use of the two terms property and attribute:
In common usage/speak/talks, they are mostly used synonymously, but since programming is all about details, I spent some time reading up on those words; my understanding is currently this:

First of all, this is what was on Wikipedia on 2021-05-01:

In computing, an attribute is a specification that defines a property of an object, element, or file. It may also refer to or set the specific value for a given instance of such. For clarity, attributes should more correctly be considered metadata. An attribute is frequently and generally a property of a property. However, in actual usage, the term attribute can and is often treated as equivalent to a property depending on the technology being discussed. An attribute of an object usually consists of a name and a value; of an element, a type or class name; of a file, a name and extension.

That, plus some more random samples from the internet, lead me to this table, which will hopefully be a good reminder for me…

Property Attribute
“Color” “Black”
Inherit characteristic of an entity Additional information about an entity
Belonging to a class (of an entity) Belonging to an instance of such a class
Comparision: Like a parameter Comparision: Like an argument value

Prefer the east const style

The rules for using the keywords const and volatile can sometimes a bit tricky, so for simplicity’s sake, just use the “east const” style.

As a mnemonic, remember this:

The const qualifier is applied to what’s on its left; if there is nothing of its left, then it is applied to what it is on its right. 2

That ‘if…’ clause alone is reason for me to keep it even simpler ;-)
Therefore, it’s recommended to put const always on the right side (in the “east”) of the item that should become constant.

The east const style is also consistent with the way how constant member functions are declared: with the const qualifier on the right:
int get() const;

Here are a few pro-west-const/anti-east-const arguments and why I don’t think they are valid:

Translate the following code lines as being read from right to left (confirm with the comments below):

int const n;         // n is a constant integer
int* p1;             // p1 is a (mutable) pointer to a (mutable) integer
int* const p2;       // p2 is a constant pointer to a (mutable) integer
int const* p3;       // p3 is a (mutable) pointer to a constant integer
int const* const p4; // p4 is a constant pointer to a constant integer

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 (with additions)

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

Attention: With Additions by me, in the form of {Text – Sascha}


  1. http://micro-os-plus.github.io/develop/naming-conventions/ ↩︎

  2. https://mariusbancila.ro/blog/2018/11/23/join-the-east-const-revolution/ ↩︎