Striving for readability

7 July 2021

Photo by Alif Caesar Rizqi Pratama

Reading code can be really hard. It depends both on who wrote it and who's reading it. I'm easily frustrated when reading code, including my own, so I've always put a high value on writing in a way that's easily interpreted.

How many times have you wondered back to a particular piece of code and thought "who the hell wrote this?" right before you realize it was you? If you're anything like me then probably too many.

How many times have you encountered variables with names that tell you nothing of their purpose? Even worse, I bet you've encountered some that actually go as far as to misinform you.

How much have you suffered on tools, libraries or code with no documentation? Sometimes you do get documentation but more often that not you get the useless kind.

 * Says hello
 * @param name - The name
function sayHello(name) {
    return `Hello ${name}!`;
An example of great documentation 👍

What about the monolithic "utils" file that acts as a black hole sucking all the out-of-place code into an even worse location. A jumbled mess of unrelated helper functions, typically badly named given their scope, hoisted into a separate file because repeating code is a mortal sin.

A blurry picture of an utils file

These are some of the pains a developer might feel on a day-to-day basis. I'll spare you further examples because you're probably much too familiar with these. I think we can all agree that having to read code made to be understood only by its writer is not particularly fun.

If you find it hard to indulge in writing code made to be read by others remember that you are part of that group. You won't be the same person 3 months from now so you'll most likely be reading your own one-liners, monolithic functions and obscure variables thinking the person who wrote them didn't care a bit about you.

That doesn't mean there's ill intent behind it. Sometimes there's just so much pressure and so little time that it becomes necessary to move on. I understand that, I've been there plenty of times. Knowing this, it's in our hands to make it an exception instead of the rule.

Your immediate thoughts on this might be that I'm greatly overstating the importance of readable code and that's completely fair. My goal with this blog post is to convince you otherwise.

If making everyone's life better, yours included, is not enough to convince you to take the leap then do it for the not so obvious benefits that come of it. I firmly believe that striving for readability will turn you into a better developer. By a better developer, I do not mean becoming more likeable to your peers because your code is easier to understand. Instead, I argue that it's a fantastic way to further develop your technical skills, specially in software architecture.

There's a way to think about it that made it clear to me why this is true:

Code is the translation of our ideas into machine instructions

I don't think this is new to anyone but the phrasing helps make the point.

There's a direct correlation between the code you write and the ideas you're trying to convey to the machine and your fellow developers. For example, if you somehow made your code easier to be read, by consequence, you have also made it easier to understand the ideas behind it. You have further refined the intent of those instructions. Of course there's also the risk of conveying the wrong idea but that's parallel to the main point.

refining a concept through codeRefining a concept through code

When someone says they'd like your code to be more readable what they actually tend to want is for your code to be organised in such a way that is more easily understood.

This is an important distinction I'm making here because there's a whole lot more you can do to help your colleagues understand your code besides making it more readable, in the literal sense of word. For example, splitting your code into separate files is not helping in terms of readability, again in the literal sense of the word, but it does help everyone grasp each separate module more easily.

By distilling our ideas into the simplest form of code possible we can better combine them, much like a puzzle. What makes it really difficult is that the puzzles we typically solve have malleable pieces of different shapes and sizes and we might want to replace some of those with arguably better ones in the future.

How it feels starting out - photo by Hans-Peter GausterHow it feels starting out - Photo by Hans-Peter Gauster

If that wasn't hard enough, now imagine trying to solve a puzzle where each piece looks pretty much like the others, the connections are ambiguous and you can't even tell how many pieces you actually need because some are too large and the box came with duplicates. That's no ease feat and far too many times the reality we see ourselves in.

To turn this into something more manageable, we could start by putting all duplicates in the trash and shaving off the excess of the larger pieces or even splitting them into smaller pieces. Then, we could make sure that each piece has only one way of connecting to any other piece thus removing all ambiguity out of our puzzle. Finally, we could make sure that each piece is as distinguishable as possible, not too big as to make it hard to replace and just small enough to represent a concrete section of the whole picture.

After a few rounds of this iterative process you finally manage to look at all the pieces at once and clearly see how they connect, enabling you to make sense of it all.

When you're done - photo by Jelleke VanooteghemWhen you're done - Photo by Jelleke Vanooteghem

Working on splitting the responsibilities of each block that you write helps you piece the whole system together in your head more easily. Using the appropriate names drastically reduces the chance for misinterpretation and makes it that much faster to understand what you're reading. Including the right abstractions minimises the context you have to juggle around in your mind.

When you follow this mindset it becomes much easier to develop systems of a higher complexity because you effectively lower the mental agility required to understand them. You quickly notice that this practice actually changes the way you think and not just the way you write.

As with everything, you get better with practice. Wouldn't be amazing to write beautiful, performant and well structured code on the first try? With every keystroke the code base becomes instantaneously closer to the end goal since you wouldn't need to re-write it. What a wonderful lie to believe in. Of course I'm fully aware this is impossible, we are human after all.

Many things in life are not achievable, solely due to the human limits and how the universe works in general. We will probably never travel at the speed of light but that does not stop us from building expensive supercars and rocket ships. Just because absolute perfection is not attainable it does not mean there aren't benefits to gain from trying to get there.

There are tangible rewards from trying to maximize readability and please note that by now the concept of readability is charged with more meaning than just its literal interpretation.

It's not as easy as following any particular list of rules that dictate how you write your code, at least where it really matters. Instead, it's more of a creative process. One that demands a lot from you and differs from situation to situation.

It's not easy initially, naming for example is one of the hardest things we do after all so it might feel like you're bashing your head against a brick wall, pointlessly. You'll also find that the struggle is constant, there is always something we can do to improve readability.

If you keep at it though, you'll notice the rewards scale exponentially. Your efficiency at creating readable code grows over time which means the base quality of the code you write will naturally grow. This in turn will ensure the codebase also gets better over time, not only because of the new code you add but also by the refactoring you do, which also gets easier over time. You'll also have a keener sense for code smells when reviewing pull requests because the lack of readability exposes them even more.

Finally, one not so readily apparent benefit is the documentation you now have baked-in into your code. All the time you spent making it readable pays off in spades for newcomers to the project, or developers touching a particular part of the codebase for the first time, because it's all pretty much self-evident and in no need of supporting documentation.

The ruthless pursuit of qualityThe ruthless pursuit of quality

And then someday it feels like a switch was flipped. You now feel that you can manage tasks of a complexity that previously scared you to death.

My whole point here is that you shouldn't dismiss practising a skill just because it's impossible to get perfect at it. Embrace the journey, reap the rewards of your newly found skills, and most importantly, have fun while you're at it.

Have you heard of the broken windows theory? Picture a building that has just finished construction, in its most pristine condition possible. When people pass by it they see it as a monument to progress, a world of possibilities waiting to happen inside. Some even might take it as part of their responsibility to make sure it stays that way.

With the passing of time things will start to degrade. The paint starts to fade, cracks start to show and trash begins to accumulate in the vicinity. Soon, there's a broken window and the whole charm of the building is dispelled. Now no one really notices if more windows appear broken and they somehow start to multiply.

broken windows analogy - photo by Matt ArtzPhoto by Matt Artz

Not long after people are purposely degrading the building through graffiti or other types of mischief. What began as simple acts of negligence paired with the natural degradation of things led to active and conscious disregard towards the integrity of that building.

This is what the theory affirms, that smalls actions of negligence promote further, worse actions of negligence. The solution to this problem is to tackle it at its roots and as soon as the first signs of trouble begin to emerge. We can stop that vicious cycle from getting out of hand by simply taking the most basic preventive measures such as repainting the building on a regular basis. When the problems present themselves we should do everything to fix them there and then. If we see a piece of trash where it shouldn't be we can simply pick it up. If there's a crack on the walls we can call the right people to fix that specific issue. There's always something we can do and if we truly give ourselves to that mentality there is simply no space for negligence to exist.

While this theory touches solely on the spreading of bad behaviour it does also seem to work in the same way with good behaviour. There's something about our human nature that compels us to protect things of beauty or of symbolic importance. We're drawn to these things and they can be found everywhere: the majestic sight of a snow covered mountain, the starry sky on a clear night and believe it or not there's beauty to be found in what we do. Shouldn't we try to nurture that feeling that is so deeply embedded in us? There's a real sense of fulfilment to be discovered here.

Much like the brand new building in its most pristine condition might influence passersby to keep it as such, having your codebase in its best possible state will undoubtedly inspire others to nurture it.

Don't underestimate the benefits of caring this much about your code, it will make you and everyone else around you a better professional.

One of the critiques that is typically leveled against this practice is the fact that it is simply not fast enough in these fast-paced days we live in. The only way to go fast is to go well. Doing the bare minimum to merge something into master surely is fast, at that moment, but it will cost you dearly further down the line.

the icon of speed - photo by Muaz AJThe Icon of speed - Photo by Siem van Woerkom

Imagine there's a particular piece of logic that everyone will eventually have to contribute to. For the sake of this experiment lets say the first iteration of that module could be written in a matter of hours as opposed to a day or two if you had to really focus on making it readable.

Those saved hours would be lost rather quickly if we were to calculate the time required to first understand that module and then modify it, multiplied by all developers that would eventually have to touch it. In addition to that there are also the bugs typically introduced by working on a piece of code you don't fully understand.

You can definitely notice when the code starts to rot, when you've reached that point where any minor change might break everything. I don't believe this is inevitable but it definitely takes some effort, experience and commitment from everyone on the team to manage. Even if it were certain that we all eventually reach that point, regardless of the variables, it makes sense to take advantage of pushing that boiling point the farthest away possible.

Fastest is not always the better way, losing a tiny bit of performance for a big readability boost might make sense to you. You get this "performance" back by being much more prepared for new features, bug fixes and future refactors.

This all started with a very specific goal in mind, to make code more readable. Then it led me all the way here, quite unexpectedly. Something that was merely meant to combat some personal frustrations evolved into something much greater.

Hopefully made it clear that readability is not the actual main point of this article, but the learnings that came of it. If readability is not your thing I'm sure there is something else you deeply care about. Pursuing that in a similar fashion might work wonders for you as well!

If you agree with most of what was stated here though, you might want to give readability a proper shot. Who knows? Maybe you'll find it as useful as I did.