Clean Code

Clean Code

Clean Code is an amazing book that should be read by every ambitious Software Engineer.

Buy the book "Clean Code"

The Total Cost of Owning a Mess

If you have been a programmer for more than two or three years, you have probably been significantly slowed down by someone else’s messy code. If you have been a programmer for longer than two or three years, you have probably been slowed down by messy code. The degree of the slowdown can be significant. Over the span of a year or two, teams that were moving very fast at the beginning of a project can find themselves moving at a snail’s pace. Every change they make to the code breaks two or three other parts of the code. No change is trivial. Every addition or modification to the system requires that the tangles, twists, and knots be “understood” so that more tangles, twists, and knots can be added. Over time the mess becomes so big and so deep and so tall, they can not clean it up. There is no way at all.

As the mess builds, the productivity of the team continues to decrease, asymptotically approaching zero. As productivity decreases, management does the only thing they can; they add more staff to the project in hopes of increasing productivity. But that new staff is not versed in the design of the system. They don’t know the difference between a change that matches the design intent and a change that thwarts the design intent. Furthermore, they, and everyone else on the team, are under horrific pressure to increase productivity. So they all make more and more messes, driving the productivity ever further toward zero.

The Grand Redesign in the Sky

Eventually the team rebels. They inform management that they cannot continue to develop in this odious code base. They demand a redesign. Management does not want to expend the resources on a whole new redesign of the project, but they cannot deny that productivity is terrible. Eventually they bend to the demands of the developers and authorize the grand redesign in the sky.

A new tiger team is selected. Everyone wants to be on this team because it’s a green- field project. They get to start over and create something truly beautiful. But only the best and brightest are chosen for the tiger team. Everyone else must continue to maintain the current system.

Now the two teams are in a race. The tiger team must build a new system that does everything that the old system does. Not only that, they have to keep up with the changes that are continuously being made to the old system. Management will not replace the old system until the new system can do everything that the old system does. This race can go on for a very long time. I’ve seen it take 10 years. And by the time it’s done, the original members of the tiger team are long gone, and the current members are demanding that the new system be redesigned because it’s such a mess.

The Art of Clean Code?

It’s no good trying to write clean code if you don’t know what it means for code to be clean!

Meaningful Names

Use Intention-Revealing Names

It is easy to say that names should reveal intent. What we want to impress upon you is that we are serious about this. Choosing good names takes time but saves more than it takes. So take care with your names and change them when you find better ones. Everyone who reads your code (including you) will be happier if you do.

The name of a variable, function, or class, should answer all the big questions. It should tell you why it exists, what it does, and how it is used. If a name requires a com- ment, then the name does not reveal its intent.

int elapsedTimeInDays;

int daysSinceCreation;

int daysSinceModification;

int fileAgeInDays;

Choosing names that reveal intent can make it much easier to understand and change code.

Avoid Disinformation

Do not refer to a grouping of accounts as an accountList unless it’s actually a List.

The word list means something specific to programmers. If the container holding the accounts is not actually a List, it may lead to false conclusions.

So accountGroup or bunchOfAccounts or just plain accounts would be better.

Use Pronounceable Names

Number-series naming (a1, a2, .. aN) is the opposite of intentional naming. Such names are not dis-informative—they are non-informative; they provide no clue to the author’s intention. Consider:

public static void copyChars(char a1[], char a2[]) {
  for (int i = 0; i < a1.length; i++) {
    a2[i] = a1[i];
  }
}

This function reads much better when source and destination are used for the argument names.

Noise words are another meaningless distinction. Imagine that you have a Product class. If you have another called ProductInfo or ProductData, you have made the names different without making them mean anything different.

Info and Data are indistinct noise words like a, an, and the.

Once again compare

for (int j=0; j<34; j++) {

  s += (t[j]*4)/5;
}

to

int realDaysPerIdealDay = 4;

const int WORK_DAYS_PER_WEEK = 5;

int sum = 0;

 

for (int j=0; j < NUMBER_OF_TASKS; j++) {

  int realTaskDays = taskEstimate[j] * realDaysPerIdealDay; int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);

  sum += realTaskWeeks;

}

Note that sum, above, is not a particularly useful name but at least is searchable. The intentionally named code makes for a longer function, but consider how much easier it will be to find WORK_DAYS_PER_WEEK than to find all the places where 5 was used and filter the list down to just the instances with the intended meaning.

Coding with Clarity

Rest of this article is taken from: https://alistapart.com/article/coding-with-clarity. Visit the site and support the author.

The single responsibility principle

The single responsibility principle states that a block of code should do one thing, and do it well. Like the drill above, limiting its functionality actually increases the usefulness of a block of code. Coding this way not only saves you a lot of headache, but it will save future developers on the project a lot of headache as well.

So when facing the problem this time, I broke everything down into single-responsibility functions wrapped up in a module object instead. The resulting function called upon form submission looked like this:

return {
    processForm: function() {
        getScores();
        calculatePercentages();
        createCharts();
        showResults();
    }
};

Command-query separation

Command-query separation provides a basis of safeguarding your code against unintended side effects to avoid surprises when functions are called. Functions fall into one of two categories: commands, which perform an action, and queries, which answer a question. You should not mix them. Consider the following function:

function getFirstName() {
    var firstName = document.querySelector("#firstName").value;
    firstName = firstName.toLowerCase();
    setCookie("firstName", firstName);
    if (firstName === null) {
        return "";
    }
    return firstName;
}

var activeFirstName = getFirstName();

This is a simplistic example—most side effects are harder to find—but you can see some potentially unanticipated side effects in action.

The function name, getFirstName, tells us that the function is going to return the first name. But the first thing it does is convert the name to lowercase. The name says it’s getting something (a query), but it’s also changing the state of the data (a command)—a side effect that is not clear from the function name.

Worse, the function then sets a cookie for the first name without telling us, potentially overwriting something we could have been counting on. A query function should never, ever overwrite data.

A good rule of thumb is that if your function answers a question, it should return a value and not alter the state of the data. Conversely, if your function does something, it should alter the state of the data and not return a value. For maximum clarity, a function should never return a value and alter the state of the data.

A better version of the code above would be:

function getFirstName() {
    var firstName = document.querySelector("#firstName").value;
    if (firstName === null) {
        return "";
    }
    return firstName;
}

setCookie("firstName", getFirstName().toLowerCase());

This is a basic example, but hopefully you can see how this separation can clarify intent and prevent errors. As functions and code bases become larger, separation becomes much more important, as hunting for the function definition whenever you want to use it just to find out what it does is not an efficient use of anybody’s time.

Loose coupling

Coupling is a measure of how much one program unit relies on others. Too much coupling (or tight coupling) is rigid and should be avoided. That’s the jigsaw puzzle. We want our code to be flexible, like Lego blocks. That’s loose coupling, and it generally results in much greater clarity.

To view or add a comment, sign in

More articles by Timur C.

Others also viewed

Explore content categories