|home| |posts| |projects| |cv| |bookmarks| |github|

Clean Code My Guidelines

This is a list of principles that I follow to write code that is readable, maintainable and easy to understand.

Line of sight coding

Line of sight coding is a concept I first saw in a talk by Mat Ryer (here's his blog post about it).

Basically, he argues that the code is more readable when the 'happy path'(i.e. the route that will be taken when all goes well) is aligned to the line of sight(i.e. the first level of indentaion in a code block).

His tips for a good line of sight:

His talk is about Go code, but the same principles can be applied to any programming language.

For example, if you look at the Linux kernel code you can see the same principles applied (these guidelines allow them to have 8 characters wide indentation levels and at the same time don't have long lines).

Compare this C++ pseudocode:

std::string do_some_work() {
    std::string toReturn = "";
    if(successCondition) {
        if(anotherSuccessCondition) {
            toReturn = "success";
        } else {
            toReturn = "error";
        }
    } else {
        toReturn = "error";
    }
    return toReturn;
}

To this:

std::string do_some_work() {
    if (!successCondition) {
        return "error";
    }
    if (!anotherSuccessCondition) {
        return "error";
    }
    return "success";
}

Which is easier to read and understand and maintain?

Rob Pike's rules of programming

Quote from here:

Rule 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.

Rule 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.

Rule 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.) For example, binary trees are always faster than splay trees for workaday problems.

Rule 4. Fancy algorithms are buggier than simple ones, and they're much harder to implement. Use simple algorithms as well as simple data structures.

The following data structures are a complete list for almost all practical programs:

Of course, you must also be prepared to collect these into compound data structures. For instance, a symbol table might be implemented as a hash table containing linked lists of arrays of characters.

Rule 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. (See Brooks p. 102.)

Rule 6. There is no Rule 6.

Unix philosophy

Especially this rule:

This applies not just to programs but also to: functions, classes, files, library/package;

Naming things

You should choose the name of things so that the code reads like prose.

Don't add useless information in the name

The name of something(class, struct, variable, function etc.) should express what that thing does and nothing else.

Things like these are pointless in my opinion:

class ISomeClassName {
// some code here
};

class SomeClassNameIface {
// some code here
};

class SomeClassNameIntf {
// some code here
};

struct SomeStruct_s {
};

struct SomeEnum_e {
};

struct E_SomeEnum {
};

bool bVar = false;
uint64_t u64Var;
int* pVar = nullptr;

There's no need for this, it doesn't make the code more readable(in fact it makes it harder to read and even harder to maintain, think what will have to happen if the type will change). Besides, all these things are a click(or keypress) away from being known.

Variables

Some think that the longer the variable name is, the more readable the code is. I disagree. My view is that the variable names need to increase in length with the size of the scope where they are declared.

For example, this loop:

colors := []string{"red", "green", "blue"}
for(i := 0; i < len(colors); i++)
    print(colors[i]);

is more readable than this one:

colors := []string{"red", "green", "blue"}
for(colorIndex := 0; colorIndex < len(colors); colorIndex++)
    print(colors[colorIndex]);

Everyone understands that i is the index into that array.

But, let's say wee need a name for a global variable that holds the current value of a counter. If we choose to name it curr, that's not a very good name for a global variable(it would be acceptable in a smaller scope). Probably something like counterMaxVal would be better.

Functions/procedures

What they do should match their name. Don't name a function printValues if inside it you don't print the values.

Also, again, follow the word of Rob Pike:

Procedure names should reflect what they do; function names should reflect what they return.

Classes/structs/packages etc.

The name should be appropriate to the operation(s) done by the thing.