loading...

What makes for readable code?

ben profile image Ben Halpern ・1 min read

What are the qualities of readable code, and what helps one create readable code?

Discussion

markdown guide
 

My tips:

  • Consistent formatting.
    • Helps in scanning files if everything is "as it should be", exact styles aren't as significant as some make them out to be, but consistency is.
  • Naming things well.
    • Some developers have trouble with this, but I like to think of it in a simplistic way. If someone came to you and asked "what does this do?", rephrase the response into a name. If every variable is a basic noun, things can get muddled and there is extra overhead to understand the code when repeat names that are basic continue to show up. Being slightly more descriptive makes a huge difference.
  • Comments that are complete sentences (subject/predicate) that do not only restate the code.
    • I don't say that to test English, but I have seen a lot of comments that are very vague or cryptic. They might be a sentence fragment or phrased as a question without a question mark, so it can be confusing as to what the meaning is.
  • Avoiding brevity and making code for humans to read.
    • Abbreviations and acronyms might make sense when writing it, but it might not mean anything to others or yourself in the future.
 

Formatting, style rules and code documentation aside, I try to get rid of anything that adds cognitive load, no matter how tiny that load is. Braces, nested structures ifs and elses and other control structures can add to that. I try to get my logic as "flat" (indented on a single level) as possible.

If there is a bunch of ifs and elses in a method, I refactor them out into smaller private methods with self-describing names. If those are still too big I repeat the process until I have small methods that pretty much do just one thing each.

I try to keep methods brief. They should not span over the height of one editor page.

I try to keep classes small. I guess if a class grows beyond 500 to 600ish lines it's probably time for a split.

If an if-condition is complex then I split the condition out in a separate method with a self-describing name.

I often do that even with simple if-conditions, because sometime's it's not clear why we do:

if (sku.endsWith("X")

but the following is much clearer and doesn't require asking any questions:

if (isRetiredProductSku(sku))

If a method / function has too many arguments (3 is already scratching the limit) then I try to refactor so:

  • Either most of the argument values are stored as properties if possible (and if it makes sense)
  • Or the arguments are consolidated in a single configuration or context object, where grouping them together makes sense
  • Using the builder pattern can also help with reducing the amount of method arguments

That's... just off the top of my head at the moment 😁

 

Hi Thomas, I loved your small example. This is exactly why I was fighting for in my ex-team, "yeah, but I won't create a function for only one line of code"... Stubborn junior devs, they've made me loss a few hair ahah.

 

Ah how rude of them! 😆

I guess your former colleagues were still OK with adding comments in case of especially obscure logic?

So it's either this:

// Retired product SKUs end with X
if (sku.endsWith("X"))

or that

if (isRetiredProductSKU(sku))

The second version is actually more maintainable. The rules for detecting a retired product SKU might change over time. In case of the first version with the comment it would be necessary to update both the code and the comment, and usually the latter one is forgotten.

Anyway, for people who are really allergic to writing extra functions, here's another variant that is also more readable:

const isRetiredProductSku = sku.endsWith("X");

if (isRetiredProductSku) {
    ...
}

I think sometimes the best you can do in a team like that is to lead by example in your own contributions, and eventually they start appreciating the readability of the code you write and adopt your style 🤷‍♂️

I totally agree with you. Yeah, I've tried to lead by example, but that didn't work either. But thanks to that my manager moved me in another team, much much better! That was such an experience, professionally: working with really bad code, and personally: mental health ahah.

I think they didn't listen because I'm younger, by ego I suppose, I don't really know but that's bad for them.

We have to let your ego aside and listen to people to progress :)

 

I am on the side of readable names. In my opinion (and experience) if you use good names you can avoid most of comments in the implementation.

