Practice this topic in a realistic system design interview
Suppose you're building a simple to-do list application. If you're the only person using it on one computer, everything can run locally. The interface, application logic, and data all live on the same machine.
But what if you want to access that same to-do list from your phone and laptop? Or share it with a teammate? If every device stores its own copy, the data can easily get out of sync. You might mark a task as complete on your phone, but your laptop still shows it as pending.
To solve this, we need a central place to store the data, where every device can access and update it. That's where a server comes in. The server manages the shared data, while the devices communicating with it are called clients.
This chapter covers how client-server architecture works, how clients and servers communicate, how responsibilities are divided between them, and the benefits and trade-offs of this architecture.
Loading simulation...
Client-server architecture is a model where responsibilities are divided between two types of programs: clients and servers. The client sends a request, and the server processes it and returns a response.
For example, when you open a banking app to check your balance, the app acts as the client. It sends a request to the bank's server, which retrieves your balance and sends it back. The app then displays that information on your screen.
The key distinction is that the client handles user interactions, while the server typically manages shared data and enforces the application's core business rules.
Let's see how this communication works when you open a website.
algomaster.io.200 for success or 404 for a missing resource, along with the requested content.This request-response cycle is the foundation of most client-server communication.
algomaster.io.443.Now let's look at what each side is responsible for, starting with the client.
The client's main responsibility is handling user interactions. It displays the interface, collects input, sends requests to the server, and presents the responses. For example, when you enter something into a search box, the client sends your query to the server and displays the results when they come back.
Clients can take different forms:
So a client isn't necessarily a device with a user interface. It's simply the program making a request.
The server handles incoming requests and typically performs work that needs to happen centrally. This includes managing data, executing business logic, authenticating users, and checking permissions. For example, an e-commerce server might calculate an order total, verify inventory, and process a purchase.
Servers usually listen for incoming connections on specific network ports. An HTTPS server, for instance, commonly uses port 443.
One important principle is that the server should never blindly trust the client.
Suppose an e-commerce application calculates a product's price on the client and sends that amount to the server. A malicious user could modify the request and change the price of a laptop to $1. If the server accepts that number, the user gets the laptop for $1.
A safer checkout request sends only what the user chose, such as { "productId": "laptop-15", "quantity": 1 }. The server looks up the current price itself, applies any discounts it knows are valid, and calculates the total. Any price the client sends is ignored.
That's why the server must independently verify prices, validate inputs, and enforce permissions. The client can perform validation too, especially to provide immediate feedback, like showing that an email address is missing an @. But the server must enforce the rules that matter for security and data integrity.
Keeping shared data and business logic on the server provides several advantages.
Instead of maintaining independent copies, clients access the same underlying data. This makes it easier to keep information consistent across devices. The to-do list from the introduction shows the same tasks on your phone, laptop, and your teammate's computer because they all read from one place.
Sensitive operations and authoritative data can remain on infrastructure you manage rather than relying on users' devices. A mobile app should never contain a database password or a private API key, because anything shipped to a user's device can eventually be extracted. The client talks to your backend, and your backend talks to protected systems.
Suppose you discover a bug in your application's pricing logic. If that logic runs on the server, you can fix and deploy it centrally. Every client benefits without needing to install a new version. This becomes especially valuable when you have millions of users.
The same goes for other server-side changes, such as adding fraud checks or improving search ranking. As long as the API contract stays compatible, old clients keep working.
Not every application divides responsibilities between clients and servers the same way.
A thin client performs relatively little processing locally. It mainly displays information from the server and forwards user actions back for processing. A traditional server-rendered website is a good example: each click asks the server for a new page.
A thick client, also called a fat client, handles more processing locally. A modern single-page web application might manage complex interface logic and update the screen without contacting the server. A video editing application is another example, since much of its processing happens on the user's device.
Thin clients are generally easier to maintain centrally because more logic lives on the server. Thick clients can feel faster and more responsive because many operations don't require a network round trip. However, they introduce more client-side complexity, especially when keeping data and business rules synchronized with the server.
Most applications use a combination of both approaches. Even a thick client should leave the final decision on important rules, like prices and permissions, to the server.
In a typical client-server interaction, the client sends a request, and the server responds. But what happens when the server has new information that the client needs immediately?
Consider a messaging application. When someone sends you a message, the server receives it. But with basic HTTP request-response communication, your application won't receive it until it makes another request.
One solution is polling. The client periodically asks the server whether any new messages are available, for example every few seconds.
The problem is that most requests might return nothing, wasting bandwidth and server resources. And a new message can still wait up to one full polling interval before the client sees it.
For real-time applications, a better option is often WebSockets. With WebSockets, the client establishes a persistent connection to the server. Once connected, both sides can send messages whenever needed. This allows the server to deliver new messages immediately without waiting for another request.
Note that the client still opens the WebSocket connection. The model is still client-server. What changes is that, once the connection is open, the server no longer has to wait for a request before sending data.
Another important design decision is whether the server maintains client-specific information between requests.
The challenge becomes clear when you have multiple servers. If a user's session is stored on server one, subsequent requests may need to reach that same server. Otherwise, the session information won't be available. This makes distributing traffic more complicated, and if server one crashes, the session is lost.
With stateless application servers, any instance can process a request as long as it can access the required shared data. That's why many production systems keep application servers stateless and store shared session data in an external database or distributed cache like Redis.
Client-server systems can also be organized into multiple tiers. A tier is a layer with a clear job. Some tiers are separate deployable services. Others are logical layers inside the same system. The point is to separate responsibilities so the system is easier to secure, change, and scale.
Before looking at client-server tiers, it helps to see the starting point. In 1-tier architecture, the user interface, business logic, and data storage all live together on one machine. There is no remote server involved. The to-do list running on a single computer is a 1-tier application, and so is a local spreadsheet or a single-user desktop accounting tool.
Best suited for local, offline, single-user applications.
In a 2-tier architecture, the client communicates directly with a server, which might be a database server. A common example is an internal desktop app that connects straight to a central PostgreSQL or SQL Server database.
This can work for smaller applications, but it creates challenges around security, maintenance, and business logic. Each client needs database credentials, business rules end up inside every copy of the client, and a schema change can break clients that are already installed.
Suitable for controlled internal environments. It is usually a poor fit for public, internet-facing systems.
A more common approach in modern web applications is 3-tier architecture. Here, the system is divided into three parts:
When a user opens their order history, the client contacts the application server. The server checks that the user is signed in, retrieves that user's orders from the database, and returns them. The client doesn't need direct database access.
This separation makes each tier easier to secure, maintain, and scale independently.
This is the common baseline for web applications, SaaS products, mobile backends, and many internal platforms.
Larger systems may introduce additional layers and components, resulting in what's called n-tier architecture. Teams usually add these layers when the system needs more scale, security, or independent ownership.
Common additions include:
Best for systems where scale, team boundaries, or specialized workloads justify the extra complexity.
One downside of relying on a central server is that it can become a single point of failure.
Suppose a food delivery application runs on one server. If that server crashes during peak dinner hours, customers may be unable to place orders until it's restored.
To reduce this risk, production systems introduce redundancy:
The architecture is still client-server, but the server side now consists of multiple machines working together. From the client's perspective, it still communicates with a single logical service.
Another limitation is that clients and servers communicate over a network. Every request takes time to reach the server, get processed, and return.
For example, a user in India accessing a server in the United States might experience hundreds of milliseconds of network round-trip latency. If an application makes several sequential requests, those delays add up. Five sequential requests at 250 ms each means the user waits more than a second before the screen is ready.
Connectivity is another concern. If a user loses their internet connection, a client that depends entirely on the server may stop working.
Applications address these problems in several ways:
Security is especially important because the client and server operate across a trust boundary. Anything running on the client's device can potentially be inspected or modified. So the server must treat incoming requests as untrusted, even when they come from an official application.
The server applies several checks to each request:
A client can hide a button in the UI, but only the server can reliably prevent the action. Keeping sensitive operations on the server allows these security controls to be enforced centrally.
Once you understand client-server architecture, you'll notice it throughout modern software systems.
For example, an order service might call a payment service, which then contacts a fraud detection service. Here, the payment service acts as a server when handling requests from the order service, and as a client when calling the fraud detection service.
So client and server aren't fixed labels for particular machines or programs. They describe the roles components play during communication.
Client-server architecture works well when an application needs centralized data management, shared business logic, and consistent access control. It makes supporting multiple users and devices easier while keeping important operations under central control.
But there are trade-offs:
Despite these challenges, client-server remains the foundation of most modern applications. Architectural approaches like monoliths, microservices, and serverless often build on the same underlying model. The main difference is how server-side responsibilities are organized, deployed, and scaled.
10 quizzes