Clean Code – Is my code always readable?

Did you ever have to go back to a fragment of code that you wrote a month or year ago? How did it feel? Was it easy or did you have to figure out how it worked from scratch? If you need more than just one look, there is a good chance that you are doing something wrong. And if you scratch your head and think: “What the heck was I thinking?”, you have definitely done it wrong. But what has gone wrong? Most probably the code works fine and at some point you knew it inside out. Why can’t you remember it now? Maybe your code wasn’t written clearly enough and in accordance with best coding practices? Here are a few tips on how to write easily readable code not just for yourself but also for other developers.

Example of using coding standards

Consider the following method in C#:

public string transform(List<DateTime> s)
{string d = null;
foreach (DateTime kc in s) 
{if (kc > DateTime.Now) 
{ d = d + kc + "\n"; } 
else { d = d + "Delayed\n"; }}
return d;}

At first glance you have no idea what it actually does or what it can be used for. But after short refactoring we can get:

public string GetText(List<DateTime> arrivalTimes)
{
    var stringBuilder = new StringBuilder();
    foreach (DateTime arrivalTime in arrivalTimes) 
    {
        if (arrivalTime > DateTime.Now) 
        {
            stringBuilder.AppendLine(arrivalTime.ToString());
        }
        else 
        {
            stringBuilder.AppendLine("Delayed");
        } 
    }
    return stringBuilder.ToString();
}

Or if we apply the “?:” operator, we will get:

public string GetText(List<DateTime> arrivalTimes)
{
    var stringBuilder = new StringBuilder();
    foreach (DateTime arrivalTime in arrivalTimes)
    {
        string message = arrivalTime > DateTime.Now ? arrivalTime.ToString() : "Delayed";
        stringBuilder.AppendLine(message);
    }
    return stringBuilder.ToString();
}

What has actually happened to the code? Several modifications have been made to increase its readability:

  1. The name of the method has been changed from one that really doesn’t say anything to one that is a little bit more descriptive.
  2. The way of naming the variables was changed:
    1. “kc” was changed to arrivalTime,
    2. “s” was changed to arrivalTimes,
    3. “d” was changed to stringBuilder,

It also much easier to understand what each variable is responsible for and how it was used.

  1. Parentheses have been standardized to one format.
  2. Tabs have been added to increase readability, spacing, and nesting in braces.
  3. The “?:” operator has been used to reduce the length of the code by 4 lines.
  4. The StringBuilder class was added to avoid string concatenation  (“string” + “string”). Although some may argue that creating the StringBuilder instance will slow down the method due to its memory allocation, I would like to remind everyone that string concatenation creates a lot of allocation for Garbage Collector to clean up. It is considered that ~5 string concasts are equal to creating instances of StringBuilder, so if a list consists of 5 or more elements the performance of this method will actually increase. And for larger collections this method will work several times faster.

Example of good coding practices

Let us go through another example. Consider User class (just for demonstration purposes):

public class User
{
    public bool HasConfirmedEmail { get; set; }
    public bool HasActiveAccount { get; set; }
    public bool IsLoggedIn { get; set; }
    public bool HasPremiumAccount { get; set; }
    public bool StatusAutoGenerated { get; set; }
    public DateTime LastStatusChange { get; set; }

    public bool SetStatus(string status)
    {
        // write to Data Base
        return true;
    }
}

And a method that is responsible for updating user status that has to check if all properties are in correct state:

public string UpdateStatus(string status, User user)
{
    if (user.HasActiveAccount)
    {
        if (user.HasConfirmedEmail)
        {
            if (user.IsLoggedIn)
            {
                if (user.HasPremiumAccount)
                {
                    if (!user.StatusAutoGenerated)
                    {
                        if (user.LastStatusChange < DateTime.Now.AddDays(-1))
                        {
                            if (user.SetStatus(status))
                            {
                                return "Updated";
                            }
                        }
                    }
                }
            }
        }
    }
    return "Fail";
}

Although this code is not as unclear as the first one presented in this article, it still does not follow the best clean code practices. Here is an example of how it can be done a little bit better:

const string OK = "Updated";
const string FAIL = "Fail";

public string UpdateStatus(string status, User user)
{
    if (!CanUpdateStatus(user)) return FAIL;
    if (!user.SetStatus(status)) return FAIL;
    return OK;
}

public static bool CanUpdateStatus(User user)
{
    if (!user.IsLoggedIn) return false;
    if (!user.HasActiveAccount) return false;
    if (!user.HasConfirmedEmail) return false;           
    if (!user.HasPremiumAccount) return false;
    if (user.StatusAutoGenerated) return false;
    if (!(user.LastStatusChange < DateTime.Now.AddDays(-1))) return false;
    return true;
}

