Architecture and Scale

Client-Server Architecture

The foundational pattern almost everything is built on. Before you can reason about anything more complex, you need to understand the client-server model and its implications precisely.


The Concept Explained

Client-server splits a system into two roles with an asymmetry that defines everything else: the client initiates, the server responds.

The server owns the authoritative state and waits. It does not know its clients in advance, does not contact them unprompted, and holds the data of record. The client knows where the server is, initiates every interaction, and holds a view of the state rather than the state itself.

That asymmetry is not an implementation detail, it is the source of both the model's power and its limits. Because the server is the single authority, correctness is easy to reason about: there is one place where the truth lives and one place that decides. Because the client must initiate, the server cannot push, which is the constraint behind long polling, WebSockets, and every real-time technique built to work around it.

KEY CONCEPT

Client-server is a statement about who holds authority and who initiates, not about hardware. A browser and a web server are client-server, and so are a microservice calling another microservice, an application calling its database, and a mobile app calling an API. Recognizing the same asymmetry at every layer is what makes the pattern worth studying rather than assuming.


How It Works

The interaction is a cycle: the client establishes a connection, sends a request, the server processes it and responds, and the exchange completes. Everything else in the model follows from decisions about where work happens and where state lives.

Thick and Thin Clients

The division of labour between the two sides is a genuine architectural choice with consequences.

A thin client does little beyond rendering. The server holds state, executes logic, and produces output the client displays. Updating behaviour means deploying the server, and every client sees the change at once. The cost is that every interaction requires a round trip, so the experience is bounded by network latency and the server carries all the load.

A thick client holds state and executes logic locally. A single-page application, a mobile app, a desktop client. Interactions feel instant because most do not touch the network, and the server carries less work. The costs are that client versions proliferate and cannot be updated in lockstep, and that state now exists in two places and can disagree.

Thin Client vs Thick Client: Where Work and State Live

Thin client

Server does the work

Application stateOn the server
Business logicOn the server
Interaction costA round trip each time
Deploying a changeServer only, instant for all
Version skewNone
Offline capabilityNone
Server loadHigh, all work lands here
Thick client

Client does much of the work

Application stateClient holds a copy
Business logicSplit across both
Interaction costLocal, most avoid the network
Deploying a changeClient rollout, gradual
Version skewMany versions live at once
Offline capabilityPossible
Server loadLower, mostly data access

The thick client trade has a consequence that catches teams out: once logic runs on the client, you cannot trust it. A client can be modified, so any validation there is a convenience for the user rather than a control. Every rule that matters must be enforced again on the server. Client-side validation improves the experience; server-side validation is the actual system.

Statelessness

A stateless server treats each request independently. Everything needed to process it arrives with it, so any server instance can handle any request.

This is what makes horizontal scaling straightforward: put instances behind a load balancer and add more. A stateful server, which remembers something about a client between requests, requires that client's requests to return to the same instance, which undermines load distribution and turns instance failure into lost user state.

The universal answer is to keep the request-handling tier stateless and push state into a store built for it. Statelessness at the server does not mean the system has no state, it means the state does not live in the process handling requests.

WARNING

The server cannot initiate. This is the pattern's defining limitation and the reason a whole family of techniques exists. If the server has something to tell the client, the client must ask, or a connection must be held open so the server can use it. Long polling, Server-Sent Events, and WebSockets are all responses to this one constraint, and knowing that they are workarounds rather than separate architectures is the useful framing.


System Design Implications

Where it strains as systems grow is the interview question, and there are four distinct answers worth separating.

The server becomes a bottleneck and a failure domain. One server serving all clients is a single point of failure and a capacity ceiling. The standard response is horizontal scaling behind a load balancer, which requires statelessness, and that requirement is why the previous section matters.

Push is impossible without workarounds. Anything real-time fights the model. Chat, live dashboards, notifications, and collaborative editing all need the server to speak first, which the pattern does not permit.

The round trip becomes the experience. Every interaction crossing the network costs at least the latency between client and server, which for a global audience is tens to hundreds of milliseconds and cannot be optimized away. Caching, CDNs, and thick clients all exist to avoid the round trip rather than to speed it up.

Everything the client needs must come from one place. As functionality grows, a single server accumulates responsibilities until it is a monolith serving many client types badly. That pressure is what leads to the patterns in the rest of this course: API gateways, backends for frontends, and service decomposition.

A practical note on trust boundaries. The client is outside your control. Treat every request as potentially hostile, validate on the server, and never rely on the client to enforce a rule. This is obvious stated plainly and violated constantly in designs that assume the mobile app will behave.

PRO TIP

When asked where client-server breaks down, avoid answering only "the server becomes a bottleneck." That is the easy half. The stronger answer adds that the server cannot initiate, which is why real-time features need connection-holding workarounds, and that round-trip latency becomes the floor on interactive responsiveness regardless of how fast the server is.


Tradeoffs and Decision Framework

DimensionThin clientThick client
Perceived responsivenessBounded by round tripLocal, immediate
Server loadHighLower
Deployment controlComplete and instantGradual, versions coexist
Offline useImpossibleAchievable
Trust in client logicNot applicableNone, revalidate on server
State consistencySingle copy, simpleTwo copies, must reconcile
BandwidthHigher, chattyLower after initial load

The framework, in three questions. How interactive is the experience, since anything where round-trip latency per action is unacceptable pushes work to the client? Must it work offline, which forces a thick client and the reconciliation problem that comes with it? And can you tolerate multiple client versions in production simultaneously, because a thick client guarantees them and your API must stay compatible across all of them.

Whichever you choose, keep the server tier stateless. That single decision is what allows the server side to scale at all, and it is far cheaper to establish at the start than to retrofit once sessions live in process memory.


Common Mistakes

Trusting client-side validation. The client is modifiable, so any rule enforced only there is not enforced. Server-side revalidation is mandatory.

Keeping session state in the server process. It prevents horizontal scaling, forces sticky sessions, and turns instance failure into lost user state.

Assuming the server can push. It cannot, without a held-open connection. Real-time requirements need a deliberate transport decision.

Ignoring round-trip latency in the design. A chatty interface making several sequential calls per user action multiplies the latency floor by the call count.

Letting the client and server versions couple tightly. A thick client means many versions live simultaneously, and an API that assumes everyone upgraded will break the ones who did not.

Treating client-server as only about browsers. The same asymmetry governs service-to-service calls, and the same constraints apply there.

Duplicating logic rather than sharing the boundary. Validation frequently needs to exist on both sides. The mistake is letting the two implementations drift until they disagree.


INTERVIEW QUESTION

Explain the client-server model and its tradeoffs. Where does it start to break down as a system grows?