Design your code for readability

So, how do you usually code?

You have a task: add this thing to that place. First thing you do – you find where “that place” is and how to get “this thing”. To do it, you read the code.
Reading happens every time you need to do something, doesn’t it? If something doesn’t work, or you need to optimize it, or add one more tiny thing, you search for the place in the code and read it again. And again.
Developers read the code much more often than write [1]. Even if you’re writing a completely new thing from scratch, you still return to relatively “older” parts of the code to modify them. While in the beginning you remember everything about your code, after some time you start to forget. You start to read more and more before modifying a piece of code. Then another developer comes to help you, or to do their own thing, and they also read your code.
In a big company, thousands of developers will read your code and modify it over the time.
‍‍‍‍‍‍
This is why writing readable code is sooo immensely important. I can’t stress it enough.
Personally, I think it’s the first thing young developers should be taught. It seems kind of obvious, but in practice not everyone takes care to write readable code.
“Readability” is a very wide concept. Let me list just a couple of things I include there myself.
Funny image: what-the-f***/sec as a metric of code quality

AVOIDING CLEVER TRICKS AND OBSCURE FEATURES

You’ve just read in a Perl book that a function will return the result of the last statement, if an explicit return statement missing.
Brilliant!
It saves you time typing the return and thus increases your velocity. You can use it every time now!
And you write this.
sub do_something_important {
  # ...
  if ($condition) {
    @array = some_function(@array);
  } else {
     another_function(@array);
  }
}

Now, your new colleague is reading this code. They have 10 years of experience working with Java and C++, but not so much with Perl. They might well think this function doesn’t return anything, right? In Java you have to return the value explicitly. Or even if they know Perl returns the last value, they’ll think that if no one cared to actually type the `return`, it’s not important, and the return value is not assigned anywhere.

So, your colleague adds a statement in the end of the function. And suddenly the page stops loading.
Your colleague spends some time debugging and googling, but eventually understands what’s the problem.
Hmm, thinks the colleague, I’ll just add a return after my statement. But what should I return?
The body of if is pretty clear, it returns @array. But what is being returned in the else block? They go to another_function, but it also doesn’t have any explicit return statement in it… Can it be a number? A string? Is it @array or some other array?

Not typing the return has saved you twenty seconds. But how much more time will your colleague spend?

Spoiler: I’ve been this unfortunate colleague, and I spent quite a lot.

I have more examples that just blow my mind when trying to read them.
unless (a1 && b1) {
  if (!a2 || b2) {
     # ...
  }
}
Or this one. It’s just black Perl magic, isn’t it?
$hash_ref->{"some_name_$hash_ref->{some_other_name}"} = 1;

USING CONSTANTS

if (value == 605 || value == 606 || value == 8000) {
  // ...
}
What do these numbers mean? What makes them so special among all other integer numbers in the world, all the way from -2147483648 to 2147483647 and beyond?
Why only those three? If I add a new item to the main list, should I add it to that list?
If I should have, but I didn’t stumble by pure chance upon this list, how am I supposed to find it?
Of course, the problem with this code goes even deeper. If I didn’t add a new number in there, does it mean we display inconsistent information in our application? Do we harm customer’s experience?

MEANINGFUL NAMING

Names should be descriptive. They shouldn’t be shortened for the sake of less typing. Again, you read more than you write, so in the end you’ll save time making them readable. ProcessWordMatrix is better than Run. SetEmptyToFullRatio is better than SetRatio.

WRITING USEFUL COMMENTS

This goes along with the naming. A name can be only so long, but it’s very important to explain to the reader why you wrote that piece of code.

Some people make arguments that comments become irrelevant quickly, so it’s better not to have them. But you should treat comments as part of the code, they must be as up-to-date and as good as the code itself. Comments must help the reader and not confuse them. If you change the code, don’t forger to change the comment!

Comments are not for saying “sorry, I know this piece of code is bad”. They are for saying “we should display this message because of legal reasons”, or “this is the base class for font”.

SHORT FUNCTIONS, SMALL MODULES

In my experience I saw files as big as 40000 lines. Forty thousand. I think I’ll refrain from pasting examples in here :).
40000 lines is approximately the length of The Lord Of The Rings book. You can’t possibly read and understand a piece of code of this length. After all, you’re just a human.
Dividing the code into short pieces helps you to navigate through the code, to understand what each and every part of the module does, and to easily add new things.
An obvious advice: every function should have a clear purpose reflected in the name. You should be able to scroll quickly to see the name and the description. And by that I mean: no 2000-line functions. My tunnel syndrome doesn’t allow me to scroll that long.
Same goes for every module, class, package, any other code unit.

