Code is for communication. Good code is clear code. Clever code is crap. As Abelson and Sussman phrase it in “Structure and Interpretation of Computer Programs,” “Programs must be written for people to read, and only incidentally for machines to execute.” But we rarely open a source code out of pure intellectual curiosity; rather, we open it with the intention of changing or extending some behavior. So our benchmarks for “clarity” and “readability” cannot be the same as those we use when reading for pleasure. We can enjoy books whose characters are complex and ambiguously motivated, but we can never safely change code whose intention is unclear.
It’s notable how rare the things are that are universally agreed to make reasoning difficult: the unstructured goto, inconsistent formatting, and deeply nested conditionals. Other than that, “It depends” is the order of the day. I tend to dislike shared state, but stateful objects are the building blocks of the vast majority of enterprise code. I see no problem in creating variables and functions near where they are used, but I have many friends who think that consistency of structure is important in functions, modules and programs.
(Related: The best coding book of the decade, so far)
Clarity is in the eye of the beholder, and we tend to think our personal prejudices are universal. I was once at a workshop for programming language designers. The speaker’s language used Smalltalk/Objective-C message syntax (‘Foo:WithBar:AndBat:’) because “It’s highly readable.” Someone raised their hand and said, “For the record, I’ve always found that style very hard to read.” And even among this very experienced crowd, the reaction was simply to shrug: Sure, this affects every line of code written the language, but it’s not worth arguing about. (To be fair, it’s also a choice that could hardly be revisited mid-presentation!)
Once we’ve internalized a concept, we tend to forget our early struggles with them. Whenever I read an introduction to recursion, I know I’ll see some variation of “recursive functions are easier to read.” No, they aren’t, at least not the first hundred. Once we no longer need a crutch, we tend to deny ever needing to use it. I wouldn’t choose Basic for a project, but it’s been taught successfully for 50 years. Similarly, it’s hip in certain circles to trash object-orientation, ignoring the fact that OOP dramatically expanded the scope of programming projects that could be reasonably achieved by most teams.
It is tempting to equate the “learnability” of a language or technique with its clarity, but that’s going too far. For instance, using matrix transforms to manipulate geometry takes some time to learn. But once you get it, a matrix is, I think, the clearest representation of a transform.
I have been working on a tide-prediction complication (that’s a widget for you non-horologists) for the Apple Watch, a task that involves calculations with literally dozens of cyclical components and corrections, with constants transcribed carefully from a textbook. The first function I tackled I simply typed in the constants and variables, connecting them with plus signs. And, of course, I got the wrong answer (I’d probably have a heart attack if I ever typed in one of these functions without a mistake in the constants or operations).
I couldn’t immediately see my mistake, so I reworked the code: I created one list of constants and another list of operations. Then I zipped them together into tuples, applied multiplication to all of them, and summed the results. For developers familiar with map and reduce functions, that might be clear. For others, maybe not. (As I’d hoped, the anomalously large component resulting from my transcription error stood out and was easily corrected.)