Code Quality – Does Simplicity Always Improve Readability?

code-qualityreadability

Recently, I was developing a set of coding standards for our company. (We're a new team branching out into a new language for the company.)

On my first draft, I set the purpose of our coding standards as improving Readability, Maintainability, Reliability, and Performance. (I ignored writability, portability, cost, compatibility with previous standards, etc.)

One of my goals while writing this document was to push through the idea of simplicity of code. The idea was that there should be only one function call or operation per line. My hope was that this would increase readability. It's an idea that I carried over from our previous language.

However, I've questioned the assumption behind this push:

Does simplicity always improve readability?

Is there a case where writing simpler code decreases readability?

It should be obvious, but by "simpler", I don't mean "easier to write", but less stuff going on per line.

Best Answer

"Simple" is an overused word. "Readable" can profitably be defined as "simple to understand", in which case increasing (this measure of) simplicity by definition increases readability, but I don't think this is what you mean. I've written about this elsewhere, but generally something can be called "simpler" either by being more abstract (in which case fewer concepts can express more phenomena) or by being more concrete (in which case a concept does not require as much background knowledge to understand in the first place). I'm arguing that, depending on perspective, a more abstract concept can reasonably be called simpler than a more concrete concept, or vice versa. This, even though "abstract" and "concrete" are antonyms.

I'll use as an example some Haskell code I wrote a while ago. I asked a question on stackoverflow about using the List monad to calculate a counter in which each digit could have a different base. My eventual solution (not knowing much Haskell) looked like:

count :: [Integer] -> [[Integer]]
count [] = [[]]
count (x:xs) =
  -- get all possible sequences for the remaining digits
  let
    remDigits :: [[Integer]]
    remDigits = count xs
  in
  -- pull out a possible sequence for the remaining digits
  do nextDigits <- remDigits
     -- pull out all possible values for the current digit
     y <- [0..x]
     -- record that "current digit" : "remaining digits" is
     -- a valid output.
     return (y:nextDigits)

One of the answers reduced this to:

count = mapM (enumFromTo 0)

Which of these is "simpler" to understand (i.e. more readable) depends entirely on how comfortable the reader has become with (abstract) monadic operations (and, for that matter, point-free code). A reader who's very comfortable with these abstract concepts will prefer to read the (short) more abstract version, while one who is not comfortable with those operations will prefer to read the (long) more concrete version. There is no one answer about which version is more readable that will hold for everybody.

Related Topic