Real-Time Patterns
Building real-time features means solving problems that developers have encountered many times before. Rather than reinventing solutions, you can apply proven patterns that handle common scenarios effectively. Understanding these patterns helps you choose the right approach for your specific use case.
Presence Tracking
Presence shows who's currently online in your application — think of the green dots in Slack or the "typing..." indicators in chat apps.
The standard approach uses heartbeats. Clients send periodic pings to the server (typically every 30 seconds). If the server doesn't receive a heartbeat within a timeout period, it marks that user as offline and broadcasts the change to interested parties.
// Client sends heartbeat
setInterval(() => {
ws.send(JSON.stringify({ type: 'heartbeat' }));
}, 30000);
// Server tracks last seen time
// If no heartbeat for 60 seconds, mark offline
This pattern handles network hiccups gracefully — a single missed heartbeat doesn't immediately show someone as offline.
Collaborative Editing
When multiple users edit the same document simultaneously, you need strategies to handle conflicts. Two main approaches dominate this space.
Operational Transform (OT) transforms operations based on what others have done. If Alice inserts "hello" at position 5 while Bob deletes character 3, OT adjusts Alice's operation to account for Bob's deletion.
CRDTs (Conflict-free Replicated Data Types) use data structures that mathematically guarantee consistency without coordination. Libraries like Yjs and Automerge implement CRDTs, making collaborative editing much more accessible.
For most applications, using an existing library beats building your own conflict resolution.
Live Updates
Real-time dashboards and feeds need efficient update mechanisms. The server pushes changes as they happen, and clients update their UI accordingly.
Consider throttling and batching for high-frequency updates. If your stock ticker updates 100 times per second, batching updates every 100ms provides a better user experience than overwhelming the UI.
// Batch updates
let pendingUpdates = [];
setInterval(() => {
if (pendingUpdates.length > 0) {
updateUI(pendingUpdates);
pendingUpdates = [];
}
}, 100);
Notifications
Push notifications to specific users require user-specific channels. Each user subscribes to their own notification channel, and the server publishes to that channel when something relevant happens.
For users who are offline when notifications arrive, store notifications in a database and deliver them when the user reconnects. Implement acknowledgment so you know which notifications were successfully received.
Combining Patterns
Real applications often combine multiple patterns. A collaborative document editor might use CRDTs for editing, presence for showing who's viewing, and notifications for mentions. Start with the simplest pattern that solves your problem, then add complexity as needed.