Building Principles
Published on
Here are the building/engineering principles that I generally subscribe to. As always, they are context dependent – different companies have different needs, a pre-product-market-fit startup has different needs than a scaleup, which again differs from an enterprise. You know the drill. And AI is changing the landscape of this again.
We put these together at TwentyThree with Rasmus, Pete and Balder.
Build for Now
Build for the problem and need that we have now. Don’t build for the need that we had yesterday, do not build for the need that we might have in the future.
We should learn from the past, and we don’t know what will happen in the future.
Get Feedback Fast
We won’t know how much value we deliver to a customer until our software runs in production. Find ways to deliver functionality incrementally and often so we can validate its usefulness.
Build for Maintainability and Change
Most software that goes into production stays in production for at least a few years. Core is even more than 10 years old!
Keep this in mind when building things. In a few years, we will be modifying the code we write today so we may as well make it easier on our future selves.
Some aspects to consider:
1) Is the code readable? In three years, will a person without context be able to modify it?
- Be explicit rather than implicit.
- Avoid shared state and mutability.
- Avoid being smart.
- Avoid magic.
- Log everything. Debugging becomes significantly easier.
- Document your code, explaining the why we are writing the code rather than what it is doing.
- Expose metrics, make it observable from the outside.
- Are there tests describing desired behaviour?
- Is naming driven by the domain and how we talk about the product?
- Handle errors. No silent errors.
2) Build independent blocks with high internal cohesion.
- The usual term is “low coupling, high cohesion”.
- You can think about it as “Deep Modules” from Ousterhout’s “A Philosophy of Software Design”.
3) Not all code is created to be equal; there are differences in requirements on library, product and ephemeral code.
- Library code is e.g. how we parse input from users, how we build SQL queries etc.
- Product code is e.g. webinar management, running a webinar etc.
- Ephemeral code is e.g. a migration script, a one-off data collection script etc.
But also:
- Code does not have to be perfect. It has to do what it needs to do and be reasonably maintainable.
- These are guidelines/principles, not dogmas. Absolutely break the guidelines, if it is a conscious, documented decision.
- You do not have to abstract everything. We are very unlikely to e.g. change the
underlying database system, so do not build for doing that every day. Build for a case
that we could do that as a gradual change.
- When it comes to abstraction, wait until you have 2-3 examples that you would like to abstract before jumping to abstraction.
- Yes, this goes against the “DRY” (Don’t Repeat yourself) principle. At least against some interpretations of the DRY principle.