Chat Application Design
Chat system architecture for connections, ordering, presence, and message delivery.
A production chat system looks simple at the surface because the user model is simple: connect, send a message, receive it on every relevant device, and keep history in order. The backend is harder because those requirements pull in different directions. Low latency wants direct long-lived connections. Durability wants storage before delivery. Multi-device sync wants replay. Ordering wants a stable sequencing rule. Presence wants fast expiry and constant updates.
Most chat systems start with a stateful edge layer. After login, the client opens a long-lived WebSocket or a similar bidirectional connection to a gateway. That connection is usually tied to an authenticated session, device ID, and conversation subscription set. The gateway should not own durable state. Its job is to terminate connections, enforce rate limits, and forward events to backend services. This separation matters because connection count scales with online users, while message storage scales with total traffic and retention.
Presence is often split into its own service because it changes frequently and tolerates eventual consistency better than message history. Clients send heartbeats, the service keeps a short-lived lease such as "online until T", and friends or guild members subscribe to updates. Failure mode matters here. A missed heartbeat should not mark someone offline immediately, or normal mobile network churn will cause presence flapping.
The core message path usually has four steps. First, the sender submits a message with a client-generated idempotency key so retries do not create duplicates. Second, a sequencing component assigns the authoritative server-side message ID. Third, the message is persisted in a durable store partitioned by conversation or channel. Fourth, the system fans the message out to currently connected recipients and queues it for offline delivery if needed. Writing before fan-out avoids the common failure where a recipient sees a message that later disappears after a crash.
Ordering needs precision. Users usually expect messages in a conversation to appear in one order on all devices. The simplest way to provide that is to define a monotonically increasing sequence per conversation or channel partition. Global ordering across the entire product is usually unnecessary and expensive. Once group chat becomes large, partition choice becomes an architectural constraint. A very large public channel can turn one partition into a hotspot, so systems like Discord often separate smaller direct-message workloads from high-fan-out guild traffic.
Offline sync is another distinct problem. A mobile device that reconnects after an hour does not want the whole account history again. It wants "all events after my last acknowledged sequence". That means the server needs retained history and a per-device checkpoint or a token that maps to one. This replay path is why durable logs and append-friendly storage matter even for chat products that are not marketed as stream systems.
Notifications sit beside, not inside, the core delivery path. If the recipient is connected on an active device, the gateway pushes the message directly. If not, a push notification service receives a compact alert that tells the device to wake up and sync. Push is only a hint. The source of truth stays in the message store, because mobile push delivery is not guaranteed and can be delayed or collapsed by the platform.
Security and abuse controls are part of the architecture, not decoration. End-to-end encryption changes what the server can inspect and how key distribution works. Rate limiting, spam detection, attachment scanning, and block lists all need enforcement points before fan-out. So do membership checks for channels and servers.
A good chat design is therefore not one giant service. It is a set of specialised paths: connection handling for latency, sequencing for order, storage for durability, sync for replay, presence for liveness, and push for offline wake-up. The hard tradeoff is deciding which guarantees really need to be strong and which can be merely fast enough.