The Art of Readable Code. Part 3

Let’s finish reading “The Art of Readable Code”. Last time we discussed chapters 5-7: comments, loops, and conditions. Today we’ll discuss chapters 8-13 and draw a conclusion.

Chapter 8: Break Long Expressions Into Smaller Expressions

TL;DR:

  • Use explanatory variables;
  • Break down the conditions according to De Morgan’s laws;
  • Try to find a more elegant solution.

If an expression is incomprehensible, explain it through the name of the variable. The rule of thumb when choosing a name for such an expression is to answer the question “what does it do” or “what does it represent”:

username = line.split(':')[0].strip()
if username == "root":
// ...

// Simplify long conditional constructions through conversions:
// not (a or b or c) ⇔ (not a) and (not b) and (not c)
// not (a and b and c) ⇔ (not a) or (not b) or (not c)

// Then a line like this:
if (!(file_exists && !is_protected)) ...

// ...Can be rewritten as follows:
if (!file_exists || is_protected) ...

After writing it, see if there is an easier solution. See if the function can return the result earlier. See if the problem can be solved backwards. Figure out how to solve the problem using a different data structure.

Chapter 9. Remove Unnecessary Variables

TL;DR:

  • An early exit is better than a temporary variable;
  • Small scope is better than large scope;
  • Constants are better than variables.

Temporary variables are often superfluous. If they don’t make it clear and are used once, they can be removed:

now = datetime.now()
root_message.last_view_time = now

// datetime.now() — clear without additional explanations,
// so it can be rewritten like this:
root_message.last_view_time = datetime.now()

The intermediate variable that holds the result can be replaced by an early exit from the function. The rule is “Finish the task as early as possible”:

var remove_one = function (array, value_to_remove) {
  var index_to_remove = null;
  for (var i = 0; i < array.length; i += 1) {
    if (array[i] === value_to_remove) {
      index_to_remove = i;
      break;
    }
  }

  if (index_to_remove !== null) {
    array.splice(index_to_remove, 1);
  }
};

// Can be rewritten like this:
var remove_one = function (array, value_to_remove) {
  for (var i = 0; i < array.length; i += 1) {
    if (array[i] === value_to_remove) {
      array.splice(i, 1);
      return;
    }
  }
};

Global variables clog the code and lead to conflicts. So do local variables if their scope is more than a few lines. Reduce the scope of a variable. To do this, break large functions, classes and expressions into smaller ones, use closures.

Prefer immutable data structures to mutable ones and constants to variables. The more places a variable can change, the harder it is to track that change.

Chapters 10-11. One Task at a Time

TL;DR:

  • Separate subtasks from the main task;
  • Write as little project-specific code as possible;
  • Simplify interfaces;
  • Perform one task at a time.

Check the code with the question “what is the purpose of this block, does it solve this particular task, what is it trying to solve along the way?” If there are lines that solve subtasks, put them in a separate function. Catch yourself thinking “I wish there was a helper for this” and write one. This approach makes written functions easier to test and extend their functionality.

Try to write as little project-specific code as possible. The more “generic” code there is, the more reusable it will be.

Write wrappers for awkward interfaces: for reading cookies, working with local storage, reading files etc.

Chapters 12-13. Write as Little Code as Possible

TL;DR:

  • Explain the logic in simpler terms;
  • Find out what is already implemented in the library you use;
  • Keep the code base compact.

Write down the algorithm as you would explain it to an employee. Explore the capabilities of the libraries and frameworks you are working with. Perhaps some functionality has already been implemented for you.

The larger the code base is, the harder it is to deal with what you have written: it is difficult for newcomers to enter a project, and it is hard to debug a program. So keep it compact. To do this:

  • Remove unused and obsolete code;
  • Split the project into subprojects.

Conclusion

I can recommend this book. I haven’t covered everything so try reading it yourself too. The book has many examples and explanations, it’s written in a simple way, the author is not tied to a specific language.

Previous Summary Parts

  • First part — names of variables and functions, their ambiguity, code aesthetics and simplicity;
  • Second part — comments, loops and conditions.