When a startup asks us to “build a SaaS,” the real question underneath is: how much architecture do we buy, and how much do we build? Get it wrong in either direction and you’re either over-engineered from day one or painting yourself into a corner by month six.
Here’s how we think about it.
Start with the tenant model
Multi-tenancy is the central decision that shapes everything else. There are three main patterns:
- Shared database, shared schema — all tenants in the same tables, a
tenant_idcolumn on every row. Cheapest to run, hardest to isolate when a customer asks for their data. - Shared database, separate schema — one database, a Postgres schema per tenant. Good balance for most products under 1,000 customers.
- Database per tenant — maximum isolation, real compliance story, but ops complexity scales linearly with customers.
For most early-stage products, shared database with separate schemas is the move. You can migrate to database-per-tenant later if a single enterprise customer demands it; going the other direction is nearly impossible.
Pick boring technology for the data layer
The data layer will outlive every other decision you make. We default to:
- PostgreSQL for relational data — battle-tested, extension ecosystem, and it handles JSON well enough to delay NoSQL for years
- Redis for cache, sessions, and queues (via BullMQ or Upstash QStash for serverless)
- S3-compatible object storage for files — never block your database with blobs
Avoid the temptation to reach for a graph database, time-series store, or vector DB on day one unless your core product genuinely needs them.
The API layer is where you earn trust
Your API design is a commitment. Every endpoint you ship becomes an implicit contract with clients. A few rules we follow:
- Version from day one.
/v1/in the path costs nothing and saves enormous pain. - Return consistent error shapes. Clients will write switch statements on your error codes; make them predictable.
- Pagination is not optional. Any list endpoint that returns unbounded results will eventually take down production.
- Webhooks beat polling. If your customers need to react to events, give them webhooks with signed payloads and delivery retries.
Observability is a feature, not infrastructure
Before you launch, you need three things working:
- Structured logs — JSON to stdout, aggregated in a log platform (Axiom, Datadog, or even Cloudwatch if you’re already in AWS)
- Traces — OpenTelemetry instrumented at the HTTP and DB layer gives you the ability to answer “why was this request slow?” without SSH-ing into a box
- Alerts — error rate, p99 latency, queue depth. Three numbers on a pager. That’s it at first.
The teams that skip this ship faster but debug slower. Over the lifetime of a product, observability pays for itself many times over.
Deploy on boring infrastructure too
Serverless is seductive. It’s also a trap if your workload isn’t a fit — cold starts, 15-minute limits, and stateful connection challenges make it a poor match for most API backends.
Our default stack:
- Fly.io or Railway for API servers (persistent processes, good DX, sensible pricing)
- Vercel or Cloudflare Pages for frontend
- Neon or Supabase for managed Postgres (point-in-time recovery, branching for preview environments)
- GitHub Actions for CI/CD
This stack gets a team from zero to production in a day and handles most products through several million in ARR before you need to care about anything fancier.
The best SaaS architecture is the simplest one that meets your current requirements and doesn’t close off the paths you’ll need later. If you’re making architectural decisions for a new product and want a second opinion, talk to us.