Building SaaS that scales: architecture decisions that matter
Scaling a SaaS platform isn't just about adding more servers. Here are the architectural decisions that determine whether your platform can grow.
Scaling a SaaS platform is about more than horizontal scaling (adding more servers). It's about architectural decisions you make early that determine whether your platform can grow to 100 customers, 1,000 customers, or 10,000 customers.
Here are the decisions that matter.
Multi-tenancy: the foundational decision
Multi-tenancy is how you isolate customer data. There are three main approaches:
- Single-tenant: each customer gets their own database (maximum isolation, highest cost)
- Shared database, separate schemas: customers share a database but have separate schemas (good isolation, moderate cost)
- Shared database, shared schema: all customers share tables, isolated by a tenant_id column (lowest isolation, lowest cost)
Most SaaS platforms start with shared schema because it's cheapest. But migrating to separate schemas later is painful. If you expect enterprise customers (who demand data isolation), start with separate schemas.
Multi-tenancy is a one-way door. You can't easily migrate from shared to isolated without a major refactor.
Data model: design for scale
Your data model determines query performance as you scale. Common mistakes:
- Over-normalization: too many joins, slow queries at scale
- Under-indexing: queries scan entire tables, get slower as data grows
- Missing denormalization: computed fields that should be stored, not calculated on every query
- Ignoring soft deletes: deleted records bloat tables and slow queries
We design for the queries we'll run most often:
- List all items for a tenant: indexed on (tenant_id, created_at)
- Get item details: primary key lookup
- Search items: full-text search index (Elasticsearch or PostgreSQL tsvector)
- Aggregate reports: pre-computed in materialized views
API design: versioning from day one
You will need to change your API. Customers will be slow to upgrade. Plan for this:
- Version your API from day one (/v1/, /v2/)
- Support multiple versions simultaneously
- Deprecate old versions with 6-12 month notice
- Use OpenAPI/Swagger for documentation and code generation
We learned this the hard way. A client's v1 API had a design flaw. They couldn't fix it without breaking existing customers. They're now stuck supporting v1 forever.
Background jobs: separate concerns
Synchronous requests should be fast (<500ms). Long-running tasks (sending emails, processing uploads, generating reports) should be background jobs.
We use a job queue (BullMQ, Sidekiq, or Celery):
- Web request: enqueue job, return immediately
- Background worker: process job, update database
- Frontend: poll for completion or use WebSockets for real-time updates
This keeps the web tier fast and lets you scale workers independently.
Caching: the multiplier
Caching is the single most effective performance optimization. Cache at multiple layers:
- Browser cache: HTTP cache headers (Cache-Control, ETag)
- CDN cache: cache static assets and API responses at the edge
- Application cache: Redis for frequently accessed data
- Database cache: PostgreSQL query cache, materialized views
The key is cache invalidation. Use cache tags so you can invalidate all related entries when data changes.
Monitoring: know before users do
You should know about problems before users report them. Monitor:
- Error rates: percentage of failed requests (alert if > 1%)
- Latency: p50, p95, p99 response times (alert if p99 > 2 seconds)
- Throughput: requests per second (alert if 50% drop)
- Resource usage: CPU, memory, disk (alert if > 80%)
- Business metrics: signups, conversions, revenue (alert if anomalies)
Use structured logging with correlation IDs so you can trace a request through all services.
Deployment: automate everything
Manual deployments don't scale. Automate:
- CI/CD pipeline: test, build, deploy on every push
- Blue-green deployments: zero-downtime releases
- Feature flags: deploy code without enabling features
- Rollback procedures: one-click rollback if something breaks
We deploy to production 10+ times per day. Automation makes this safe.
The reality
Scaling SaaS is about making good architectural decisions early and living with them. The decisions you make at 10 customers determine whether you can scale to 10,000 customers.
Invest in the boring stuff: multi-tenancy, data models, API design, monitoring, deployment automation. It's not exciting, but it's what separates platforms that scale from platforms that don't.