Are you a good programmer? How do you know? The definition of “good code” is probably very subjective, but maybe it can be helpful to try and define some less-subjective criteria for discerning between good code (a.k.a “my code”) and the terrible excuses for code I have to wade through every day (a.k.a “my code from the month before”).
The primary criteria for code quality that I currently value amounts to:
- Elegance and Simplicity
- Reliability and Predictability
- Speed and Efficiency
The code we write should at the very least be working correctly.
This may be so obvious that it’s hardly worth saying. I mean, what is the point of writing code that is not working? But looking at the number of times we inadvertantly introduce bugs in our code I think it is worth making this implicit assumption explicit.
What can we do to measure and guarantee correct code?
Unit tests are a great tool, especially if we can write them as property checks. But in recent months I (like many others before me) have started asking “how can we move these runtime checks to compile time?”
Type systems are fascinating. I’m beginning to think that we can encode a fair number of the checks we currently do at runtime (and check with unit tests) into the type system.
Some programmers stop after they get a feature working, as if “working code” is the only thing that matters. Correct and working code is obviously important, but it is only the first step towards quality code.
“A program that produces incorrect results twice as fast is infinitely slower.”
— John Osterhout
“Correctness is clearly the prime quality. If a system does not do what it is supposed to do, then everything else about it matters little.”
— Bertrand Meyer
Elegance and Simplicity
“Everything should be made as simple as possible, but no simpler.”
— Albert Einstein
Complexity is one of the biggest problems in software. Complex code are sometimes engineered like a Rube Goldberg machine. It may work, and it may look impressive, but the simple unimpressive solution is superior in every way.
Elegance and simplicity are the aspects I value most, because it seems that all aspects of code quality require simplicity, or is at least vastly improved by simple and elegant code.
“Fools ignore complexity. Pragmatists suffer it. Some can avoid it. Geniuses remove it.”
— Alan J Perlis, Epigrams in Programming #58
- Don’t repeat yourself (remove duplication).
- Don’t Mix Different Levels of Abstractions
- Reduce the Attack Surface
“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.”
— Antoine de Saint-Exupery
“Simplicity does not precede complexity, but follows it.”
— Alan J Perlis, Epigrams in Programming #31
According to Kent Beck’s 4 Rules of Simple Design, simple code:
- Passes all the tests.
- Clearly expresses the intent.
- Contains no duplication, even in config.
- Minimizes all the moving parts (functions, objects etc).
Most of the quotes related to simplicity in software resonates well with me:
“There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.”
— C.A.R. Hoare, The 1980 ACM Turing Award Lecture
“Complexity kills. It sucks the life out of developers, it makes products difficult to plan, build and test, it introduces security challenges and it causes end-user and administrator frustration.”
— Ray Ozzie
“Beauty is more important in computing than anywhere else in technology because software is so complicated. Beauty is the ultimate defense against complexity.”
— David Gelernter
“Simplicity is the ultimate sophistication.”
— Leonardo da Vinci
Reliability and Predictability
One of the reasons that I like Functional Programming so much is that it stresses the decoupling of pure functions from functions with side effects. Pure, referentially transparent functions are reliable, consistent and predictable. We should do our best to maximise the amount of pure code we write.
Another reason I like Functional Programming is that it allows me to write simpler code. And some of the greatest minds agree that you need simplicity in order to get reliability:
“Simplicity is prerequisite for reliability.”
— Edsger W. Dijkstra
“The price of reliability is the pursuit of the utmost simplicity. It is a price which the very rich find most hard to pay.”
— C.A.R. Hoare
“The cheapest, fastest, and most reliable components are those that aren’t there.”
— Gordon Bell
Speed and Efficiency
Speed optimisation is listed last, because:
“Premature optimization is the root of all evil.”
— Donald Knuth
But performance is still extremely important:
“If you can do something really fast, really well, people start using it differently. It affects how you work and it affects quality.”
When is a speed optimisation premature, and when is it crucial?
My guideline is that end users should never wait for my software. Given this, I prefer to only do optimisations on pieces of code that has been shown to be bottlenecks in the system, e.g. after doing load testing or profiling.