CLEANING UP UNUSED CODE

There’s this function in the code that seems to do something useful. It’s been called, calculates something, and the result is assigned to a variable. But the variable is never used. Or is it? Does this function have some side effects you don’t know about, and thus must be called? Or did someone just forget about it, and now it’s like Flying Dutchman, haunts the endless waters of code without purpose? People spend time to read it, occasionally to modify and support it, but it’s already dead inside.
Sad story, I know.

Conclusion

What will happen if you don’t care whether your code is readable? Well, the obvious short-term problem is that someone will spend a bit more time reading your code. Seems like not a big deal, right? But if they don’t understand your code completely, because it’s vague and tricky, they might implement their feature with a bug. They might break your code. They will waste a whole evening debugging.
All this is especially important in a big company. It has people with various experience, like 10 years in Java, or 20 years in C++, or straight from the university with no previous coding experience. We need them to onboard fast, to start coding right away and to bring value. And, of course, before changing the code, they need to read it. If the code is not readable enough, they will spend much, much more time adding a new feature, than if it was more readable. And for the company, this means money.
Next time, when you’re done coding, but before you push it: just stop for a moment and look at your code. Put yourself into another person’s shoes. Is it readable? Can a junior developer understand it? Will you be able to understand it in a year? If not, then make it better and try again!
Of course, this will take you some time. But it will take much more time for other developers to read it afterwards. You’re borrowing it from them. And it’s not fair. ‍‍‍‍‍‍ ‍‍
[1] “The ratio of time spent reading versus writing is well over 10 to 1”, says Robert C. Martin in the Clean Code book. I tend to agree, while I don’t have precise numbers.

3 thoughts on “Design your code for readability”

  1. Hey Elena,

    thanks for this great post on the importance of readable code. I do believe that way too few programmer spend enough time and thought on making their code readable. According to Peter Hilton it was Phillip Relf who said that ‘it takes a professional software engineer a good decade to realise that good identifier naming is important.’ That’s a decade of writing code that’s probably hard to read.
    I hope that posts like this will change that!

    As a Rubyist I do think, however, that the return statement example is not a good one. In Ruby the return statement is usually implicit and you only make it explicit in early returns.
    So this may be due to hour different backgrounds – and I do remember putting return statements into my Ruby code everywhere when I started to learn Ruby, having learned C and Java before that – but I think that in a great majority of cases a caller should and does care about the return value of a method. The if anything the reverse is true: We should signify when the caller should not care about the return value. E.g., in Ruby I’d explicitly(!) return nil (the null value in Ruby) or self (the reference to the Object instance that received the message).

    There is definitely a trade-off going on. The implicitness of the return reduces noise in the code (and I’d argue makes it more readable). But at the cost of people having to know that the return is implicit. In this case I think it is a good trade. There is another case in Ruby where things are implicit, while Python for example makes it explicit: the receiver.
    In python (I don’t actually know python so this may not compile..) you define methods like this:
    def full_name(self):
    self.first_name + self.last_name

    in Ruby like this:
    def full_name
    self.first_name + self.last_name
    end

    Self is implicit in Ruby since it is obvious in 99.999% of the cases who self is. BUT that 0.001%? They can really surprise you. They usually co-occur with meta programming.

    Anyway my point is, that this is probably a case of implicit vs. explicit which is a trade off one has to carefully examine. But in case the language has made that decision for you, I’d argue to stick to their decision.

    Again, thank you for this great post!
    Kai

    1. Hi Kai,
      Thank you for reading my post! Nice to know you liked it.
      And thanks for such a detailed comment.

      I don’t know Ruby, so I really can’t say if the code in Ruby becomes more or less readable without return statements. Maybe in Ruby it’s obvious what’s being return and it doesn’t hurt the readability, and in this case its fine.

      I wouldn’t say return statements are “noise” though. They always help when reading the code. But again, maybe in Ruby it’s different.

      In general, if you don’t see the return statement, even if you know it’s implicit, you have to “calculate” the return value and type in your mind. So it’s not only the cost of knowing the language, it’s the cost of spending a minute running a compiler in your head. And it’s a costly operation. In a method more than one line length, it’s really hard.

      One last point: you see that code example with one return, but not the other. Can you understand what’s being returned? Do you understand that if you add a debug print in the end, the code stops working? I’ve recently seen another colleague, much more experienced in Perl than me, stumbling at this problem. In that case, it even was him writing the code a month ago.

      Thank you again for this discussion!

Comments are closed.