Readability Matters: It’s Not The Language

It’s not the language that makes code more readable, it’s how you write the code. It’s how you decompose the problem space into classes and methods in those classes. It’s how you decouple the classes. It’s whether your language uses a garbage collector or not (and if not, whether you’ve been consistent in freeing memory and how you free it). It’s consistency in how you write similar code. It’s in keeping the reader familiar with your code.

Most readers of your code are going to be reading it from the vantage point of knowing the language you’re using. Some readers will be reading it knowing just enough about the language to be dangerous. And then some readers will be reading it from a vantage point of complete ignorance and trying to match the code to a language they know better.

For an extreme example for most of my readers, here’s some Haskell code:

insert :: Int -> [Int] -> [Int]
insert n [] = [n]
insert n (x:xs)
   | n <= x   = n:x:xs
   | otherwise  = x:insert n xs

Is this readable? To a C#, Java or Delphi programmer, heck no! To a Haskell programmer, yep, it’s pretty simple. Here’s the roughly equivalent Delphi code as I would write it:

procedure Insert(n : integer; aList : TList);
var
  i : integer;
begin
  for i := 0 to pred(List.Count) do
    if (n <= integer(List[i])) then begin
      List.Insert(i, pointer(n));
      Exit;
    end;
  List.Add(pointer(n));
end;

And here it is in C#:

public static void Insert(int n, ArrayList list) {
  for (int i = 0; i < list.Count; i++) {
    if (n <= (int) list[i]) {
      list.Insert(i, n);
      return;
    }
  }
  list.Add(n);
}

So, the Haskell code inserts an integer value in a sorted array or list of integers. Which is more readable? Answer: what you are more familiar with.

(To those not familiar with Haskell, you can read the above code like this. Line 1: insert is defined as a function that takes an integer and an array of integers and produces an array of integers. Line 2: If the array of integers is originally empty, return an array that just contains one element, n, the item we’re inserting. Line 3: if the array is not empty, it’ll consist of the first element x and the remaining part of the array xs, so let’s define how that works in the indented lines underneath. Line 4: if the item we’re inserting, n, is less than or equal to x, return the array formed from n, x, and the rest of the array. Line 5: otherwise the answer is obtained by prepending x to the result of inserting n in the remaining part of the array, xs (a recursive call, in other words).

As you can see my equivalent Delphi and C# code is not the same at all since it returns the same list with the new item in it, not a completely different list. I cheated (hey, I’m writing this article and I’m allowed to!). See how you can do in writing a better Delphi/C# equivalent that does what the Haskell code does. The C# version should be very easy; the Delphi version less so since the question to consider is this: what happens to the TList you pass in? Who frees it? Should this routine do so? Haskell and C# are garbage collected and you don’t have to worry about it. Delphi (at least the Win32 version) is not and so you have to consider adding a boolean parameter perhaps to free the original list. The code will become less readable.)

Do you favor multi-page, big methods? Your code is less readable. Do you have code that indents more than, oh, say, twice? Your code is less readable. Do you have methods with more than, say, three local variables? Your code is less readable. Do you regularly write methods with more than three parameters? It’s harder to read and understand and use. Do you have if or do statements with more than two conditions perhaps? Your code is automatically awful to read. Do you ignore refactoring possibilities? Your code is less easy to read. Do you use a lot of comments? Obviously your code is not readable since you are having to explain it. Do you leave the auto-generated names for controls on your forms (Button1, et al)? Your code is not readable. Do you favor single-letter names for identifiers, even for variables that are not counters? Your code is harder to read.

Do you have to work hard at making your code properly indented? You need a new IDE that formats your code for you. Do you read code in a program that doesn’t do syntax highlighting? Get an IDE that does for the language you’re reading.

Do you not use accepted coding patterns for your particular language when writing code, but instead use something unfamiliar? Your code will be nigh on impossible to read. Do you like trying to optimize your code as you write it without running a profiler instead of writing the most obvious thing? Your code will probably be slower, and harder to read to boot.

Being someone who writes algorithm articles, my research tends to throw out code in many different languages: C, Java, C++, Ruby, Haskell. I’m so glad when the writer has taken the care to make his code readable, no matter which language he uses.

In essence, writing readable code is a high-level, design-like activity. It’s usually not about the nitty-gritty syntax details like block delimiters or where variables are declared. It’s about how we structure our code.