Readable Source Code

Denes Kellner
11 min readMar 25, 2023

--

If I could give programmers just one advice, one thing to focus on a little more, it would most certainly be this one aspect of coding. Something even professionals seem to get wrong so many times. Something juniors think they don’t need, mediors think they already know, and seniors wish they understood a lot better.

Readability.

You may think, “I will format my code later, first I need to make it work”, or even “readability is just a nice-to-have”. Let me show you why this is absolutely mistaken.

  1. 99% reading.
    You think programming is writing code, right?… Well, less than 1% of your time. You’re spending the rest by looking at your own lines, or documentation, or something someone else created. Mostly it’s reading and understanding. How easily you can do this, that’s the single most important factor in how fast you work.
  2. The Slowdown Paradox
    Projects start fast, and they gradually slow down. Why? There’s plenty of science behind that but here’s the lite edition: the more you’ve done, the more you need to connect to. Your initial freedom of naming things and organizing your structures suddenly changes into thinking what’s already there, reading what you’ve done so far, and adapting to your earlier thoughts. As you realize this, you start to rush yourself because you want to perform as fast as you did in those first days. Now the paradox is, the more in a hurry you are, the less readable source you’re creating, so it actually slows you down to not slow down and do what you really should: refactor. Decrease the viscosity of your source before it becomes a deadly swamp. It should be water, my friend.
  3. You will forget everything
    As a junior, most of us think “no need to document this, I will remember”. Yes, your mind has a cache. And then the cache is full and it starts dropping old things to pick up the new. That’s how you gradually forget, maybe not everything, but most of it. And it’s across all your projects, and all your lifetime. So the best thing to do is, consider you will forget literally everything.

So, how do I…?

Million dollar question. How do you write readable code?

You get lots of google hits for that, and they keep recommending best practices. Proper indentation, variable naming and all that jazz. Usually they tell you to use a formatter as well. This is something I don’t agree with, but surely if you work for a big company, you won’t have a choice.

But to be really productive, you need to understand why some codes are super readable, while others, however well-formed, are not. Tips like code grouping, avoiding deeply nested structures, good commenting, they all make sense and they will be covered in my next articles. But here’s the most important thing.

It’s all about
visual focus.

You see? This text stands out, because it’s not like the rest. You need to use this quality of your eyes: if it looks at things that only differ at some points, it picks up the pattern, and focuses on what’s out of the pattern.

Check this:

See what your eyes just did? They realized it’s almost the same, looks very similar, but then you scanned to the end of the line and spotted that one different word. Pattern and difference. It’s how you read, it’s how you see things; it’s how you recognize the elements and spot the unexpected piece.

Now your eyes jump back and forth, and because you’ve already seen the text, you’ll start to look for the known elements; it’s a lot more work because of the multiline form, but yess, there it is, “below” vs “above”. Quick check: is everything else the same? Jump, jump, jump — yes it looks alright. You move on, without realizing there’s a Star Wars character in one of the strings. Now that I said that, your eyes jump back, and do the scanning again. Oh wow, there it is!… How many seconds now? And how many seconds this will take the next time, when you’re already afraid of making the “luke” mistake again?

You’re losing lots of valuable time by navigating within your own code. It would be awesome to make it all easier for your eyes — every second that you save, literally every second, will matter at the end of the day.

So we have our first takeaway:

Takeaway 1: clear visual patterns help your eyes navigate.
Try to make your code convenient & pleasurable for your eyes. Give it a rhytm.

A bigger fish

To dive a bit deeper, let’s see some real-world example! Here’s a piece of php from BaconQrCode, you can find this in any fresh Laravel install, this is a low-level function in a file called MaskUtil.php, if you’re interested in finding it. But just take it in visually for now.

This is a simple function, and some would say it’s nice and clear. After all, it’s formatted consistently, right?

If you think there’s nothing wrong with it — please stop reading this article. You must be someone who likes autoformatters, and I respect that; here’s the perfect moment to part ways :)

So, in my books this is not a very readable source. Here’s why:

  1. It’s overly spacious, almost scattered around the screen;
  2. It looks random, I must think about every single line, and when I understand one, it doesn’t tell anything useful about the others;
  3. All the formulas seem to do something complex and important, then there’s a return statement at the end, reducing it all to a true/false.

Usually, I don’t recommend the switch statement at all because it gives nothing and introduces some risk of forgetting a break, also it’s hard to format nicely. However, this is a special case when we can do the only thing it’s good at: handling each case in a single row, with a straight vertical arrangement where you just look and see what each of them is doing.

So that’s the first thing for me to do here.

Switch reformatted: one case = one line

It may look a bit dense, but already a lot more obvious: most of these lines are closely related. Each case is putting some value in the variable called $intermediate. And if you’re like me, your eyes are already asking you what the hell happens here:

These lines contain some additional stuff. What exactly? Oh, it’s a multiplication. For a calculation later on the line. Is that really so important to break the whole pattern? I’d say no; each line has two further mentions of $temp and then it’s gone forever.

How’bout just moving it before the switch statement:

What a relief, right?… All we had to do is a harmless modification, and suddenly it’s so much clearer. Yes, 5 of the 8 cases won’t use the value, but this is not a function where the cost of a single integer multiplication (a few nanoseconds) would make a difference. (But always stop to consider — this is a dangerous move in some cases.)

