10 principles for good code
It’s always interesting to apply ideas, principles and notions from one field to another. Especially when those two fields are very different.
For instance, software and product design look very different on the surface. While the former is supposedly about pure mathematical rationality, the latter is, at least apparently, about emotions, psychology i.e. about fathoming the messiness of the human mind and the physical world.
Is that true? I don’t think so. Code is written primarily for human to read [1]. Code also usually fixes problems that are coming from the physical world, and thus inherits some of its irrationality.
So, what happens if we apply one of the most regarded designers’ principles to code? Namely, how can Dieter Rams’ ten principles for good design [2] enable us to write better code?
Good code is innovative
The possibilities for progression are not, by any means, exhausted. Technological development is always offering new opportunities for original designs. But imaginative design always develops in tandem with improving technology, and can never be an end in itself.
Some problems our code is solving are very old (e.g. moving people across a city). How can we use software, and the machine's capabilities, to better solve them? Even when we’re already using computers (e.g. to scan signed paperwork), how can we use them to go one step further (e.g. ask for an electronic signature), delivering a better experience to the end user?
Even if you have a well established product that is deemed very innovative today, you’re still at the mercy of a new competitor out-innovating you. The tech sector is full of such examples. Companies that stay successful are the ones which don’t fear to constantly disrupt themselves through technological innovation.
Good code makes a product useful
A product is bought to be used. It has to satisfy not only functional, but also psychological and aesthetic criteria. Good design emphasizes the usefulness of a product whilst disregarding anything that could detract from it.
Code is written to be used. As I wrote in We should not ship code, we don’t code for the sake of coding. We code because that’s how a lot of problems can be solved.
Because code is an intellectual product, its usability needs to be even more an area of focus. Front-end interfaces and back-end API need to provide good affordance, i.e. their capabilities needs to be self-evident for the user.
Using the pareto principle can be useful here: the codebase should stay focused on the problem at stake, with more than 80% of its LOC being about the business problem. Otherwise, those other functionalities need to be put in libraries, or other services.
Good code is aesthetic
The aesthetic quality of a product is integral to its usefulness because products are used every day and have an effect on people and their well-being. Only well-executed objects can be beautiful.
Note that while this obviously applies to the web interface or the mobile app’s interface, it also applies to a back-end API.
Since code is written to be primarily read by human beings, there’s no reason it can’t take inspiration from literature. Describing how code can be made to be aesthetically pleasing would take a much longer blog post, but here’s some ideas:
- Consistency of code style
- Structure showing clarity of thoughts
- Absence of cruft (
TODO
,FIXME
, etc.) - Orthography and grammar for comments
- Appropriate naming
- Conciseness of code, comments, naming, function
- Reusability of concepts and routines
Good code makes a product understandable
It clarifies the product’s structure. Better still, it can make the product clearly express its function by making use of the user's intuition. At best, it is self-explanatory.
Code, more than anything, has a tendency to drift away from the business’ reality. This is one kind of technical debt. When the abstractions that are used in code don’t map the physical world’s reality, they need to be refactored.
One good way to identify when code is impeding a product’s understandability is to document it. If you find yourself having to give too much context about how code and reality differ, then it means code can be improved.
Documentation has a tendency to become out of date, so if you need to invest some time, it’s probably safer to invest it in refactoring the code (taking the opportunity of improving the product as well, refactoring for the sake of refactoring rarely brings enough value) to make it more self-documenting.
Good code is unobtrusive
Products fulfilling a purpose are like tools. They are neither decorative objects nor works of art. Their design should therefore be both neutral and restrained, to leave room for the user’s self-expression.
Code can go too far. Overusing abstraction (here’s an interesting article about how Go is an anti-abstraction language), using fancy data structures, overly complex libraries (for instance, using an ORM where it’s not needed), reinventing the wheel: all of those get in the way of having code that is actually maintainable.
This is another point I’ve made in Maslow’s pyramid of code review. Code that is elegant for the sake of being elegant and at the expense of being correct is just getting in the way, and should be refactored or removed.
Good code is honest
It does not make a product more innovative, powerful or valuable than it really is. It does not attempt to manipulate the consumer with promises that cannot be kept.
Over-engineering a solution is a very powerful temptation. Even when a company is moving really fast, and seems to be very far from over-engineering, it can still invest in the wrong area: invest in the wrong tooling too soon, build something from scratch when there are some better off-the-shelf alternatives, etc.
Good code is lasting
It avoids being fashionable and therefore never appears antiquated. Unlike fashionable design, it lasts many years – even in today’s throwaway society.
More than in any other domain, software development is plagued with short-lived programming libraries, frameworks, patterns. Pick solutions that are battle-tested, and be conservative in your technical choices.
Write tests so that functionality can’t be removed and changed without the future maintainer getting explicit feedback about it.
Good code is thorough down to the last detail
Nothing must be arbitrary or left to chance. Care and accuracy in the design process show respect towards the user.
Edge cases needs to be accounted for, without going into over-engineering. While it’s fine to sacrifice certain use cases when building an MVP, it’s not what makes the end product delightful. When code elegantly accounts for all edge cases (for instance, through using patterns such as strategy), maintenance is easy and edge cases can be fixed as they come, leading to an amazing user experience.
Also, code is not enough. There’s a lot of context around code that needs to be there:
- Documentation
- Monitoring (operational and business monitoring)
- Alerting
- Testing (unit, integration, end-to-end, capacity)
- Logging and ability to be introspected and debugged
Good code is environmentally-friendly
Design makes an important contribution to the preservation of the environment. > It conserves resources and minimizes physical and visual pollution throughout the lifecycle of the product.
Code consumes electricity, which is finite resource. When code is made more performant, it not only has an impact on the customer, but also on the environment.
Good code also makes efficient use of data structure and algorithm, and promote reusability. Too often the same piece of functionality is copy pasted between codebases, leading to huge loss in developer efficiency. They should be put in libraries, and open sourced when possible.
Good code is as little code as possible
Less, but better – because it concentrates on the essential aspects, and the products are not burdened with non-essentials. Back to purity, back to simplicity.
This is my preferred principle. I already talked about it in We should not ship code, but the basic idea is that software engineering is not primarily about writing code. It’s about solving problems. It just so happens that a lot of problems can be solved through code - but that does not mean that code solves everything.
How do we write less code?
- By finding existing, off-the-shelf solution and avoiding the NIH (not invented here) syndrome.
- By focusing on the problem at stake, escaping the YAGNI (you ain’t gonna need it) syndrome.
- By spending enough time thinking about the problem, without writing code, so as to design the simplest solution possible (but not any simpler).
Why having less code is better in the long term is pretty obvious: less maintenance, (usually) more performance, less cognitive load for new developers, etc.
Conclusion
Because code is as much a literary experience as it is a rational undertaking, using principles coming from product design can be useful to think and talk about code.
References:
- [1]: Harold Abelson, Structure and Interpretation of Computer Programs
- [2]: Wikipedia contributors, "Dieter Rams," Wikipedia, The Free Encyclopedia, https://en.wikipedia.org/w/index.php?title=Dieter_Rams&oldid=682540168 (accessed October 1, 2015).
- [3]: Vitsoe at en.wikipedia [CC BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0)], from Wikimedia Commons