Building for Scale: Backend Architecture Decisions That Actually Matter
Most startups over-engineer early and under-engineer where it counts. Here are the backend decisions that will save you from a painful rewrite at 100k users.
We’ve built backends that started as simple REST APIs and grew to handle hundreds of thousands of daily active users. We’ve also seen backends that crumbled under load because the foundational decisions were wrong.
Here’s what we’ve learned.
The Rule: Solve Today’s Problem, Design for Tomorrow’s Scale
The most common mistake is building distributed systems infrastructure for a product with 200 users. Microservices, event queues, distributed caches — these solve real problems, but only at real scale. Early, they slow you down and add failure points without benefit.
Build a clean monolith first. Split only when you have a demonstrated reason to.
Decision 1: API Design — REST vs GraphQL
For most products, REST is the right choice. Simpler to implement, easier to cache, better tooling support, and every developer already knows it.
Choose GraphQL if:
- Your clients (mobile, web) need very different data shapes
- You have a complex graph of related data
- You’re building a developer-facing API platform
We’ve shipped both. For client products, REST with thoughtful resource design wins almost every time.
Decision 2: Database Architecture
Start With One Database
Most early products need one well-configured database. Not a cluster, not sharding — just one reliable instance with proper indexing.
MongoDB vs PostgreSQL: The Real Answer
- MongoDB: When your data model is evolving fast and you need flexibility. Excellent for document-heavy data, real-time aggregations, and when speed of iteration matters more than query complexity. Our default for products in early stages.
- PostgreSQL: When you have complex relational data, need ACID transactions across multiple tables, or require advanced SQL queries. Add it when you need it, not before.
Redis From Day One
Redis for caching is one of the highest-ROI additions to any backend. A well-placed cache layer can reduce database load by 80% and cut API response times from 200ms to under 10ms. We add Redis to every production stack.
Decision 3: Authentication Architecture
Use an established pattern, not a custom one. We use:
- JWT tokens for stateless API auth
- Refresh token rotation for security
- Redis for token blacklisting and session management
- Bcrypt for password hashing (never MD5, never SHA1)
Third-party options like Auth0 or Firebase Auth are fine for MVPs. Plan your migration path if you anticipate needing custom logic.
Decision 4: Async Processing
Not everything should happen in the HTTP request cycle. Email sending, push notifications, report generation, image processing — these should be background jobs.
Our stack:
- Bull/BullMQ with Redis for job queues
- Node.js workers for job processing
- Dead letter queues for failed job handling
- Retry logic with exponential backoff
Getting this right early prevents a class of bugs where your API is slow because it’s doing too much synchronously.
Decision 5: Caching Strategy
Three levels of caching, applied in this order:
- In-memory caching (Node.js Map or LRU cache) — for reference data that changes rarely
- Redis caching — for user-specific data, expensive query results
- CDN caching — for static assets and public API responses
Define TTLs explicitly. Stale data bugs are harder to debug than performance problems.
Decision 6: Error Handling & Observability
You cannot improve what you cannot observe. From day one:
- Structured logging with correlation IDs (we use Winston or Pino)
- Error tracking with Sentry — know about errors before your users report them
- API response time monitoring — set a P95 latency budget and track it
- Health check endpoints — mandatory for any deployed service
What Scale Actually Looks Like
At 10k DAU, your bottleneck is almost certainly database queries, not your Node.js code. Profile your slowest endpoints, add indexes, add caching.
At 100k DAU, you might need read replicas or sharding. Not before.
At 1M DAU, now you’re thinking about horizontal scaling, geographic distribution, and service decomposition. This is a good problem to have.
The Architecture We Use
For most of our client products:
Client Apps (React Native / React)
↓
Load Balancer
↓
Node.js API (TypeScript + Express)
↓
Redis Cache Layer
↓
MongoDB (Primary + Replica)
Background Jobs (BullMQ)
Simple, proven, and capable of getting you to millions of users without fundamental changes.
If you’re designing a backend for a new product and want an expert sanity-check, let’s talk.