The problem
You are building a group chat service where users can send messages to groups, and every member receives messages in order, even if they were offline at the time. The product is launching in multiple countries and expects rapid growth, with groups ranging from a handful to thousands of members. Reliability and message delivery speed are crucial, as users rely on the platform for both social and work-related communication.
You are the lead backend engineer tasked with designing the core messaging backend for this group chat service.
If the design fails, users may miss messages, receive them out of order, or experience long delays, leading to loss of trust and user churn.
Must hold true
- Messages must be delivered to all group members in order, within 1 second.
- Offline users must receive all missed messages upon reconnect.
- System must support up to 10 million daily active users.
- Groups can have up to 10,000 members.
Functional Requirements
What the system must DO.
| Requirement | Priority |
|---|---|
| Users can send a message to a group and all group members receive it. | Must have |
| Messages are delivered to all group members in the same order. | Must have |
| Offline group members receive all missed messages when they reconnect. | Must have |
| Users can join and leave groups. | Must have |
| Users can see which messages they have read. | Nice to have |
Non-Functional Requirements
How well it must do it — latency, availability, scale. Measurable, not vibes.
| Requirement | Priority |
|---|---|
| Messages must be delivered to all online group members within 1 second. | Must have |
| The system must support up to 10 million daily active users and groups up to 10,000 members. | Must have |
| No messages may be lost, even if users are offline. | Must have |
| The service should be highly available, with minimal downtime. | Nice to have |
What you learn by asking
In an interview these facts are NOT volunteered — the candidate earns them with clarifying questions. These are the answers behind this design.
| If you ask about… | The answer |
|---|---|
| Daily active users | Up to 10 million daily active users are expected at peak. |
| Peak message rate | Peak is 5,000 messages per second globally. |
| Average group size | Average group size is 50, but some groups have up to 10,000 members. |
| Message size | Average message size is 500 bytes. |
| Offline duration | Users may be offline for up to 7 days and must receive all missed messages on reconnect. |
| Growth rate | User base is growing at 5% per month. |
Capacity Estimation
Rough arithmetic turns the requirements into engineering decisions — it tells you which parts need scaling machinery and which are trivially easy. Only the order of magnitude matters.
| Quantity | Answer | How it is derived |
|---|---|---|
| Peak total message deliveries per second | ~250K deliveries/sec | Each message is delivered to all group members. At peak: 5,000 messages/sec × average group size 50 = 250,000 deliveries/sec. |
| Total message storage required to retain 7 days of messages | ~151 GB | 5,000 messages/sec × 500 bytes = 2,500,000 bytes/sec. Per week: 2,500,000 × 60 × 60 × 24 × 7 = 1,512,000,000,000 bytes ≈ 1.51 TB ≈ 1,510 GB. |
| Maximum possible deliveries per second (max group size) | ~50M deliveries/sec | If all messages go to max-size groups: 5,000 messages/sec × 10,000 = 50,000,000 deliveries/sec. |
Quick check
Without scrolling back: roughly what did we work out for peak total message deliveries per second?
The architecture, built up
Nobody designs the final diagram in one stroke, so this design is built up one component at a time rather than shown finished. Start with the simplest thing that works, then add each piece and see where it attaches.
Each stage lets the numbers break the design, then fixes exactly one bottleneck with exactly one new component — and at each break you get a chance to call the fix yourself before it is revealed. You can stop reading after any stage and still hold a complete, working system in your head — that is the point.
The simplest thing that works
Start with a client, a single API server, and a database to store and serve messages.
The most basic architecture connects the client directly to an API server, which writes messages to a database and reads them back for delivery.
This works for a small number of users and groups, ensuring persistence and basic delivery, but does not scale or meet latency under load.
The design so far
Where it breaks
With 5,000 messages/sec and 10M users, a single API server cannot handle the load.
Quick check
Which single component would you add to fix that?
One app server is now the weak point
The fix: Add a load balancer to distribute requests across multiple API servers.
A load balancer spreads traffic across several API servers, increasing throughput and availability.
This prevents one server from becoming a bottleneck and supports horizontal scaling.
The design so far
Where it breaks
API servers must deliver up to 250,000 messages/sec (fan-out) at peak, which is not feasible synchronously.
Quick check
Which single component would you add to fix that?
Writing and delivering every message synchronously overloads the API servers
The fix: Introduce a message queue to decouple message ingestion from delivery.
A message queue lets API servers quickly enqueue messages, while dedicated workers handle the heavy lifting of delivering to all group members.
This decoupling improves throughput and resilience to spikes.
The design so far
Where it breaks
At 250,000 deliveries/sec, a single delivery worker cannot push messages to all clients fast enough.
Quick check
Which single component would you add to fix that?
Queue grows too fast for a single worker to keep up
The fix: Add a pool of delivery workers to parallelize message delivery.
Multiple delivery workers can consume from the queue in parallel, each handling a subset of deliveries.
This allows the system to scale horizontally with traffic.
The design so far
Where it breaks
Fetching recent messages for large groups from the database creates high latency and load.
Quick check
Which single component would you add to fix that?
Fetching recent messages for large groups is too slow
The fix: Add a cache—a small, very fast memory holding the most recent messages for popular groups.
A cache dramatically speeds up reads for recent messages, which are the most frequently accessed.
This reduces database load and improves user experience, especially during reconnect or for active groups.
The finished design
The full reference design, with the reason behind every connection. When you practice, you will rebuild this from a palette that includes decoys.
Why each connection exists
- ClientLoad BalancerClients connect to the load balancer for high availability and scalability.
- Load BalancerAPI ServerLoad balancer distributes requests to API servers.
- API ServerMessage QueueAPI server puts messages on the queue for delivery.
- API ServerDatabaseAPI server persists messages and metadata to the database.
- Message QueueDelivery WorkerWorkers consume messages from the queue for delivery.
- Delivery WorkerCacheWorkers update the cache with newly delivered messages.
- Delivery WorkerClientWorkers send messages to online clients.
- ClientAPI Server(optional)Clients may fetch missed messages directly from the API server.
- API ServerCache(optional)API server may fetch recent messages from cache for fast reads.
The design in one glance
Each stage fixed exactly one problem. If you can retell this table from memory, you can derive the whole architecture on a whiteboard.
| The design so far | What broke | The fix |
|---|---|---|
| The simplest thing that works | — the starting point | Start with a client, a single API server, and a database to store and serve messages. |
| One app server is now the weak point | With 5,000 messages/sec and 10M users, a single API server cannot handle the load. | Add a load balancer to distribute requests across multiple API servers. |
| Writing and delivering every message synchronously overloads the API servers | API servers must deliver up to 250,000 messages/sec (fan-out) at peak, which is not feasible synchronously. | Introduce a message queue to decouple message ingestion from delivery. |
| Queue grows too fast for a single worker to keep up | At 250,000 deliveries/sec, a single delivery worker cannot push messages to all clients fast enough. | Add a pool of delivery workers to parallelize message delivery. |
| Fetching recent messages for large groups is too slow | Fetching recent messages for large groups from the database creates high latency and load. | Add a cache—a small, very fast memory holding the most recent messages for popular groups. |
Good to know — What this design leaves out — on purpose
Every real system also needs monitoring and metrics, logging and tracing, alerting and on-call, CI/CD, auth hardening and abuse limits, and a cost model. None of them appear above, and that is a choice: they sit beside EVERY system in much the same shape, so drawing them here would add the same five boxes to every problem in this library while crowding out the part that is actually specific to this one — the path a request takes and where it breaks. This is also why a metrics or monitoring block is a wrong answer in the practice canvas: it is never the thing that makes this design work. In a real interview, name these in one sentence once the data path is settled — "I would put metrics on the cache hit rate and alert when it drops" — and move on. Reaching for them before the core flow is drawn reads as avoiding the question.
API Design
Every operation traces back to a requirement — nothing extra, nothing missing.
| Endpoint | Purpose |
|---|---|
| POST /groups/{groupId}/messages | User sends a message to a group. |
| GET /groups/{groupId}/messages?since={timestamp} | User fetches messages for a group since a given time (for offline catch-up). |
| POST /groups/{groupId}/members | User joins a group. |
| DELETE /groups/{groupId}/members/{userId} | User leaves a group. |
| POST /groups/{groupId}/messages/{messageId}/read | User marks a message as read. |
Quick check
Which one would you call to user sends a message to a group?
Data Model
| Entity | Fields |
|---|---|
| Message | id: string (Unique message ID), groupId: string (Group the message belongs to), senderId: string (User who sent the message), timestamp: datetime (Time message was sent), content: string (Message body) |
| GroupMember | groupId: string (Group ID), userId: string (User ID), joinedAt: datetime (When user joined) |
| ReadReceipt | messageId: string (Message ID), userId: string (User who read the message), readAt: datetime (When message was read) |
Defending the Design
Interviewers close by stress-testing the design. Strong answers follow one shape: which component → what breaks there → the fix and its cost. Here are this problem's probes and the points a strong answer hits:
What is the main bottleneck in your design when delivering messages to large groups, and how would you address it? Delivery worker or message queue can become bottlenecks due to fan-out. Horizontal scaling of delivery workers. Partitioning message queues by group or region.
How does your system guarantee message order for all group members, especially when scaling horizontally? Ordering enforced per group in message queue. Single partition per group or sequence numbers. Workers process messages in strict order per group.
If the database fails, what happens to undelivered messages and how can you prevent message loss? Messages are persisted to the database before delivery. Message queue durability (persistent queue). Replication or backup for database.
What are the tradeoffs of using a cache for recent messages in this architecture? Improves read latency for recent messages. Cache consistency with database is a challenge. Cache adds operational complexity.
Tip — Key takeaway
Designing a scalable group chat system requires careful handling of message fan-out, offline delivery, and strict ordering. Using queues, worker pools, and caching enables high throughput and low latency, but each adds complexity and must be justified by real load.
Common trap — Common mistakes on this problem
Assuming a single server or database can handle all message delivery and storage, leading to scalability bottlenecks. · Ignoring the need for strict ordering, which can result in users seeing messages out of sequence. · Not persisting messages before delivery, risking message loss if a server crashes.
You've seen the whole design — now build it yourself
The practice run walks the same steps, but YOU do the work: gather the requirements, run the numbers, wire the architecture — with an AI interviewer and per-step feedback.