Sometimes I collect a piece of code in a procedure that is called just once, only to provide a "descriptive name" to that piece of code. Of course, it is not efficient because of the stack manipulation at call/return time, but if efficiency is not a strong issue (and often nowadays it isn't), the gain in readibility is worth the inefficiency.

Of course, coherent and good formatting helps a lot, but nowadays you can outsource that to the IDE.

About comments: I usually do not put comments in the implementation, comments that should explain what the code does. If you choose the right names and your language is fairly high.level (so you do not have to mangle with pointers at every line of code), the code is often self-esplicative.

I usually use comments to document stuff that (i) is not immediately clear from the code and (ii) it is pretty stable (so it does not change often when the code changes). Most of my comments are put in the spec file of a package (.h for C people) to explain (i) what is the objective of the package, why it is there and (ii) what is the duty of every procedure declared there.

 

+1 on the avoiding comments. I remember hearing somewhere that most comments are lies.

 

Sometimes I collect a piece of code in a procedure that is called just once, only to provide a "descriptive name" to that piece of code. Of course, it is not efficient

It depends on your compiler: in C, if you write said function as static inline, the compiler might insert it in your code (it is said to be as fast as macros).

 

You're right. I think that many compilers could do that autonomously during the optimization phase. If I remember correctly gcc does this unless you specify an option.

 

I think readable is code is too subjective to provide a one size fits all solution to achieve it.

Qualities that make code readable?

How much can you understand with the least amount of context. Code that requires the least amount of context is often considered readable.

what helps one create readable code?

Context/information. Descriptive variable/function names and comments are a good way to give context.

Knowledge also plays a big part on what one person considers readable.

 

To make a code readable, the fewer words you must read, the better.

That's why I'm a big fan of terseness, (hence my nickname)

A great thinker I love is Saint-Exupéry. He once said:

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.

Another master, whose words I bring in my heart is Winston Churchill, and there is two quotes of him I want to echo to inspire you as they did with me.

The first says:

All the great things are simple, and many can be expressed in a single word: freedom, justice, honor, duty, mercy, hope.

And the second,

Short words are best and old words when short are the best of all.

As those masters with theirs writings, I see my code as poetry. Sometimes I spend hours in a few lines, as if I was writing a testament. (If I must leave a legacy, I want it be precious.)

So, when writing your next line, remember those luminaries of the past and please,

Keep It Simple Sweetie

 

Many people have already said almost everything I would put so I will just add one more little snippet: don't use every piece of magic and syntactical sugar known to humanity.

Something like a simple loop can tell a whole story where something like .map(...).filter(...) etc can be hard to reason about. Verbosity is not always evil.

 
  • Simple. Minimal nesting.
  • Indentation. Consistent formatting.
  • Good naming.
  • Docstring. Doctest. Rather than simply comments.
    • JSDoc also helps.
    • Unit-testing is a separate topic.
 

In addition to what everyone else has said -

  1. Smaller functions - Huge functions make things difficult to comprehend and build a mental model of.

  2. A function should do one thing and one thing only.

  3. Complex tasks should be achieved by composing together smaller functions.

  4. Smarty pants is bad - whether you are working alone or in a team. Not everyone has the same mental model for a piece of code. If you try to be excessively clever you will slow others down.

  5. Verbosity is a fine line. Don't get overly verbose with your code nor make it so short that people have to break down steps line by line.

  6. Add tools as your codebase grows. Don't start with every thing known to the ecosystem from the get go.

  7. Be cognizant that all advice (including this one) is subjective. You should evaluate your condition and strategize accordingly. Just because one big company does things one specific way, doesn't mean you should go apply it immediately to your project. I've seen plenty of startups frustrate themselves by imitating how one particular FAANG does its work.

 

Being consistent, I think.

Sure, we all have our tastes, but if you read new styles every file, you go crazy.

 

The aim: Code my gran could read.

The reality: Code my dog could eat.

 

// , How do you test for that, and by what metric would you measure whether a given piece of code is "readable"?

 

It does what it looks like it should do. There are not unexpected results from running it after I read it.

 

Please, please, PLEASE indent your code. I have people in my business that write code on as few lines as possible. "It's a waste of space", they say.

I say it's a waste of my time having to make it readable before I can work on it!

 

And separating chunks with a blank line is nice too. I've seen source files in the thousands of lines without one single blank line. ugh

 
 

In short: Care about your work. Try to make your code readable. If you just attempt this as you work you are bound to take more steps towards that direction than if you don't.

Other than that, the answer is: apply standard programming principles.

The rest of the answer would be extremely long as entire books have been written on this topic. However, I would mostly suggest the following.

KISS + principle of least astonishment (things should work as you except) + good naming

  • Code should be as simple as possible. Not smart, no clever tricks. Think of your poor future self or colleague that will have to decipher your code tomorrow.
  • If you have good naming, and you trust your code works, you immediately (mostly) understand what a function does just by reading its name.

Single responsibility principle

Code should do one thing. When in doubt, lean towards extracting or refactoring additional concerns into separate functions with a descriptive name.

There are multiple benefits for this. The basic one is that it keeps functions short and readable.

And much more...

 

Care about your work. Exactly! I've worked with different kind of developers, and I've seen that, readable code is coming from developers that like their job, otherwise... "I don't care, my code works".

 

I think the biggest factors are:

  • consistency in formatting (indentation, bracketing, etc.) and naming
  • refactoring and cleaning up comments once something works the way you want

As far as creating readable code, I think the biggest lesson I've learned was to NOT reinvent the wheel. There's no need to work out a new method of achieving a common goal because the standard is already out there and easily understood by others.

 

Wow what a nice article 👍