OpenMarket – January 6, 2019
Writing clean code is one of the main goals of the SMS team at OpenMarket. It comes near to the top of the list in our definition of code quality. Readable, maintainable code is a key asset to any engineering organization and has many downstream benefits from higher service availability to increased velocity.
One sometimes surprising counterpart to the goal of clean code is concise code. Unlike daily speech, code should be short, when practical. This is often counterintuitive. People who say very little are generally considered rude. The adjectives available for someone who doesn’t talk much almost entirely have a negative implication. By being ‘terse’ or ‘blunt’, you’re more likely to hurt someone’s feelings than help them. No one ever won a social award for being ‘abrupt’, ‘brusque’, or ‘trenchant’. ‘Concise’ may be the only neutral to positive adjective in the thesaurus next to these words.
Nevertheless, code should trend away from verbosity. There are many reasons for this, but three of them are especially compelling:
- Fewer lines to write means fewer lines to read
- Bug ratio goes down when lines of code go down
- Memory is unreliable
Fewer lines to write means fewer lines to read
In Code Complete, Steve McConnell shares research that programmers read code ten times more often than they write it. Experience also bears this out. When something can be written two different ways, the shorter way wins for simple maintainability and readability reasons. If enjoyability were a software metric then shorter usually wins on that as well. The abstract, vague notion of elegance almost always incorporates shorter length as a supporting dimension.
At any rate, fewer lines of code lowers friction for the reader. And since your team reads code so much, then having to read less of it in order to get on to the bit of code they want to add is self-evidently a good thing. Long live short code.
Bug ratio goes down when lines of code go down
Another common software metric measures bugs per lines of code. There are (of course) two parts to any ratio. As much focus as reducing bugs rightly gets, another way to reduce bugs is simply to write fewer lines of code. If the software industry in general averages 15 to 50 bugs per 1,000 lines of code, then keeping lines of code down also keeps bugs down!
Steve Yegge observed that lines of code are ‘code’s worst enemy‘. Complexity makes an application collapse under its own weight:
My minority opinion is that a mountain of code is the worst thing that can befall a person, a team, a company. I believe that code weight wrecks projects and companies, that it forces rewrites after a certain size, and that smart teams will do everything in their power to keep their code base from becoming a mountain…
Memory is unreliable
Finally, human memory is unreliable, and that’s why code should be short so long as it is clear. It’s important to remember code has two purposes. First, it instructs the computer to perform the task or series of tasks in question. Second, it documents and communicates to other humans what our team wants the computer to do.
Donald Knuth agrees:
Let us change our traditional attitude to the construction of programs. Instead of imagining that our main task is too instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.
Now, the problem with long code in this context is that it doesn’t alleviate the fact that human memory is unreliable. We have made a lot of attempts to address the problem of human memory in software engineering (specifications, user stories, requirements documents, etc.) and have largely failed.
Today, people often refer to code as documentation. Code has the purpose of supporting our memory of how to do a task. The reason this is important is that the business logic for any given task often comes at least partly from some other person or some other team in the organization. Sometimes it comes from a product organization within a company. Sometimes it comes from a customer. Sometimes it might come from other stakeholders.
The important question here is: can you recover the business logic from a reading of the code? And the important point is: there is almost no scenario where the longer a piece of code is, the more it helps you understand the business logic.
Verbosity of code often helps support technical concerns (such as strong typing of variables) but it almost always harms the efficient recovery of the underlying business logic.
To give a concrete example, let’s assume you are a software engineer working on a large system. There are multiple applications interacting in very specific ways to produce a real-world result. Every day, there is something new you turn your attention toward. Maybe today someone filed a bug that looks exactly like a particular bug you fixed last year. Can you remember all the little details like where in the code you fixed that bug? Probably not! But if the code is no longer than it needs to be, you have less surface area to cover in locating it.
Maybe tomorrow you have to answer to a director who is demanding answers about why production monitoring did not catch the last big event in a timely manner. Unfortunately, you know the answer. There is an incomplete integration with your systems’ monitoring service. Time is always in short supply for an engineering team, and these things happen. Last time the team was working on the monitoring, the same director asked the team to stop and move onto a big business initiative. So ever since then, the monitoring items have been floating in the backlog at a level just below ‘important’ work. But that’s not the important bit. The important bit is that when monitoring stories get pulled off the backlog, your team will have to go back over all the code again from scratch if they want to understand it.
In this reality, what helps a team get things done faster: a lot of code or a bit of code?
How to write as little as possible
Unfortunately, the answer isn’t as simple as ‘write less code’. The real answer is to take the time to understand the given business cases in greater depth and iteratively groom the code down to meet them. I have always found that it’s easiest to make fat code, but trimming it back is hard. TDD is the most helpful working style I know for keeping code slim (well, slimmer) when first written, but its real power comes from the refactor phase. By refactoring, you take the good hearty stew you’re simmering and boil it down to its essence–the fewest lines of code possible without obscuring its meaning.
All of this requires a little more time and a little more thought than you probably want to put into the thing, but remember what we talked about so far: if you don’t take this little bit of time and thought, you’re creating a monster that will eat your time and brain at some point in the future, maybe two weeks from now, maybe a year. Code has its own weight. It’s our job as software engineers to resist its collapse into a black hole that will suck our team and company down inside and never let us escape.
Pay Attention to Complexity
All that being said, the goal isn’t to write the fewest lines of code possible. (Save that for the International Obfuscated C Code Contest.) It’s to get the point across as quickly as possible. It’s more about complexity than length.
I find the thoughts of Alan Perlis, the godfather of object-oriented programming, the most helpful here:
Fools ignore complexity. Pragmatists suffer it. Some can avoid it. Geniuses remove it. Simplicity does not precede complexity, but follows it.
That’s why I lead off with the idea that, “you should write as few lines as practical”. It’s no longer practical if you pass that point where the code starts to make less sense than more. But there’s certainly a point where simplicity follows complexity, and that’s where we should aim.
So in conclusion, strive to write as little code as practical. Certainly no more, but also no less. Doing so is an investment, with the payoff (at least) of having fewer lines to read, fewer bugs, and an easier time remembering where to go when you do need to fix bugs.