My engineering philosophy (for startups)
What I believed to be true about building software at a startup, as of June 2024.
I wrote this in June 2024 as a one-page summary of how I think about engineering at an early-stage startup. It’s published here as I wrote it, apart from formatting, because it’s the baseline for a follow-up. That post asks which of these still hold now that AI writes much of the code.
Strategic
Nobody buys code
Code exists to support and create the product. Nobody goes to the code store and asks for a meter (or a yard) of code.
If you are writing code, you should know and care about what it does in the product. It pays your salary.
If you are an engineering leader, your team should be making the product better or making the other developers in the company better.
Use boring tech
Boring technology has been around for longer, so it has fewer unknown unknowns. Your company doesn’t exist to solve somebody else’s unknown unknowns.
Your company has a limited amount of attention. Don’t spend it on things that don’t move your company forward.
Boring isn’t old, it’s mature.
Know your trade-offs
Every decision is a trade-off (otherwise it’s not a decision). Capture the trade-offs in writing, so that when something changes you can see whether the assumptions underneath that decision have changed.
Startups aren’t small big companies
Startups exist to find product-market fit. Big companies exist to exploit it.
You need to be able to change things rapidly, and the codebase and architecture must support that. Every architecture implies things about the products built on it, so be aware of the trade-offs.
“Do things that don’t scale” applies to code as well (decouple, decouple, decouple).
Tactical
These are in no particular order. Most of them trade time spent now for much more time saved later, so you can keep a high velocity. How far to take each trade is the decision.
Build a majestic monolith that is designed to be broken up
Build a majestic monolith. Monoliths are easier to build and reason about than microservices (an assumption). They are easier to run locally, easier to deploy and easier to debug.
- Use interfaces for everything, so you can change things quickly.
- Build it so it can be broken up.
- Use a message bus for communication, even when everything is in the same process.
Sort out your metrics and logging early
Finding and resolving problems in production should be as easy as doing it on your dev box. Tracking down problems quickly improves velocity, and it should be easy to find the context around any particular problem.
- Hook up OpenTelemetry, Sentry and the like to gather logs, metrics and traces.
- Make sure all your logs are structured.
Write down, then automate as much as possible
You have limited attention (an assumption). Don’t make yourself remember how to do something.
The first time you do something, write down how you did it. After you’ve done it four or five times, build a tool (if it’s worth it).
Some obvious automations:
- Use GitHub Actions or an equivalent to check pull requests.
- Only let changes reach
mainthrough pull requests. - Use pre-commit hooks to lint and fix styling.
Keep all your infrastructure in scripts and code. Write down how to use those scripts to rebuild your infrastructure, and run them once a year (this also tests your backups).
You’re creating standard operating procedures, making institutional knowledge explicit, and making it possible to delegate the task and do something more valuable.
Assume you’ll have to become SOC 2 compliant
At some point you’ll want to sell to enterprise customers (an assumption), and they’ll want something that shows they can trust you. SOC 2 is the minimum bar that says “we’re professionals”.
Setting it up isn’t much more work than not setting it up when you’re starting out, so do the work then and keep on top of it. SOC 2 isn’t that bad (and just pay for Vanta).
The non-technical parts of SOC 2, like HR policies, are a different can of worms, but they can be outsourced.
Use SOC 2 to improve your process
SOC 2 has very sensible things to say about the software development lifecycle. Lean into it and use it to set up good processes.
“Outsource” everything that isn’t your core business
The cost of SaaS tools can seem to add up quickly, but do some quick maths and the time saved is usually worth it.
Build internal tools
When you identify something that happens a lot, build a tool to make it easier, assuming you can’t buy one.
Internal tools are easier to build than external ones (the UI can be worse), easier to get feedback on (the customer is your colleague), and they help bring different disciplines together.
If it’s slow to build an internal tool, it probably means it’s slow to build your external-facing product.
People, product, process
People follow a process to build a product. The process should let people do their best work, to build the best product. I believe it’s important that everybody knows what is expected of them.
This quickly gets out of the realm of the technical, and there are many ways for process and people to interact. I have some strong opinions here, but they aren’t well enough formed yet to write down.
What about X?
If I haven’t mentioned something you think I should have, let’s talk about it. If something doesn’t make sense, let’s talk about it.
This reflects my thoughts and beliefs at the time of writing, based on my experience and what I was working on.