Server architecture & where the backend runs
Serverless vs always-on vs managed platform, and how to structure a backend so it stays understandable — the decisions, not the deep patterns.
“Backend” isn’t one shape. It might be a function that wakes up per request, an always-on server, or a managed platform that hands you a database and auth out of the box. Picking right — and structuring the code so it stays understandable — is a handful of decisions, not deep theory. This chapter is those decisions. (Architecture and scaling depth: 2.4, 2.7, 6.1.)
Three places a backend runs
Match the runtime to the workload, not the hype — and know the same CRUD code can usually run in any of them. Serverless functions spin up per request and scale to zero: ideal for spiky traffic and simple APIs, but each call can have a cold start and nothing stays in memory between calls. An always-on server (Express, Fastify) fits steady traffic, long-lived connections (websockets), and background work — at the cost of managing scaling and uptime. A managed platform / BaaS (Supabase, Firebase) hands you a database, auth, and APIs so you can ship now — trading control and portability for speed.
Statelessness is what lets you scale
The split that trips people up: serverless functions don’t stay running between requests — so there’s nowhere to keep a websocket open or hold something in memory. More generally, a stateless server keeps no per-user state in memory; everything it needs comes in the request, and shared state lives in the database or a cache. That’s not a limitation to fight — it’s the property that lets you run many identical copies behind a load balancer and scale horizontally. If your code “remembers” something in a local variable between requests, it breaks the moment there’s a second instance.
Keep it understandable: layers, and start monolithic
Inside the app, a little structure goes a long way: a route receives the request, a handler validates and orchestrates, a service holds the business logic, and a data layer talks to the DB. The smell to avoid is the fat handler that does validation, business logic, and SQL all in one function — it’s where bugs hide and the AI’s first draft usually lands. At the system level, start with a monolith: one deployable app is far simpler to build, debug, and deploy. Split into microservices only when you have a concrete reason (independent scaling, separate teams) — the operational cost is real. A BFF is the one split worth knowing early: a thin API shaped for a single client.
| Workload | Reach for | Watch out |
|---|---|---|
| Spiky / simple API | serverless functions | cold starts; no in-memory state |
| Websockets, long jobs, steady load | always-on server | you own scaling + uptime |
| Ship a backend fast, small team | managed / BaaS | lock-in; less control |
| A new product, unsure of scale | a monolith | don't pre-split into microservices |
01 Learning objectives
0 / 6 done02 Curated reading
03 Knowledge check
- 01easy
For a brand-new app of uncertain scale, the sensible default architecture is…
- 02medium
Why is a websocket server awkward to run as a serverless function?
- 03medium
What property lets you run many identical server instances behind a load balancer?
04 Interview questions
browse all ↗What gets asked on this topic — tap a card for how to approach it, the follow-ups, and the trap. Company tags are best-effort & sourced.
-
Why is “start with a monolith” usually the right call instead of microservices?
A monolith — one deployable app — is far simpler to build, run, debug, and deploy: one codebase, one deploy, in-process calls, easy local development. Microservices add real operational cost: network calls between services, distributed failures, separate deploys, and harder debugging. You take that on only when you have a concrete reason — independent scaling of a hot component, or separate teams owning separate services. Splitting too early buys you distributed-systems problems before you have the problems microservices solve.
What a strong answer coversMonolith = one app: simpler to build, deploy, and debug.
Microservices add network calls, distributed failures, ops overhead.
Split only for a concrete reason (independent scaling, team boundaries).
Premature splitting buys distributed problems without the payoff.
Follow-ups they push on- What's a sign you've actually outgrown a monolith?
- What is a BFF, and where does it fit?
Red flag Letting an AI scaffold five services for a small app — you inherit the complexity of distribution with none of its benefits.
source: Martin Fowler — MonolithFirst ↗