19 february 2026
We often hear that "everything comes at the cost of something." You can't achieve everything at once, much like how you can either be happy or be an engineer. System design follows the same principle: every choice optimises for something while sacrificing something else.
Some high level trade-offs in system design are:
A system is considered scalable if adding more resources results in increased performance. Here, performance means the ability to handle more units of work.
If your system is slow for a single user, you have a performance problem.
If it's fast for one user but becomes slow when multiple users use it simultaneously, you have a scalability problem.
In real-world systems, you rarely choose one over the other. The goal is to strike a balance, optimising both performance and scalability so neither is compromised excessively.
Let's understand this by a real-world example. Consider an API backend serving user requests.
Initially, with a small number of users, the system is fast. Each request is processed quickly, and response times are low. However, as the number of users increases, the same API starts slowing down. Requests queue up, timeouts increase, and users experience lag.
To fix performance, you might optimise queries, add caching, or reduce unnecessary computations.
To fix scalability, you might introduce load balancers, horizontal scaling, or split the system into smaller services.
The trade-off appears when these solutions conflict. Aggressive caching may improve performance but introduce consistency issues. Scaling horizontally may increase throughput but also add coordination and operational complexity.
Latency is how long a single request takes to complete.
Throughput is how many requests a system can handle in a given amount of time.
Since developers practically run on coffee, let's explain this with a coffee shop example.
If the barista makes your coffee instantly, that's low latency. But if there's a long line behind you and the barista can only serve one person at a time, the shop has terrible throughput.
Now imagine the barista starts making coffee in batches, five orders at once. The line moves faster, so throughput improves, but you'll wait longer for your coffee. Your latency just went up.
Systems behave the same way. Techniques like batching, queues, and async processing improve throughput, but often at the cost of higher latency. Optimising for one usually means sacrificing a bit of the other. So the question isn't "how do I maximize both?" It's "which one do I annoy less?"
Consistency means every user sees the same, up-to-date data at the same time.
Availability means the system always responds, even if the data isn't perfectly up to date.
Now, imagine a group chat.
You send a message: "I'm on the way."
One friend sees it instantly. Another friend sees it five seconds later. A third doesn't see it at all because their network dropped.
If the app refuses to show anything until everyone is perfectly in sync, it's choosing consistency but at the cost of availability.
If the app delivers messages whenever it can, even if some users see them later, it's choosing availability but consistency takes a hit.
Most messaging apps choose availability. You'd rather see something than stare at a loading spinner while the system tries to be philosophically correct.
Distributed systems work the same way. When a network partition happens, a system must choose: stay available with potentially stale data, or stay consistent by refusing requests.
This is the essence of the CAP theorem, you can't have both at the same time.
Read a detailed article on CAP Theorem here: https://x.com/pixperk/status/2020510840026484880