The main modifications that have been made to this code to increase its readability and maintainability:

  1. A static method has been created to check if the user status can be updated. This makes the UpdateStatus method way simpler to understand at first glance. Also, the CanUpdateStatus method logic can now be reused in other parts of the system as it is public and static.
  2. All the “if” statements have been reversed to reduce nesting. Now almost all of parentheses are gone and it is much easier to read or debug through the code. Another advantage of this convention is its scalability. Imagine that the User class has now three more properties that need to be checked before updating status – 3 more “if” statement nestings would be necessary. Now you can only add three more lines in the CanUpdatedStatus method.
  3. Strings that represent messages were removed from bodies of the methods and were put in constant variables. This also helps with maintaining the code because regardless of the number of usages of the code there is only one place where you have to change the content of the message if needed. To be honest though, all the texts should be transferred to an external resources project.

Useful links

But how can you know what conventions you should use when writing a code? Where to look for all good practices? And where to find all (or most) of this knowledge?

For C# coding conventions I recommend:

http://msdn.microsoft.com/en-us/library/ff926074.aspx

Although these examples are written in C# all the same principles apply to Java. For Java coding conventions I recommend:

https://google-styleguide.googlecode.com/svn/trunk/javaguide.html

And if you are interested in writing code that is not only useful but also readable, check out this website:

http://code.tutsplus.com/tutorials/top-15-best-practices-for-writing-super-readable-code–net-8118

Conclusion

I have participated in the GET .NET conference recently. A number of speakers talked about countless benefits of writing clean and understandable code. An argument that convinced me most was a quote from Udi Dahan who said: “Write your code as if the next programmer that will work with it is psychopath serial killer that knows where you live”. Now go and refactor your code and let me know how it changed your coding experience.

Tags: , , ,

13 comments

  1. Good stuff. I agree with a lot of what you say here, but I think the use of so many negative “if” expressions in the last example can also be problematic. I think it takes a bit more cognitive effort to evaluate negative if statements than positive ones, and so I would probably have written the CanUpdateStatus method more like:

    public static bool CanUpdateStatus(User user)
    {
    return user.IsLoggedIn
    && user.HasActiveAccount
    && user.HasConfirmedEmail
    && user.HasPremiumAccount
    && !user.StatusAutoGenerated
    && user.LastStatusChange < DateTime.Now.AddDays(-1);
    }

    The short-circuiting nature of the && operator accomplishes the equivalent of your multiple "return false" statements, but without all of the code-cruft.

    1. Thank you Eric for your comment. You have raised a very valid point. In this particular case your code would indeed be more readable. My point was to demonstrate how the inversion of ‘if’ statement works – and you have taken this to the next level – aggregating multiple ‘if’ statements. Which is really good unless each ‘if’ statement would have to return different value. Another possible refactor is to sort all conditions from most to least likely to happen – this would also decrease average time needed to execute this method. I’m looking forward to your comments on my future posts.

  2. i think this is a great list of examples on keeping code clean and legible. when do you find yourself running into these refactoring exercises? are you integrating newer pieces of code, bug fixing, code review, etc? I’d also be interested what you had to say surrounding breaking larger multipurpose functions down. Maybe another topic? 😛

    1. Well, I have found myself working with this kind of code on numerous occasions. Mostly they started with client saying: “I need a small new feature in my application and I can’t reach my previous contractors…” And then one opened existing solution and started to cry… About article concerning breaking larger multipurpose functions down – great idea 🙂 I will add it to my ‘To Do’ list of publications. One spoiler though – it will definitely point out Single responsibility principle.

  3. You’re missing your DateTime specifier in the method declaration of your fixed first example and it’s bugging me, lol.
    public string GetText(List arrivalTimes)

    Also, while I agree with that use of the “?:” operator, I think having the multiple lines that are easier to set breakpoints on is better than saving the few additional lines.

    1. Thanks for pointing out the lack of DateTime specifier – edytorial error.

      About ‘?:’ operator – it always depends on the context of use. I (from my subjective point of view) use it only if two requirements are fulfilled:
      1. The line is not longer than ~120 characters – longer lines seem to be less readable at first sight.
      2. Condition and both expressions do not contain any hidden logic (complex method calls, etc.)

  4. Great post Michal. All developers need have this concepts in your bloodstream… And to complete, we have some tools to help this goal, such as StyleCop, FxCop, ReSharper or the simple command CTRL+K+D in VS…

    ps.: great quote => “Write your code as if the next programmer that will work with it is psychopath serial killer that knows where you live”

  5. Very good post, Michal. Thanks.
    I have just a minor grammatical correction: The word “concasting” should actually be “concatenating”.

  6. GetText() is not a very descriptive method name. I like GetStatus, or even better, GetArrivalStatus. Even better, create an Arrival class that has a member of a DateTime, and put the GetArrivalStatus method into that, so that the method operating on these times is encapsulated with the data, rather than having the method in a different class and operating externally on the data.

    1. Thank you for your comment Michael. I do believe that “GetText” method can be renamed for more clarity, but I also think that mixing in there “Status” word can be a bit misleading. (e.g. this method returns string datetime or “Delayed”, and string datetime is not a status).

      Another issue that you raised is context of “GetText” method. And from presented example we do not know anything about it. The aim of this article is to show how one can gain more readable code while doing some minor refactoring. If I would like to show all best coding practises this article would have to be couple times longer. It would also have to include quite a few patterns to follow all principles of SOLID code such as “Strategy”, “Dependency Injection” etc.

Comments are closed.