← Back to the journal

Engineering

Choosing a Backend Stack for a Small Team

Backend architecture decisions get made in a vacuum more often than any other early technical choice — an engineer picks what they know best, or what's trending, without weighing it against the team's actual size and the product's actual data shape. For a small team, the right answer is usually narrower than the options make it look.

Firebase — Auth, Firestore, Functions, Storage

Firebase remains the fastest path from zero to a working authenticated app with real-time data sync, tight first-party SDKs on iOS and Android, and a genuinely generous free tier for early-stage traffic. Its tradeoffs are real: Firestore's NoSQL document model makes complex relational queries (joins across collections, in particular) awkward compared to SQL, and cost curves can climb quickly once you're at meaningful scale with heavy read/write patterns. Security rules take real care to get right — an over-permissive rule set is a common, quietly serious mistake.

Supabase

Supabase pairs a real Postgres database with Auth, Storage, and Edge Functions on top, open-source and self-hostable if you ever need to leave the managed service. When your data model is genuinely relational — orders that reference customers that reference products, with real joins and constraints — Postgres and row-level security policies are a more natural fit than a document database, and you keep the option to write actual SQL for reporting and analytics later.

Custom Node/Express or FastAPI with managed Postgres

Rolling your own backend gives full control and zero platform lock-in, at the cost of owning authentication, infrastructure, scaling, and operations yourself. This is the right call when your requirements are genuinely unusual — an existing system to integrate deeply with, compliance requirements a managed platform doesn't cover well, or a team that already has backend engineers and infrastructure experience sitting idle. It's rarely the right call purely because it feels more "real" than a managed backend.

Serverless vs. always-on

Cloud Functions, Cloud Run, and Lambda-style serverless compute scale to zero and back up automatically, which is ideal for early-stage, spiky, or low-traffic workloads — you don't pay for idle capacity, at the cost of occasional cold-start latency. A small, always-on server is more predictable in latency but you're paying for capacity around the clock whether it's used or not. For most early products, serverless wins on cost; for latency-sensitive real-time features, an always-on process is often worth the tradeoff.

What actually matters for a one-to-three person team

  • Time to first real user beats architectural purity — a working product with a managed backend today outperforms a perfectly architected one that ships two months later
  • Pick the stack your team already knows well over the one that's theoretically more powerful
  • Avoid microservices at MVP stage — a single well-organized backend is easier for a small team to reason about and deploy
  • Use managed authentication instead of rolling your own — session handling, password resets, and social login all have sharp edges that aren't worth relearning
  • Plan for a migration path, not a rewrite — pick a stack you can grow inside of for a while, not one you'll have to replace at the first sign of real traffic

Observability from day one, not after the first incident

Small teams routinely skip logging, error tracking, and basic uptime monitoring until something breaks in production and there's no record of what happened. This is one of the cheapest investments available — a managed backend's built-in logging plus a lightweight error-tracking tool costs little in setup time and pays for itself the first time a user reports a problem you'd otherwise have no way to diagnose. Treat it as part of the initial build, not a follow-up task for "later."

Authentication complexity grows faster than people expect

A single-tenant app with email/password login is simple regardless of backend choice. The complexity shows up later — adding social login providers, supporting multiple organizations or teams per account, role-based permissions, or single sign-on for an enterprise customer. Managed auth providers (Firebase Auth, Supabase Auth, Clerk) handle most of this progression without a rewrite; a hand-rolled auth system built for the simple case often needs real surgery once a customer asks for SSO or team accounts, which is one more reason to default to managed auth even on a stack you otherwise built yourself.

We run Firebase/GCP for most client products where the data is naturally document-shaped and the team wants managed infrastructure, and Postgres-backed services when the data model is genuinely relational or the client already has backend engineering in-house. Neither is the "correct" default — the product's data shape and the team's existing skills should decide it.

More from the journal