We’re not even close to the final form, but we already see a lot clearer. Our eyes now understand that this is a switch that performs one of the 8 possible bitwise operations, whatever they’re doing; at this point we don’t care because it’s still a bit messy.

The Weight of Words

Remember, all we’re going for is your eyes’ convenience, and the ease of understanding. Now if something takes a lot of visual field, your eyes usually spot it and think “this must be something important”.

Well? Not this one:

We have a variable naming problem here.

Schools usually teach you to use long and meaningful variable names, which is (in general) a good idea. But not when it steals the focus from other parts: not when something has marginal importance, and your eyes are automatically drawn to it instead of what’s really going on.

The ideal length of a variable depends on its importance, and the size of its scope. If the whole application has access to an identifier — class, function, variable, whatever — it can afford to be long, but for temporary calculations within a five-line area, shorter is better. I mean, $a + $b makes more sense than $temporaryResult + $anotherIntegerValue when the whole point is just the addition and you never see the variables again.

Takeaway 2: don’t increase clutter with naming.
Long names mean importance. Disposables can be short.

So in our example, for such a temporary integer, even a single-letter variable name would be absolutely sufficient.

Also, I’m a bit worried about $temp over there. The one we just moved to the top. Okay, it’s temporary; but neither temp, nor intermediate tells anything about the real purpose, they’re just showing off like dude, check this out, I have 12 characters in me! Yes you do, and it’s quite unsettling.

Time to rename them!

Same code with a long variable name replaced by a short one

We named the product of $x*$y as $mul, I think it’s clear enough, what else could it be than something multiply-related. And instead of $intermediate, we now have $out which gives us a hint that it’s something we’re about to output (return).

The visual focus is now a bit more shifted towards the right side, where the actual magic happens:

A closer look to the inside of the switch statement

It’s still a bit of a jungle on the right, though. Parenthesized expressions, something plus something modulo something, combined by a bitwise AND with something else, divided by 3 for some reason — question marks all around the place. This is not the readability we’re looking for. If you’re in a hurry looking for a bug somewhere in there, your eyes will not thank you for what they see.

Now, watch my hands! All I’m adding is some spaces:

Same code but some of the operators and punctuation is now vertically aligned

See how it’s already happening?… Your eyes see the vertical patterns, and can easily understand how different cases are doing something similar.

But you know what, I don’t like that ($y + $x) thing. First of all, why not just ($x + $y), it comes more naturally; but also, it has a few mentions so let’s precalculate and name it too, just like we did with $mul before!

Let’s replace these expressions!
Now they are a lot easier to get

Looks a lot better!

Side note: we could say $m and $s instead of $mul and $sum, but that’s the case of overdoing. Those variables have the right to stand out a bit: they are different. Visual understanding loves when looks reflect logical relations.

Takeaway 3: make visual appearance reflect logic.
What’s similar / what’s different should be obvious at a glance.

Now let’s deal with that little elephant in the room.

Like, wow! That one line is acting like it was the center of the universe, when all it really does is divide $y by 2, in this case. We need to be careful when replacing things like that — usually they have a reason to exist. But in this particular case it’s safe to just use $y>>1, or even (int)($y/2), instead of a cryptic function call in the middle of the nicely cut grass.

(Long explanation for those who like splitting hairs: for negative numbers, an unsigned bitshift gives a different result, but this function is only called with positive numbers, yes we know that. Also, to support negatives, a normal “>>” operator would work like a charm because parity-wise it gives you the same results as unsigned would. Furthermore, just for trolling, there are no negative numbers, and 32768 is Schrödinger’s cat, #provemewrong.)

So let’s replace the whole BitUtils::unsignedRightShift thing with:

Bitwise operation replaced with a simple division by 2

And now just one more thing before we call it a day: there’s a default line at the end of the switch, it needs a little discipline because it’s annoyingly long and not pleasant to look at.

If you like it this way, just leave it alone, but I’d prefer this:

Just a double quote (to let the variable blend in nicely) and a trim() around the string (to get rid of the whitespaces I added by making it multiline). This way, the final screenshot will simply look better.

So here we go:

Final version after all the formatting

What do you think?

As for me, I think this is one possible station to get off the bus. This source will not torture your vision: when your eyes ask a question, the source will give the answer. It could be better, it could be more compact, we could make the break statements go away by using return instead of $out = , we could use if-lines instead of the switch structure to make it shorter and more concise; we can do a lot about the logic too, if you dig into it. But let’s stop at the point where 20% of the work gained us 80% quality.

Takeaway 4: there’s a point where you stop beautifying
No one will thank you for spending a day formatting 30 lines of code.

Afterwork

We might have made mistakes. We might have introduced new bugs, maybe the function now works differently, so always, always use some form of testing when you refactor. That’s a no-brainer.

But also, when you make a function (or any part of the code) more transparent, more obvious, it often reveals some old bug or logical flaw. You didn’t see it so far but now it’s shouting and waving. So when you touch the business logic, I’d say make it a different step, a different commit, first just deal with the looks and then the functionality. When beautifying a code, it’s best to take small and absolutely harmless steps only. Like adding spaces or joining two lines. Not saying that can’t go wrong; but less likely.

Otherwise: hey, congratulations, enjoy your super readable code!

There’s a lot more to do for a codebase, and we’ll cover most of that in the next chapters, but for today this should be plenty to take in.

--

--

Denes Kellner

World is already a beautiful place. I'm just looking for ways to contribute.