seyshee.dev

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.