My last post was an introduction to Robert Martin’s Clean Code. In this post, I’ll give you some examples of clean code. But it’s not meant to be a summary of Clean Code. I only want to give you an idea about what clean code looks like. I already said it in my last post and I’m going to say it again: read the book! You’ll find a lot of aspects in it I’m not even mentioning here.

Names

Names have to be meaningful. Good names make your intent obvious. Be it the name of a variable, a method, a class or a project. Let’s have a look at the naming of a variable:

int dsc;

Do you have any idea what dsc stands for? I don’t. Even Wikipedia isn’t sure. I think it’s a good idea to make the name more meaningful:

int daysSinceCreation;

Did you guess dsc was supposed to mean days since creation? Probably not. That’s why you should go for a more descriptive name - take the guessing out. The same’s true when you’re naming methods, classes or projects. Put yourself in the reader’s position.

But longer names aren’t automatically better. Unnecessary long names can have a negative effect and blur your intent. Here’s a particularly wordy example:

public class SubscriberIdList
{
    public void AddSubscriberIdToList(Guid subscriberIdToAdd)
    {
        //...
    }

    public void RemoveSubscriberIdFromList(Guid subscriberIdToRemove)
    {
        //...
    }

    public void RemoveAllSubscriberIdsFromList()
    {
        //...
    }
}

You can see the good intention of the author. But it’s too much. Most of the words are nothing but noise. A meaningful version might look something like this:

public class Subscribers
{
    public void Add(Guid id)
    {
        //...
    }

    public void Remove(Guid id)
    {
        //...
    }

    public void Clear()
    {
        //...
    }
}

No unnecessary distractions. Clean code is direct and tells you exactly what it does.

Methods

Methods should do one and only one thing. If a method does more than one thing you should split it up into two or more methods. How do you know if a method does only one thing? Well … that’s up to us developers to decide. One hint is if you can explain what the method does without using “and”.

Methods should be small. Do you write methods with more than 100 lines? That’s too long! Even 50 lines is too long. But how many lines is ok? It depends. A reader should be able to understand and reason about the entire method. So to say, the method has to fit into one’s head. There isn’t a fixed limit. But if you can keep it under 10 or 15 lines, that’s great.

Methods should operate on a consistent level of abstraction. Don’t deal with high-level concerns and low-level concerns in one method. The following method operates on an inconsistent level of abstraction:

public void CreateInvoice()
{
    LoadItems();
    CalculateNet();

    if(!IsVATExempt)
    {
        var rate = GetVATRate();
        this.sum +=
            Math.Round(sum * rate, decimals: 2, MidpointRounding.AwayFromZero);
    }

    Export();
}

It doesn’t look right. On the one hand, there are high-level abstractions like LoadItems or Export. On the other hand, there’s some very low-level code dealing with rounding stuff like rounding precision. Let’s improve the method by bringing it to a more consistent level of abstraction:

public void CreateInvoice()
{
    LoadItems();
    CalculateNet();
    AddVAT();
    Export();
}

void AddVAT()
{
    if (!IsVATExempt)
    {
        var rate = GetVATRate();
        this.sum +=
            Math.Round(sum * rate, decimals: 2, MidpointRounding.AwayFromZero);
    }
}

Now CreateInvoice looks way better. All steps are on a similar level of abstraction. The new method AddVAT deals with the low-level concerns. But there’s still room for improvement:

public void CreateInvoice()
{
    LoadItems();
    Calculate();
    Export();
}

void Calculate()
{
    CalculateNet();
    AddVAT();
}

void AddVAT()
{
    if (!IsVATExempt)
    {
        var rate = GetVATRate();
        this.sum +=
            Math.Round(sum * rate, decimals: 2, MidpointRounding.AwayFromZero);
    }
}

Now CreateInvoice truly operates on a consistent, high level of abstraction. Calculate operates one level of abstraction below CreateInvoice and AddVAT operates on the lowest level of abstraction. The example also shows how a consistent level of abstraction automatically leads to small methods that are doing only one thing.

Classes

Classes should have a single responsibility. Or as Uncle Bob puts it

… a class or module should have one, and only one, reason to change.

Like with methods, it’s not always easy to define what’s a single responsibility. Again, a hint is if you can describe what the class does without using “and”. If you have to use “and”, your class has probably more than one reason to change.

Classes should be small. Do you have to permanently scroll up and down to understand a class? Then, your class is too large. You can’t reason about a class if it doesn’t fit into your head. Sticking to the Single Responsibility Principle is a major step to reducing the size of your classes.

We’ll talk about classes in more detail in another post.

Comments

The most important quote about comments is

Comment do not make up for bad code.

And that’s not the job of comments. As a developer, you have to express your intent in code. Everything that can be expressed in code has to be expressed in code. Comments are for those cases were code isn’t enough. Code can only express what is done, but it can’t express why something is done. If the why is of importance to the reader, adding a comment is a good idea.

But let’s have a look at some bad comments. Here’s a perfect example of an unnecessary comment:

i++;    //add one to i

That comment is nothing but noise. There’s no value to the reader. Want to have some more noise? Ok, let’s take the wordy method from before and make it worse:

/// <summary>
/// List of subscriber identifiers.
/// </summary>
public class SubscriberIdList
{
    /// <summary>
    /// Adds a subscriber identifier to the list of subscriber identifiers.
    /// </summary>
    /// <param name="subscriberIdToAdd">The identifier to add to the list of
    /// subscriber identifiers.</param>
    public void AddSubsriberIdToList(Guid subscriberIdToAdd)
    {
        //...
    }

    /// <summary>
    /// Removes a subscriber identifier from the list of subscriber identifiers.
    /// </summary>
    /// <param name="subscriberIdToRemove">The subscriber identifier to remove
    /// from the list of subscriber identifiers.</param>
    public void RemoveSubscriberIdFromList(Guid subscriberIdToRemove)
    {
        //...
    }

    /// <summary>
    /// Removes all subscriber identifiers from the list of subscriber identifiers.
    /// </summary>
    public void RemoveAllSubscriberIdsFromList()
    {
        //...
    }
}

Does anyone think those comments are valuable to the reader?

Successive Refinement

Up until now, we’ve looked at examples of good and bade code. But how do you actually write clean code? We’ve already seen how to write clean code when we were talking about methods: You start with working code and refine it into clean code. It’s hard to write clean code from scratch. Clean code isn’t obvious, it emerges once you know how to solve a problem. If you want to have clean code (or quality code in a broader sense), you have to put additional effort into your code.

The Boy Scout Rule

How much effort? As professionals, we have to manage our time responsibly. There’s a danger to the commitment to clean code. You can get lost in meaningless details. Refining your code for hours without adding real value. But that’s part of our job. It’s up to us to find the sweet spot between extra time spent now and long-term benefits.

The Boy Scout Rule might help:

Leave the campground cleaner than you found it.

It’s talking about the campground, not the entire forest. Don’t clean up code without need. We all learn and get better over time. I constantly cringe about the code I wrote a couple of years or maybe only several month ago. But that’s no excuse to spend all your time on improving old code. Do the best you can with the code you’re writing right now. Eventually, you’ll have to modify some of your old code anyway - be it to fix a bug or to add a new feature. That’s the time when you put some extra effort into. You don’t just fix the bug or cram the new feature into the existing mess - you also clean up the campground around it.

Secondly, it says cleaner. It doesn’t say you have to fix everything. Only make sure that the code is in a better condition now than when you found it.


Are you an author? Do you put your readers first? Or is it their problem if they don’t understand your code. Tell us in the comments.