Horizontal alignment

Or, how I learned to stop worrying and love the second dimension

We programmers are mostly trained to write code from top to bottom, hardly considering the horizontal dimension. Sure, we indent to delimit blocks, but that’s it. We habitually waste opportunities for the forgotten dimension to make our code easier to read.

Consider a standard solution to FizzBuzz that looks like this:

for (i = 1; i <= 100; i++) {
  if (i % (3 * 5) == 0) {
    print('FizzBuzz')
  } else if (i % 3 == 0) {
    print('Fizz')
  } else if (i % 5 == 0) {
    print('Buzz')
  } else {
    print(i)
  }
}

There’s nothing “wrong” with this code, in the sense that it gives the right answer and passes the rules of many style guides, but we can do better:

for (i = 1; i <= 100; i++) {
  if      (i % (3 * 5) == 0) { print('FizzBuzz') }
  else if (i % 3 == 0      ) { print('Fizz')     }
  else if (i % 5 == 0      ) { print('Buzz')     }
  else                       { print(i)          }
}

Nothing changed except that I rearranged the whitespace, yet the second version is much easier to read. Horizontal alignment draws our eyes to the patterns; it makes the if-else block look united, emphasizing which parts of the code are the same and which parts are different.

When you start looking for these kinds of opportunities, they appear everywhere. Don’t try this in php, but in any other language with a ternary operator, we can make our if-else stack look even more like a table:

for (i = 1; i <= 100; i++) print
  ( i % (3 * 5) == 0 ? 'FizzBuzz'
  : i % 3 == 0       ? 'Fizz'
  : i % 5 == 0       ? 'Buzz'
  : /*else          */ i
  )

Aside from the ugly C-style for-loop, this is even more clear than the English specification: Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz.” For numbers which are multiples of both three and five print “FizzBuzz.”

There are two reasons we rarely write programs this way.

First, it makes diffs harder to read: if you align your code like this, when you change part of the block, you need to change surrounding lines just to maintain alignment. The diff, therefore, includes distracting changes that are nothing but indentation. At first, the diff-readability argument looks strong because when we consider the readability of diffs versus the readability of source code, it’s not obvious which should win. Look a little closer though and the argument turns specious. We have the technology to solve that problem, you see. Any decent diff or blame tool can ignore whitespace changes, so if whitespace changes are a problem for you, you’re using the wrong tool.

The second argument, by contrast, looks easy to dismiss at first, but is more troublesome in practice: it takes time to write code that uses horizontal alignment effectively. Every competent programmer knows that readability trumps writeability, so obviously you should take the time to align things neatly if it makes for easier reading, right? In the heat of coding, it’s not so easy. When you’re focused on solving a problem, “in the zone,” little distractions like realigning your ascii tables can be real impedements.

Luckily, there’s a strategy to work around this problem that we all should employ anyway: read our own code and edit for readability. It’s ok to leave it a little messy on the first draft, just be sure to revisit and clean up.

Still, there’s no reason our tools couldn’t shoulder some of the burden, particularly when we edit existing code that uses tabular structures. Text editors could recognize these types of structures and adjust column widths as we type…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s