When I first introduced the CCDAO framework, my goal was simple: give frontend developers a practical structure for frontend system design interviews.
Instead of jumping straight into React components or picking a state management library, CCDAO helps us slow down and think through the problem step by step:
C — Collect Information
C — Component Structure
D — Data Modeling
A — API Design
O — Optimization StrategiesI later wrote a deeper dive into the framework, expanding each part with more examples and questions.
But after applying CCDAO to more realistic frontend case studies — especially data-heavy applications like a board application, feed list, typeahead, and optimistic UI — I realized something important:
The most difficult part of frontend system design is often not the component structure.
It is how the frontend represents, changes, synchronizes, and recovers its state.
That means the D and A parts of CCDAO deserve more depth.
So today, I want to refine the framework slightly.
Not to replace the original idea, but to make it more useful for real-world frontend systems.
The updated version still keeps the same acronym:
C — Collect Constraints
C — Component and Interaction Structure
D — Data and State Modeling
A — API and Synchronization Design
O — Optimization, Operation, and ObservabilityThe structure is almost the same, but the meaning is sharper.
The old version was good for organizing an interview answer.
This refined version is better for thinking about production frontend systems, where data changes, users interact, requests fail, and multiple parts of the UI need to stay consistent.
This refined version of CCDAO also reflects how I think about my course, Frontend System Design Essentials.
The course is not just about learning individual techniques like caching, pagination, optimistic UI, or lazy loading. It is about seeing how these decisions connect inside a realistic frontend application.
That is the same reason I find CCDAO useful: it gives us a structure to move from vague requirements to concrete design decisions.
Let’s walk through each part.
The first step is still about understanding the problem.
But I now prefer the phrase Collect Constraints instead of Collect Information.
Because the goal is not to gather random details. The goal is to find the constraints that will shape the design.
For example, if someone says:
“Design a Trello-like board.”
We should not immediately start drawing components.
We first need to ask:
Who are the users?
Is this single-user or collaborative?
How many cards can a board contain?
Do users need real-time updates?
Does it need to work on mobile?
Does it need keyboard accessibility?
What happens if a move fails?
What should happen on a slow network?These questions matter because the answers can completely change the design.
A small internal board for one team is very different from a collaborative board used by thousands of users.
So the first C is about discovering the forces that shape the system.
The second C is still about components, but I would now extend it to include interactions.
A component tree tells us what renders.
But a frontend system also needs to describe what can happen.
For a board application, the component structure might include:
Board
Column
Card
Card detail panel
Add-card form
Edit-card form
Loading state
Error stateBut the interaction structure asks more useful questions:
How does a card move?
Can users move cards with the keyboard?
What happens while the move is pending?
What happens if the move fails?
Does the detail panel stay open after the card moves?
What should screen readers announce?This is especially important because many frontend bugs happen between states, not inside a single component.
The UI is not just a static tree. It is a set of possible interactions and transitions.
This is where I would make the biggest refinement.
Previously, I described this step as Data Modeling. That is still correct, but a bit too narrow.
In frontend system design, we do not only model server data.
We also model UI state, derived state, pending state, optimistic state, and the relationship between them.
So I now call this step:
Data and State Modeling
A useful way to think about it is:
Data modeling decides what the frontend believes.
For a board application, the obvious entities are:
Board
Column
Card
UserBut that is only the beginning.
We also need to model relationships:
A board has many columns.
A column has many cards.
A card belongs to a column.
A card can have an assignee.Then we need to model ordering.
A column does not just contain cards. It contains cards in a stable order.
So we might separate card content from card order:
type BoardState = {
cards: Record<string, Card>;
columns: Record<string, Column>;
cardIdsByColumnId: Record<string, string[]>;
};This gives us a useful rule:
Store entities for lookup.
Store ordered IDs for rendering order.
This is where normalized data becomes practical, not academic.
Normalization helps us avoid duplicated identity.
For example, imagine a card appears in a column and also in a detail panel. If the column renders one copy of the card, and the detail panel stores another copy, moving or editing the card can create stale UI.
The user thinks they are editing the same card.
But the frontend may actually be editing an old copy.
So in the D step, we should ask:
What are the core entities?
What are their stable IDs?
How do entities reference each other?
Does order matter?
What data comes from the server?
What state is local to the UI?
What state is derived?
What state is optimistic or pending?
Can two parts of the UI drift apart?For simple features, D may only mean defining a few data types.
For complex features, D means designing the frontend’s local model of server truth.

Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.