In defense of boring backends
Every few months I see a small project with a message queue, three microservices, a cache cluster and a service mesh, serving a hundred users. I understand the appeal — it's fun to build — but it is rarely what the project needs.
Here's the stack I default to, and I've yet to regret it:
- One process. A Go or Python app that serves HTTP and runs background jobs in goroutines or a worker thread.
- One database. Postgres. It is also the queue (
SELECT ... FOR UPDATE SKIP LOCKED), the lock service (advisory locks), the full-text search and the cache for anything that isn't hot. - One reverse server. nginx in front, for TLS and static files.
- One deploy script. Build, copy, restart, check. Keep the previous build to roll back.
When to add something
Add a component when you can name the problem it solves and you've measured that problem. "Postgres is too slow for this query" is a reason to add an index first, a read replica second, and a cache third. "We might need to scale" is not a reason.
What boring buys you
Fewer things to monitor. Fewer things to upgrade. Fewer ways to fail at 3 a.m. And — the part people underestimate — a system that a new person can understand in a day.
The most reliable systems I've worked on were the ones nobody talked about.