System Design: CAP Theorem From A Frontend Perspective.

Hi, My name is Ayoola. I'm thejuggernaut, here to put a dent in the universe.
I'm a software developer with about 4 years of experience building and maintaining web/mobile applications
System design is often perceived by frontend engineers as something that isn't really necessary, that it primarily pertains to backend engineering or is fundamentally a backend problem. This notion is not only wrong, but dangerous. It often leads to poorly designed frontend systems that, sooner or later, become difficult to improve, extend with new features, remove features from, and scale.
A good analogy is trying to build a multi-storey building without doing the foundational work: investing in proper land and site investigation, architectural design, building planning, structural engineering, professional oversight, and obtaining the required approvals.
You might save time and money initially, but those savings can eventually turn into foundation failure, expensive repairs, water and dampness problems, legal consequences, or, in the worst cases, loss of lives.
The same principle applies to building a frontend system.
Before writing core business logic, you need to understand the problem you're solving, clarify the requirements, identify constraints, think through the architecture, define the major components, and understand how those components will interact.
When this foundational work is skipped, you can end up with a frontend system that is difficult to scale, expensive to maintain, full of technical debt, and constantly requires patching and rework.
Just like a building, a frontend system is only as strong as the foundation it is built on.
What Is System Design?
System design is the process of defining the architecture, components, modules, interfaces, and data flows of a software system to satisfy specified functional and non-functional requirements. It acts as a blueprint for how the system should work before you commit to implementing its core behavior.
At a high level, system design involves several stages:
Clarifying Requirements: Understanding what the system needs to do and the constraints under which it needs to operate. This usually involves separating:
a. Functional requirements: what the system should do.
b. Non-functional requirements: how the system should behave, including concerns such as scalability, availability, latency, reliability, accessibility, and security.High-Level Design (HLD): Defining the major components of the system and how they interact.
Low-Level Design (LLD): Diving deeper into the implementation details, including component boundaries, interfaces, data structures, APIs, state transitions, and internal logic.
The exact problems, components, and trade-offs discussed by frontend and backend engineers can be different, but they are not separate from each other. They are different perspectives on designing the same system.
For backend engineers, system design discussions often focus on APIs, databases, distributed systems, caching, queues, scalability, fault tolerance, and data consistency.
For frontend engineers, the discussion often revolves around application architecture, state management, component design, rendering strategies, performance, accessibility, user interactions, client-side data flow, and how the application communicates with backend services.
The distinction is therefore not that one is doing system design and the other isn't. The distinction is which part of the system they're responsible for designing.
And this is where understanding backend system design concepts becomes valuable for frontend engineers. You don't necessarily need to become a backend engineer to build a good frontend. But understanding how backend systems behave gives you a better mental model for the systems your frontend is consuming and interacting with.
Many backend system design concepts have direct analogues in frontend engineering:
Backend concept Frontend application:
Consistency models ––> UI state, server state, optimistic updates
Eventual consistency ––> Optimistic mutations, background synchronization
Retries & backoff ––> API requests, uploads, mutations
Circuit breakers ––> Graceful degradation when APIs or services fail
Caching ––> Client-side data caching and stale-while-revalidate strategies
Concurrency control ––> Preventing race conditions between UI interactions and requests
Fault tolerance ––> Handling partial failures without taking down the entire application
This brings us to one particularly interesting concept: the CAP theorem.
CAP Theorem
The CAP theorem, also known as Brewer's theorem after computer scientist Eric Brewer, is a principle about distributed systems that describes a fundamental trade-off between Consistency, Availability, and Partition Tolerance. It states that a distributed system cannot simultaneously guarantee all three properties when a network partition occurs. The three properties are:
Consistency (C): Every read receives the most recent write, or an error. In other words, all nodes in the system behave as though there is a single, up-to-date copy of the data.
Availability (A): Every request receives a response, even if that response may not contain the most recent data.
Partition Tolerance (P): The system continues to operate despite communication failures or network partitions between nodes.
The important part of CAP is the relationship between these guarantees during a partition.
A network partition means that parts of a distributed system can no longer reliably communicate with each other. When that happens, the system has to make a fundamental choice: prioritize consistency or prioritize availability.
And while CAP is traditionally discussed in the context of distributed backend systems, the underlying ideas have surprisingly useful implications for frontend engineers.
Because the moment your frontend communicates with remote services, deals with asynchronous state, performs optimistic updates, handles stale data, retries failed requests, or continues operating while a network connection is unreliable, you are dealing with many of the same fundamental problems.
The frontend may not be responsible for designing the distributed database underneath the application. But it is responsible for deciding how the user experiences that distributed system.
Consistency vs Availability: Which One Should You Prioritize?
The important thing to understand about CAP is that neither consistency nor availability is inherently better than the other. The right choice depends on the problem you're solving and, more importantly, what happens when the system is temporarily unable to provide both.
Some systems cannot afford to show users stale or conflicting information. In those systems, consistency is more important than remaining available. Other systems can tolerate stale data or temporary inconsistencies. In those cases, keeping the application available and responsive can be more valuable than guaranteeing that every piece of data is immediately consistent.
When Consistency Should Take Priority
Consider a trading system. A trader is watching a market move in real time. The price, order book, and available liquidity are changing constantly. They use that information to decide when and where to place a trade. Now imagine the system is showing a price that's several seconds old. The interface may still be available and responsive, but the information the trader is acting on is no longer accurate. That can become a serious problem.
The same principle applies to: Payments system(A retry shouldn't create a duplicate transaction), Inventory system (Two users shouldn't purchase the last item), Ticketing (Two people shouldn't receive the same seat.), Permissions (A revoked user shouldn't continue accessing protected resources because one service has stale authorization data.)
Here, stale data isn't just inconvenient. It can cause business correctness problems. Temporarily rejecting, delaying, or preventing an operation may therefore be safer than allowing an action based on state the system cannot guarantee is current.
When Availability Should Take Priority
Now consider a social media feed. If the server is temporarily unreachable, does the application need to stop working because it can't guarantee the feed is perfectly up to date? Probably not.
It can show cached content while reconnecting. You might see a post that's a few seconds old, but the application remains useful. The same idea applies to news feeds, content platforms, search, and analytics dashboards. Here, slightly stale data is often preferable to no data at all. This is where caching, optimistic updates, background synchronization, and offline-first architectures become useful.
Where Frontend Engineers Come In
This trade-off eventually becomes a frontend problem. Imagine a trader looking at a price displayed in the UI. Should the interface continue showing the last known price when the connection is lost? Or should it clearly indicate that the data is stale and prevent certain actions?
That isn't simply a UI decision. It depends on the consistency guarantees of the underlying system and the consequences of acting on stale state.
The same applies to optimistic updates. Imagine a user changes their name from "John Doe" to "John Smith" and clicks Save. Do we wait for the server before updating the UI, or immediately show "John Smith" and send the request in the background?
The second approach is an optimistic update. It's faster, but what happens if the request fails? Now the frontend and server disagree. We need a strategy: retry, rollback, reconcile, or ask the user to resolve the conflict.
These are distributed-systems problems, but the frontend determines how users experience them.
CAP Theorem Is a Mental Model
CAP is less about asking whether consistency or availability is more important and more about understanding the consequences of each trade-off: what happens when the system is inconsistent, and what happens when it is unavailable?
That question leads to better architectural decisions. And for frontend engineers, it changes the questions we ask: What happens when the network fails? Can the user continue working? How stale can our data safely be? Can this operation be retried safely? What happens when the client and server disagree?
These aren't just backend questions.
They are system-design decisions expressed through the interface. System design isn't only about how a system behaves when everything works. It's about designing what happens when things inevitably go wrong.

