In The Art of Readable Code (ISBN 0596802293), the authors call the following the “Fundamental Theorem of Readability”:
Code should be written to minimize the time it would take for someone else to understand it.
They go on to explain
And when we say “understand,” we have a very high bar … they should be able to make changes to it, spot bugs, and understand how it interacts with the rest of your code.
9 thoughts on “Fundamental theorem of code readability”
I don’t buy it. Not any longer. Let me explain it with three situations:
1) Assume you are working on a problem that requires convex optimization. You use a solver to do so but you are not happy because it converges slowly. Assume that the code is written as the authors say so you understand what it does. Do you think you know enough numeric methods to improve that solver? Ok, bad example because you are likely to know, but my point is that no matter how cute code looks never never underestimate the need for domain knowledge in order to actually understand what a program does.
2) Should good and old carved-in-stone code from BLAS, Lapack & Co. be subjected to the same rule above? After all, it’s so mature that there is little need to enhance it and in case of highly optimized libs from Intel or AMD there is not way out of assembler if you want to squeeze down to the last drop of juice of your rig…
3) Last but not least: I’m gonna dare to say that you are as experienced as me using Prolog (which is close to none). How would an expert write Prolog code for fellows like you or I? And the converse: would you change your current C/C++/C# coding manners so that Prolog experts with little exposure to your languages of choice would be able to understand it?
jdborras: I didn’t interpret the book’s advice the same way. If you need to write tricky, highly optimized code, then do so. But make that code as easy to understand as you can, which might not be very easy.
Your third point brings up an interesting issue: easy for whom? At a minimum, make sure your future self will find the code easy to understand. Next, make the code understandable to a hypothetical person you could imagine inheriting the code.
I don’t think you should avoid advanced features of your language. If you’re not trying to show off but simply writing idiomatic code, then someone who doesn’t understand it needs to read a book. The good news is that they probably can find such a book. You can’t find a book on how to read my coding style, but you can find books on idiomatic C++, Python, etc.
This is a good general rule. Sure there are exceptions that warrant breaking the rule – but nonetheless, you must be able to justify why you are breaking this rule before you can go ahead and break it.
As jcborras says performance optimizations almost always decrease code readibility.
However, unless you happen to be one of those few programmers writing assembly code for Intel, performance optimizations are also often premature.
I agree code needs to readable, but the easiest way to understand code is documentation. Sometimes just a few brief comments can go a long way in understanding what is trying to be achieved. How many times have you looked at code you wrote 3 years ago and had to take longer than you figure you should have to understand what you wrote then? I think there is too much code out there that is poorly documented. Even just a quick comment on what the goal is of a complex peice of code can go a long way. Sometimes there may be a reason why it was written a certian way so it’s always helpful to have a clue to what the reason was.
You are right: «Easy for whom?»
I write code and comments in the way it will be easy to me to understand it in future. I modeling myself reading the code and making changes until this hypotetical «me» easily understands code.
In contrast in place where I work my code is considered as «very hard to understand», due to heavy use of C++ templates and metaprogramming techniques. But these things make my code easily adaptable to new projects! I hate templates and metaprogramming in C++-way, but I have no choice.
I’m with jcborras: I don’t buy it too. There are lots of reasons but lets begin by what the author called “Theorem”. If it is supposed to be a theorem, at least some assumptions are required. However, there is none. This means the author was trying to give some undeserved weigh to his assertive or trying to put some humor or both. Anyway, good writing is hard (as you can conclude from my own text :-)) but no master writer have written texts without requiring specific backgrounds from the reader (not even Shakespeare, I think). Writing programs seems similar to me. You have a language, sintax, semantics and history. Write about numeric integration but don’t expect that a reader without a calculus class to understand it. My solution is to write down exactly what background is needed to understand and maintain the programs. I want to think this may be good when my employer need to hire someone else: he will have precise knowledge of what look for.
The “fundamental theorem” doesn’t adequately appreciate that the variable “someone else” has a probability distribution over it. As Anton suggests, who your audience is determines what’s easy to understand – and what’s not. Folds and continuation passing style may be intuitive and even informative to functional programmers, but not so much to OO programmers. So know your audience and write to what they know.
It’s not so hard to figure out who the target audience should be: someone who is suitably qualified to work on that code, i.e. someone who has the appropriate programming and domain knowledge. You, as author, presumably fall in that group, but you are not the ideal target audience, as you have special knowledge of the code.
My apologies if I am wrong, but I got the impression from jcborras that he is thinking in terms of self-documenting code, while the authors of the book include commenting in their scope (I do not know if they cover documentation more broadly.) I consider strictly (i.e. commentless) self-documenting code to be infeasible in non-trivial cases, because programming languages are not suited for explaining the intended purpose of each entity (class, function, module…) introduced into the program, and which, in nontrivial programs, do not obviously follow from a combination of requirements and domain knowledge but are also a consequence of design decisions. A knowledgeable (in the sense of my first paragraph) person could certainly deduce that purpose, but it is a wasteful and frustrating exercise.
Consequently, the highly-optimized programs that jcborras writes about are the ones that most benefit from a little explanation. I assume your optimization process includes a certain amount of reasoning that the optimizations are sound, and your successor would surely benefit from a summary of that reasoning, were he to read it.
This line of reasoning leads me to conclude that the ‘fundamental theorem’ is overstating the case: I would have to prefer an undocumented but soundly-reasoned implementation over a well-documented piece of wishful thinking. As it happens, writing down your reasoning over anything nontrivial is one of the better ways of finding flaws (an even better one: have someone else read that explanation.)
One word in response to jcborras’ second item: abstraction. Where code is mature and there is little need to enhance it, documentation of its purpose and interface is all that is needed.
RP is right in saying that good writing is hard, and that applies to writing code as well. Some of the skills needed to write well generally are also skills needed to write code well – in particular, the ability to formulate and express a cogent argument.
Hi everyone, co-author of the book here. I appreciate all the discussion. Let me clear up a couple points asked above:
– yes, by “code” I am including comments, and even whitespace, file structure, or whatever else affects how easy it is to understand a program. Adding comments is certainly a great way to make code easier to read (and there are 2 chapters in the book on it).
– regarding optimization, I am assuming the business constraints of what your code has to do are fixed. So maybe your constraints are “this function must return in under 10ms” — in that case, you should come up with some algorithm that can return in under 10ms, and within that, the code should be as readable as possible. More often though, there are a variety of algorithms that get the job done, and in that case you should pick the one that results in the easiest-to-understand code.
– the “someone else” is whoever is likely to read your code, which probably includes *you* in the future, as well as possible teammates, coworkers, maintainers, etc…