Low Level Design: Typing Indicator Service

Ephemeral State Design

Typing state is intentionally ephemeral — stored only in Redis, never persisted to the database. There is no value in querying who was typing yesterday.

Redis Key Structure

Key:   typing:{conversation_id}:{user_id}
Value: 1
TTL:   5 seconds

Redis keyspace notifications are enabled. When a key expires, a stopped-typing event is triggered, allowing the system to broadcast that the user stopped typing without any explicit client action.

Typing Start Flow

Client sends typing_start event
  -> Server: SETEX typing:{conv_id}:{user_id} 5 1
  -> Server publishes typing event to Redis pub/sub channel chat:{conv_id}
  -> WebSocket nodes push typing event to all connected participants (excluding sender)

Typing Stop Flow

Client sends typing_stop event  OR  Redis TTL expires
  -> Server publishes stopped event to chat:{conv_id}
  -> WebSocket nodes push stopped event to participants

Debounce Strategy

  • Client sends typing_start every 3 seconds while the user is actively typing.
  • Server-side TTL is 5 seconds — longer than the client interval.
  • If the client disconnects abruptly, the key expires in at most 5 seconds, cleaning up state automatically.

Broadcast via Redis Pub/Sub

PUBLISH chat:{conversation_id} {"type":"typing","user_id":123}

All WebSocket nodes subscribed to that channel receive the event and forward it to clients in that conversation. The sender is excluded from delivery.

Participants List

Participants for a given conversation are read from the ConversationMember table (cached in Redis). Only members receive typing events.

Group Chat Display

To show "Alice and Bob are typing…", the client aggregates active typers received from the server. The server can also provide a batch query:

KEYS typing:{conv_id}:*  -- scan active typers in a conversation
-> returns list of user_ids currently typing

Rate Limiting

Maximum 1 typing_start event per user per 2 seconds is enforced server-side to prevent event spam from misbehaving clients.

State Recovery on Reconnect

When a WebSocket client reconnects, it queries active typers in the current conversation:

SCAN typing:{conv_id}:*
-> returns all user_ids with active typing keys

This restores the typing indicator UI without waiting for new events.

Architecture Summary

Client  --typing_start-->  WS Node  --SETEX + PUBLISH-->  Redis
                                                               |
                                                    Other WS Nodes (subscribed)
                                                               |
                                                    Push to participants

{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “Why is typing indicator state stored in Redis instead of a database?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Typing state is ephemeral and only meaningful in the moment. Redis provides sub-millisecond reads and writes, supports TTL-based auto-expiry, and keyspace notifications for cleanup — making it ideal for transient state that should never be persisted.”
}
},
{
“@type”: “Question”,
“name”: “How does a typing indicator handle abrupt client disconnections?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “The Redis key typing:{conversation_id}:{user_id} has a TTL of 5 seconds. If the client disconnects without sending a typing_stop event, the key expires automatically, triggering a keyspace notification that broadcasts a stopped-typing event to other participants.”
}
},
{
“@type”: “Question”,
“name”: “How is typing indicator spam prevented?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Server-side rate limiting enforces a maximum of one typing_start event per user per 2 seconds. Events beyond this rate are dropped, preventing misbehaving clients from flooding the broadcast channel.”
}
},
{
“@type”: “Question”,
“name”: “How do you show multiple users typing in a group chat?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “The client accumulates typing events from all participants. The server can also scan Redis keys matching typing:{conv_id}:* to return the full list of currently active typers. The UI aggregates these into a message like 'Alice and Bob are typing…'”
}
}
]
}

See also: Meta Interview Guide 2026: Facebook, Instagram, WhatsApp Engineering

See also: Atlassian Interview Guide

See also: Netflix Interview Guide 2026: Streaming Architecture, Recommendation Systems, and Engineering Excellence

See also: LinkedIn Interview Guide 2026: Social Graph Engineering, Feed Ranking, and Professional Network Scale

Scroll to Top