Interview Setup
Interview Prompt
Design a user presence system like WhatsApp or Slack. Show online and offline status, last seen timestamps, typing indicators, and propagate detected status changes to friends within 5 seconds. Treat abrupt disconnect detection as a separate latency budget.
Clarifying Questions (ask before designing)
| Question | Why it matters |
|---|---|
| How many concurrent online users and friends per user must the system support? | If all 200M online users each changed status once, a naive fan out to 500 friends would generate 100B pushes, making update coalescing mandatory. |
| What are the heartbeat interval and offline detection latency requirements? | A 30 second heartbeat with a 60 second TTL expires roughly 60 seconds after the last successful heartbeat. An abrupt disconnect can therefore remain undetected for nearly 60 seconds, plus expiry processing and network or scheduling delay. An optional 75 to 90 second mobile TTL profile or an explicit grace period increases that bound further. The detection interval and the 5 second propagation SLO should therefore be treated as separate budgets. |
| Multi device handling: should a user appear online if any registered device is active? | Multi device presence requires per device keys and an aggregation rule that evaluates a user as online when any device key exists in Redis. |
| Privacy controls: can users hide their last seen timestamp or online status from specific contacts? | The fan out pipeline must filter against block lists and privacy preferences before dispatching updates. |
Scope
In scope
- Heartbeat mechanism with Redis TTL
- WebSocket connection registry
- Presence fan out to friends with optimization
- Coalescing and batching updates
- Last seen persistence in Cassandra
- Multi device presence aggregation
- TTL expiry detection and repair
Out of scope (state explicitly)
- Voice and video calling via WebRTC
- Full Signal end to end encryption protocol implementation
- Content moderation machine learning pipeline
Functional Requirements
Clarify online, offline, and away semantics alongside contact visibility rules. The system requires heartbeat status updates, active friend presence subscriptions, multi device aggregation, and granular privacy controls.
In an interview, note that users frequently close mobile applications without logging out explicitly. Key expiry in Redis via TTL serves as the primary mechanism to detect offline transitions.
- Show online status: Display an active indicator, such as a green dot, for currently connected users.
- Show last seen: Provide contextual timestamps such as "Last seen 5 minutes ago" when a user is offline.
- Real time updates: Propagate detected status transitions to subscribed friends within 5 seconds.
- Typing indicator: Deliver ephemeral indicators such as "Alice is typing..." within active chats.
- Privacy controls: Allow users to hide online presence and last seen timestamps from specific contacts or global lists.
- Multi device presence: Mark an account online whenever any registered device maintains an active connection.
- Idle detection: Transition user status to away after 5 minutes of client inactivity.
Non-Functional Requirements
Detected presence updates must become visible within 5 seconds, while client heartbeats must maintain minimal overhead across hundreds of millions of concurrent connections. Abrupt disconnect detection has a separate bound determined by the heartbeat interval and TTL.
- Low Latency: Propagate detected status changes across the friend network in less than 5 seconds.
- Scale: Support 200M+ concurrent users with an average of 500 friends per user.
- Bandwidth Efficiency: Minimize payload sizes and avoid network flooding with targeted pushes and update coalescing.
- Eventual Consistency: Accept a bounded propagation lag of up to 5 seconds after a status transition is detected.
- High Availability: Achieve 99.99% uptime because presence indicators are an ambient feature expected across all chat surfaces.
Capacity Estimations
Concurrent user volume and friend list fan out directly determine the memory footprint in Redis and the required capacity for WebSocket gateway connections.
| Metric | Calculation | Value |
|---|---|---|
| Concurrent online users | Given | 200M |
| Heartbeat interval | Given | 30 seconds |
| Heartbeats / sec | 200M ÷ 30s heartbeat interval | ~6.7M |
| Baseline Redis key refresh writes / sec | 6.7M heartbeats x 2 keys (device presence + WebSocket registry) | ~13.4M |
| Status changes / sec | Given scenario assumption | ~2M |
| Filtered push attempts / sec before coalescing | 2M status changes x ~15 active chat recipients | ~30M |
| Avg friends per user | Given | 500 |
| Fan out (optimized) | Given | ~15 per change |
Architecture Diagram
Presence operates as a heartbeat driven architecture. Clients send periodic pings every 30 seconds, Redis records active user status keys with an explicit time to live expiration, and verified status changes fan out to subscribed friends over persistent WebSocket connections. Without coalescing and active window filtering, high volume account status changes could trigger massive push storms, making debounce buffering and fan out caps essential.
In an interview, distinguish between online, away, and offline states. Redis key expiration naturally resolves instances where mobile operating systems terminate background applications without a graceful disconnection handshake.
Component Deep Dives
1. Heartbeat Management and Time to Live Expiration
The core presence loop processes client heartbeats, updates in memory key state, and infers disconnection through time to live expiration rather than relying on explicit disconnect frames.
Active clients transmit lightweight heartbeat pings over persistent WebSocket connections every 30 seconds. The server registers the device in user_devices:{uid} and refreshes presence:{uid}:{device_id} with SETEX for 60 seconds, storing online for active clients and away for idle clients. If a client does not refresh the key for 60 seconds after the last successful heartbeat, the device key expires automatically. A 60 second TTL provides a bounded detection interval, but it does not inherently tolerate a missed heartbeat if the next refresh arrives after the TTL, so the hardened mobile configuration can use a longer TTL and explicit grace handling.
For multi device accounts, the server maintains separate per device keys such as presence:{uid}:{device_id}. A heartbeat refreshes the device TTL on every interval. When the device activity state changes between active and idle, or when a device expires or disconnects, the server atomically recomputes the account state and increments its revision only if the aggregate state changes. An account is considered online when any device is online. If at least one device remains connected but all connected devices are away, the account is away. It is offline only when no active device keys remain. At a scale of 200M concurrent users sending heartbeats every 30 seconds, the ingestion layer processes approximately 6.7M heartbeat messages per second. Refreshing both the device presence key and WebSocket registry produces a baseline of approximately 13.4M Redis key refresh writes per second, sharded horizontally across a Redis Cluster.
2. Expiration Detection and Repair
Redis key expiration removes stale device state, but expiration itself does not invoke application logic. The presence service therefore uses an expiry notification path or a periodic repair sweep to detect expired device keys. Notifications are treated as best effort because they are not a durable event log. The detector evaluates all device keys atomically before declaring the account offline, writes the latest last seen record asynchronously only on the account level transition, and publishes the resulting presence update. The repair sweep reconciles missed expirations after Redis failover or notification delivery loss.
3. Fan Out Optimization and Coalesced Delivery
Broadcasting raw presence transitions across social graphs produces immense write amplification that overwhelms network interfaces without aggressive filtering and coalescing.
In a naive implementation, 2M status changes per second multiplied by an average of 500 friends per user results in a crippling volume of 1B pushes per second. The production pipeline applies a multi stage reduction strategy:
- Online Only Filtering: Updates are pushed exclusively to friends who are currently online, reducing the target audience from 500 contacts to roughly 100.
- Active Chat Window Filtering: Among online friends, real time push events are restricted to contacts who currently have an active chat conversation window open with the user, cutting target recipients to approximately 15.
- Debounce Coalescing: The presence service batches and coalesces state transitions over a 5 second debounce window instead of emitting updates per change. If a user rapidly toggles between online, offline, and online within 5 seconds, friends receive only the final converged state.
- Pull on Chat Open: Contacts who open a chat conversation pull fresh presence status on demand upon opening the view, transitioning to real time pushes only while that window remains active. Together, these optimizations slash fan out volume from 500 pushes down to approximately 15 pushes per change, achieving a 33-fold reduction.
4. Last Seen Persistence and Read Paths
Last seen persistence provides historical availability data without incurring database write amplification on the hot heartbeat path.
When a device disconnects explicitly or its Redis presence key expires due to TTL exhaustion, an expiry detector evaluates all registered device keys before declaring the account offline. Only the account level online to offline transition writes the latest last seen record asynchronously to Apache Cassandra under last_seen (user_id UUID PRIMARY KEY, last_active TIMESTAMP, last_active_device TEXT, transition_revision BIGINT). Keeping this write asynchronous prevents high volume heartbeat ingestion from contending with storage I/O.
The read path evaluates status through a two tiered hierarchy. The gateway first queries the derived account presence state in Redis. If active device keys exist, the service returns online or away according to the aggregate state. If no active device key exists, the service queries Cassandra to retrieve the recorded last active timestamp and returns a formatted last seen message. If the user has enabled privacy settings to hide last seen data, the service strips the timestamp and returns only an offline indicator.
5. WebSocket Connection Routing and Gateway Registry
Distributing bidirectional WebSocket connections across thousands of gateway nodes requires an accurate, low latency connection registry.
The presence service maintains a routing registry in Redis keyed by ws_location:{uid}:{device_id}, mapping each user ID and device identifier to their connected gateway server instance. When a status update must be delivered to a recipient, the dispatch worker looks up the recipient's assigned server identifier and publishes the payload to a Redis Pub/Sub channel dedicated to that gateway node.
The recipient's gateway node consumes the message from its dedicated channel and immediately forwards the frame across the user's open WebSocket connection. When a user maintains connections across multiple devices, the dispatcher pushes messages to all registered gateway servers in parallel. On an explicit disconnect, the gateway conditionally deletes the registry entry only when the stored connection ID still matches the closing connection. The registry key carries a 60 second TTL that refreshes with each heartbeat, ensuring that crashed gateway nodes or stale connection records automatically expire without requiring manual reconciliation.
API Design
Service Interfaces and Domain Types
The presence service defines explicit contracts for heartbeat frames, status updates, batch queries, and real time subscription events.
export type UserId = string;
export type DeviceId = string;
export type ServerId = string;
export type EpochMs = number;
export type PresenceRevision = number;
export type PresenceStatus = "online" | "away" | "offline";
export type ActivityState = "active" | "idle";
export type DeviceType = "mobile" | "desktop" | "web";
export interface ActiveDevice {
deviceId: DeviceId;
type: DeviceType;
}
export interface UserPresence {
userId: UserId;
status: PresenceStatus;
lastActiveTimestamp?: EpochMs;
activeDevices?: ActiveDevice[];
revision: PresenceRevision;
}
export interface BatchPresenceRequest {
userIds: UserId[];
}
export interface BatchPresenceResponse {
presences: Record<UserId, {
status: PresenceStatus;
lastSeen?: string;
revision: PresenceRevision;
}>;
}
export interface ClientHeartbeatFrame {
type: "heartbeat";
deviceId: DeviceId;
deviceType: DeviceType;
connectionId: string;
activityState: ActivityState;
clientTimestamp: EpochMs;
}
export interface ServerHeartbeatAckFrame {
type: "heartbeat_ack";
serverTimestamp: EpochMs;
}
export interface SubscribePresenceFrame {
type: "subscribe_presence";
userIds: UserId[];
}
export interface PresenceUpdatePushFrame {
type: "presence_update";
userId: UserId;
status: PresenceStatus;
timestamp: EpochMs;
revision: PresenceRevision;
}Batch Presence Query
Clients execute batch queries to fetch initial presence and last seen data when rendering chat lists or contact screens.
POST /api/v1/presence/batch HTTP/1.1
Host: api.presence.domain.com
Content-Type: application/json
Authorization: Bearer <session_token>
{
"userIds": ["u1", "u2", "u3"]
}
HTTP/1.1 200 OK
Content-Type: application/json
{
"presences": {
"u1": {
"status": "online",
"revision": 42
},
"u2": {
"status": "offline",
"lastSeen": "2026-03-24T12:00:00Z",
"revision": 18
},
"u3": {
"status": "online",
"revision": 77
}
}
}Client Heartbeat and Server Acknowledgment
Connected clients transmit periodic heartbeat frames to maintain active socket leases and refresh Redis presence keys.
{
"type": "heartbeat",
"deviceId": "d1",
"deviceType": "mobile",
"connectionId": "conn-9f2a",
"activityState": "active",
"clientTimestamp": 1710320000000
}Presence Subscription and Push Updates
Clients subscribe to presence updates for visible contacts and receive push events when friends transition state. Each push carries a monotonic revision so clients can discard stale updates and detect gaps that require a fresh read. The server derives the requesting user from the authenticated WebSocket session and validates requested contact IDs against relationship and privacy rules instead of trusting client supplied visibility claims.
{
"type": "subscribe_presence",
"userIds": ["u1", "u2"]
}Common Error Responses
Standardized error responses returned across HTTP endpoints and WebSocket connection handshakes.
400 Bad Request: invalid input, missing required fields, or malformed JSON payload 401 Unauthorized: missing or invalid authentication token or API key 403 Forbidden: authenticated caller lacks required permissions for this resource 404 Not Found: requested resource ID does not exist 409 Conflict: duplicate write or version conflict, retry with a unique idempotency key 422 Unprocessable Entity: syntactically valid request failed semantic business validation 429 Too Many Requests: rate limit quota exceeded, client should honor Retry-After header 500 Internal Error: unexpected server failure, retry safely with an idempotency key 503 Service Unavailable: downstream dependency is unavailable or overloaded, retry with exponential backoff 440 Login Timeout: WebSocket connection session expired, client reconnect is required
Data Model
In Memory Key Schemas (Redis)
Redis stores ephemeral presence keys, per device connections, and active subscriber sets with strict TTL constraints.
presence_device:
key: "presence:{uid}:{device_id}"
type: string
value: "online" # "online" or "away"
ttl: 60s # Optional hardened mobile profile can use 75 to 90s
user_devices:
key: "user_devices:{uid}"
type: set
members: ["d1", "d2"]
repair: "Remove device IDs whose presence keys no longer exist"
note: "The {uid} hash tag keeps related keys on the same Redis Cluster slot"
presence_account:
key: "presence:{uid}"
type: hash
fields:
status: "online"
last_active: "1710320000000"
revision: "42"
update: "Atomically update when the account level status changes"
presence_subscribers:
key: "presence_subs:{uid}"
type: sorted_set
members:
u2: 1710320000000
u5: 1710320005000
u9: 1710320010000
score: "Last subscription refresh time in epoch milliseconds"
cleanup: "Remove stale subscribers by score to avoid unbounded membership"
websocket_location:
key: "ws_location:{uid}:{device_id}"
type: hash
fields:
server_id: "gateway-pod-14"
device_type: "mobile"
connection_id: "conn-9f2a"
ttl: 60s
expiry_detection:
mechanism: "Redis expiry notifications plus periodic repair sweep"
guarantee: "Notifications are best effort, while the repair sweep reconciles missed expirations"Durable History Schema (Cassandra)
Apache Cassandra provides a partitioned, write optimized store for persisting historical last seen timestamps upon account level offline transitions.
CREATE TABLE last_seen (
user_id UUID PRIMARY KEY,
last_active TIMESTAMP,
last_active_device TEXT,
transition_revision BIGINT
);Atomic Multi Device Evaluation Script (Lua)
An atomic Redis Lua script evaluates all registered device keys for a user, derives online, away, or offline state, and increments the account revision only when the aggregate status changes. This prevents an account from being prematurely marked offline while another device remains active.
-- Atomic account presence recomputation after a device expiry or disconnect
-- KEYS[1] is presence:{uid}, KEYS[2] is user_devices:{uid}, and KEYS[3..N] are device presence keys.
-- All keys use the same {uid} hash tag and are supplied explicitly by the caller for Redis Cluster safety.
local presence_key = KEYS[1]
local current_devices_key = KEYS[2]
local has_online = false
local has_away = false
-- KEYS[2] is authoritative for registered devices. The caller expands the current device IDs
-- into KEYS[3..N] so all script-accessed keys are declared to Redis Cluster.
local registered_devices = redis.call('SMEMBERS', current_devices_key)
if #registered_devices == 0 then
has_online = false
has_away = false
end
for i = 3, #KEYS do
local state = redis.call('GET', KEYS[i])
if state == 'online' then
has_online = true
elseif state == 'away' then
has_away = true
end
end
local next_status = 'offline'
if has_online then
next_status = 'online'
elseif has_away then
next_status = 'away'
end
local current_status = redis.call('HGET', presence_key, 'status')
local revision = redis.call('HGET', presence_key, 'revision') or '0'
if current_status ~= next_status then
revision = redis.call('HINCRBY', presence_key, 'revision', 1)
local now = redis.call('TIME')
local now_ms = now[1] * 1000 + math.floor(now[2] / 1000)
redis.call('HSET', presence_key, 'status', next_status, 'last_active', now_ms)
end
return {next_status, tostring(revision)}Fault Tolerance
WebSocket Gateway Failure and Client Reconnection
When a WebSocket gateway node terminates unexpectedly, connected clients detect socket closure and initiate reconnection with randomized exponential backoff. Upon establishing a connection with a new gateway instance, the client issues a fresh heartbeat frame that updates the Redis connection registry. Presence delivery is restored as soon as the client reconnects and registers the new connection. The 60 second presence lease controls stale state cleanup rather than the reconnection latency itself.
Application Force Kill and Clean Disconnect Handling
When a mobile operating system force kills an application or network connectivity drops abruptly, the client cannot emit an explicit offline frame. The architecture handles this through the 60 second Redis device key expiration. Expiry detection runs asynchronously and a periodic repair sweep reconciles missed expiry notifications, so no synchronous cleanup is required on the heartbeat path.
Redis Cluster Outage and Graceful Degradation
If a Redis shard becomes temporarily unavailable during failover, the presence service degrades gracefully by returning an unknown status indicator rather than showing misleading offline markers. Heartbeat refreshes retry with bounded backoff while replica promotion completes. The service avoids unbounded buffering of heartbeat writes so a prolonged outage does not create a memory growth cascade.
Multi Device Offline Synchronization
Users frequently maintain active sessions across multiple devices simultaneously. Disconnecting from a single client must not mark the overall user account as offline. The system executes an atomic Lua script that evaluates all registered device keys. It keeps the account online when any device is online, changes it to away when connected devices exist but all are away, and declares it offline only when no active device key remains. The repair process removes device IDs from user_devices:{uid} when their presence keys no longer exist.
Network Flapping and Heartbeat Jitter Mitigation
Mobile networks frequently introduce packet delay and transient connection drops. As an optional hardened profile, the system can set key TTLs to 2.5 times the 30 second heartbeat interval, typically 75 to 90 seconds. This increases the failure detection bound compared with the base 60 second TTL. The dispatch worker can also apply a 10 second grace period before broadcasting an offline transition to subscribed friends, but that grace period must remain inside the separate propagation budget if the 5 second propagation SLO is strict.
Additional Considerations
Typing Indicator Pipeline
When a user types, the client emits a typing signal throttled to at most once every 3 seconds over the active WebSocket connection. The presence gateway records an ephemeral key in Redis with a 5 second expiration using SETEX typing:{conversation_id}:{user_id} 5 1 and routes the notification directly to conversation participants through Redis Pub/Sub. Because typing indicators represent transient UI feedback, no durable database persistence is required. In group conversations, updates are delivered exclusively to participants who currently have the chat window actively open.
Privacy Model and Access Enforcement
User privacy configurations have a durable authoritative source and are cached in Redis under privacy:{uid} containing rules for presence visibility, last seen timestamps, and typing indicators. Visibility levels include everyone, friends, nobody, and custom exclusion lists. Blocked relationships are maintained using Redis sets via SADD presence_hidden:{uid} {blocked_uid}. Cache invalidation or version checks propagate privacy changes promptly. The gateway strictly validates these permissions at two distinct stages. It checks them during initial batch query evaluation when rendering contact lists and again during real time push distribution before dispatching updates over WebSocket connections. If privacy state cannot be resolved reliably, the system suppresses the update rather than risk disclosure.
Multi Region Presence and Cross Region Fan Out
Presence states are typically homed within the user's primary region to maintain sub 5 second fan out latencies. Cross region gossip protocols that synchronize active states globally introduce significant network overhead, which remains unnecessary unless product requirements mandate near real time global visibility. For cross region contacts, a pull on chat open model provides a clean operational alternative to active global replication.
Related Systems and Core Concepts
User presence forms the backbone of real time collaboration and chat platforms. For deeper exploration of real time transport layers and complementary architectures, explore the related systems and foundational concepts below:
- Real Time Messaging: Explore connection management, message ordering, and delivery receipts in Design Real Time Chat.
- Low Latency State and Caching: Learn key expiration, distributed locks, and pub-sub primitives in Redis Patterns for System Design.
- Decentralized State Dissemination: Examine peer to peer membership and failure detection algorithms in Gossip Protocol.
- Transport Protocols: Contrast HTTP long polling, Server-Sent Events, and bidirectional sockets in Network Protocols: HTTP, gRPC, WebSocket, and DNS.
- Standard Architecture Frameworks: Review horizontal partitioning and fan out strategies in System Design Interview Patterns.
Interview Walkthrough and Time Management
- 25 minute cut
Focus on the core presence loop and fan out reduction unless staff depth is requested.
- Illustrate the core presence loop spanning client heartbeats, Redis key expiration via TTL, and friend fan out delivery (5 min)
- Quantify 6.7M heartbeats/sec and why naive fan out produces 1B pushes per second (6 min)
- Show coalescing: batch friend updates and cap fan out to ~15 pushes per presence change (5 min)
- Separate online, away, and offline states and explain TTL based expiry when apps close without logout (5 min)
- Staff only: privacy controls, presence subscriptions, and cross region consistency (4 min)
- Model presence using periodic heartbeats combined with time to live expirations. The client emits a ping every 30 seconds with its active connection ID and activity state, while Redis executes SETEX for the device state with a 60 second expiration, eliminating direct database writes on the hot path.
- Route status changes through WebSocket gateways subscribed to Redis Pub/Sub channels keyed by user ID or conversation.
- On disconnection, allow Redis device keys to expire naturally. Detect expiry asynchronously through expiry notifications plus a repair sweep rather than running synchronous cleanup on the heartbeat path.
- Enforce privacy at both query time when the API filters blocked contacts and push time when the gateway checks permissions before forwarding status updates.
- Return raw UTC last seen timestamps from the server and let the client format relative phrases such as "2 minutes ago" based on local device timezones.
- Handle typing indicators as ephemeral Redis SETEX keys with a 5 second TTL and a 3-second client throttle, ensuring no database persistence overhead.
- Scale to 200M concurrent users using Redis Cluster sharded by user identifier, distributing active keys evenly across the cluster nodes.
- Avoid the common pitfall of polling a relational database every 5 seconds for every friend's online status, which causes query load to scale at O(friends x users) rather than O(users).
Engineering Trade-offs
Presence Architecture Paradigms: Centralized In Memory vs Decentralized Gossip
Selecting the underlying presence synchronization architecture balances query latency, operational complexity, and infrastructure cost across different user scales.
| Architecture Paradigm | Latency and Scalability Profile | Operational Trade-offs and Best Use Case |
|---|---|---|
| Redis + TTL (Centralized Cluster) ⭐ | Can provide sub millisecond read and write latency within a healthy regional cluster. The 200M user and 20+ shard figures are scenario assumptions. Actual capacity depends on shard hardware, key distribution, payload size, and observed workload. | Recommended for modern messaging platforms. Simpler operational overhead and predictable partitioning by user ID, though cluster failovers require reconnection grace periods. |
| Custom Gossip Protocol (Decentralized P2P) | Balanced gossip topologies can converge in roughly O(log N) dissemination rounds, but actual convergence depends on fan out, churn, and failure detection. | Eliminates centralized storage bottlenecks entirely, but increases network background traffic and introduces complex convergence failure modes during network partitions. |
| XMPP Presence Stanzas (Federated Standard) | Variable latency with XML parsing overhead. A 10M concurrent user point is an illustrative scale threshold, not a protocol limit. | Standardized across federated enterprise chat systems, but XML serialization and naive quadratic room fan out can impose high memory and CPU overhead at consumer scale. |
| Managed Presence (Firebase or Supabase) | Low latency socket hooks managed by cloud infrastructure. Fits low to medium concurrency, with scale and cost depending on the provider and usage pattern. | Enables rapid prototyping with provider managed disconnect handling, while scale, cost, and control over fan out coalescing depend on the provider and usage pattern. |
Timestamp Formatting: Server Side vs Client Side Localization
Formatting presence and last seen timestamps requires deciding where relative string representations are constructed.
The server transmits raw ISO-8601 or UNIX epoch timestamps, delegating string formatting to client applications. Client side localization respects device timezones, internationalization rules, and dynamic relative time counters without invalidating server side response caches. The client formats relative strings according to explicit evaluation windows:
| Elapsed Duration | Client Display Format | Localization Rationale |
|---|---|---|
| Elapsed < 1 minute | "just now" | Avoids confusing 0-minute countdowns while providing instantaneous feedback. |
| 1 to 59 minutes | "N minutes ago" | Maintains high granularity context for recent activity without displaying noisy seconds. |
| Earlier today | "today at 2:30 PM" | Anchors the event to the user's current calendar day using local 12-hour or 24-hour formats. |
| Yesterday | "yesterday at 10:15 PM" | Distinguishes previous day interactions clearly without requiring full calendar dates. |
| 2 to 7 days | "Monday at 3:00 PM" | Emphasizes the day of the week for recent context within the current week. |
| Older than 7 days | "Mar 7" | Condenses older timestamps to compact calendar month and day representations. |
Review
How helpful was this walkthrough?
Click a star to rate. We actively use this feedback to refine and update our system design content.
Discussion
Share your thoughts, ask questions, or help others.