Loading...
Loading...
Senior · 45 min
Also asked as: Slack, WhatsApp Web, Discord, Microsoft Teams chat, Intercom messenger.
Read it once, then start the timer and work without scrolling.
Design the frontend for a realtime chat application with channels, direct messages, typing indicators, read receipts, and reliable delivery across flaky mobile networks.
Everything below is a model answer. You will get far more out of this drill by attempting it cold first — set the timer, talk through it out loud, and only then compare.
A 45-minute phase plan you can practise against a clock.
Interactive · Drill rehearsal timer
6:00
Time remaining in Clarify and scope.
What you should establish before drawing a single box.
Architecture, data, interfaces, and optimisations — the RADIO body.
The half of the answer most candidates skip entirely.
Naming how the system breaks, and what the user sees when it does, is the cheapest way to signal production experience. Work through these before the interviewer has to ask.
The interviewer will challenge you. Rehearse the concession, not just the defence.
Interviewer
WebSockets are operationally painful — sticky sessions, proxy timeouts, scaling connections. Why not just long-poll or use SSE?
Model response
The operational cost is real and I would not pretend otherwise: persistent connections mean connection-count capacity planning, load balancer idle timeouts to tune, and corporate proxies that terminate them. SSE plus HTTP POST is a genuinely good answer and I would keep it as the fallback path, because it is plain HTTP, works through almost every proxy, and reconnects natively with Last-Event-ID. The reason I still lead with WebSockets is the send path: with SSE every outbound message is a fresh HTTP request with its own TLS and header overhead, and in a chat app the user is sending constantly, not just receiving. On mobile that per-message overhead is the difference between a snappy composer and a laggy one. So the design is WebSocket-primary with SSE fallback, and importantly the message layer above is transport-agnostic so switching does not touch delivery semantics.
Interviewer
You are keeping a durable outbox in IndexedDB. That is a lot of machinery for something the user could just retype.
Model response
For a desktop app on stable wifi, you are right that this is over-built and I would cut it. The case that justifies it is the mobile one: the user types a message in an elevator, hits send, the app is backgrounded, and iOS kills the tab. Without durable persistence that message is simply gone, and the user believes it was sent because they saw it in the transcript. That is the worst possible failure in a messaging product — silent data loss that the user has already been told succeeded. The alternative that avoids IndexedDB is refusing to render optimistically until the server acks, which makes every send feel slow. So the outbox is the price of optimistic rendering being honest. If we dropped optimistic send, I would drop the outbox with it.
Interviewer
Read receipts as a high-water mark are wrong. If someone scrolls up and reads an old message, or jumps to a link, your model says they read everything in between.
Model response
That is exactly right, and the model is a deliberate approximation rather than a correct one. A precise per-message read set is the accurate model, but it costs one row per user per message, so a 500-member channel with 100k messages is fifty million read records, and the write volume during a scroll is brutal. The high-water mark collapses that to one row per user per channel. The place the approximation actually bites is jump-to-message, where a user lands deep in history and the naive implementation marks everything below as read — so I would explicitly not advance the watermark on a jump, only on continuous scroll from the current mark. For products where per-message accuracy is a real feature, like a compliance-audited messenger, the high-water mark is not acceptable and I would pay for the precise model.
Interviewer
Virtualizing a chat transcript while also supporting edits, deletes, reactions, and variable-height attachments sounds like a bug factory. Why not just cap history to the last 200 messages?
Model response
Honestly, capping is the right v1 and I should have led with it — most users never scroll past the recent window, and a capped list with content-visibility gets you a long way with a fraction of the complexity. You are also right about the specific hazards: bottom anchoring plus prepending older pages plus rows whose height changes when an image decodes is where homegrown virtual lists break, and each of those interacts with the others. What forces the issue eventually is the jump-to-message and search-result flows, because those land the user arbitrarily deep in history and a capped list cannot represent that. My plan would be to ship capped, instrument how often deep history is actually reached, and adopt a maintained virtualization library rather than hand-rolling when the data says it is needed. The part I would build carefully from day one is the normalized store keyed by message id, because that is what makes edits and reactions cheap regardless of which rendering strategy wins.
Specific signals an interviewer is listening for on this prompt.
Turn one attempt into retained skill.