Engineering the Real-Time Social Graph: A Comprehensive Architectural Analysis of Instant Messaging Systems


1. Introduction: The Real-Time Imperative

The ubiquity of instant messaging has fundamentally altered the expectations for digital communication. Users no longer tolerate the delay inherent in email or the page refreshes of early web forums; they demand an experience that mimics face-to-face interaction, where presence is felt, and responses are perceived as instantaneous. This shift has necessitated a radical departure from traditional web architecture, moving engineers away from stateless request-response models toward persistent, stateful, and event-driven distributed systems.

At the scale of platforms like WhatsApp, Discord, or Slack—serving hundreds of millions of concurrent users—the engineering challenges transcend simple data movement. Architects must contend with the "C10M" problem (handling ten million concurrent connections), the physics of network latency, the erratic nature of mobile networks, and the logical paradoxes of distributed time. A message sent is not merely a packet of text; it is a transactional event that must be ordered, delivered, persisted, and acknowledged across a fractured global network, often within milliseconds.

Furthermore, modern messaging experiences are defined not just by the text delivered, but by the ephemeral signals that accompany it. The "Typing..." indicator, the "Online" status dot, and the "Read" receipt create a sense of copresence that is technically expensive to maintain. These features generate a "signaling storm"—a volume of ephemeral events orders of magnitude larger than the message data itself. Managing this throughput without overwhelming backend infrastructure requires rigorous client-side optimization, such as throttling and debouncing, and highly specialized backend architectures.

This analysis provides an exhaustive technical examination of the "zero to hero" journey in building such systems. It explores the transition from simple polling to WebSocket-based gateways, the optimization of ephemeral event streams, the concurrency models (Erlang Actor Model vs. Go CSP) that make massive scale possible, and the complex distributed algorithms required to identify and resolve race conditions in real-time environments.

2. The Transport Layer: Protocols and Persistent Connections

The foundation of any real-time system is the transport layer. In the context of the web and mobile applications, this layer is responsible for establishing and maintaining the conduit through which data flows. The historical dominance of HTTP, designed for document retrieval, proved insufficient for the bidirectional demands of chat.

2.1 The Limitations of the Request-Response Model

Traditional HTTP functions on a client-initiated basis. The client sends a request header, the server processes it and returns a response, and the connection is typically closed or returned to a pool. For a chat application, this model forces the client to "poll" the server to check for new messages.

2.2 WebSockets: The Standard for Bidirectional Communication

The WebSocket protocol (RFC 6455) was standardized to solve the overhead of HTTP polling. A WebSocket connection begins its life as a standard HTTP request with an Upgrade: websocket header. If the server supports it, it responds with 101 Switching Protocols, and the connection transitions from a request-response model to a raw, full-duplex TCP stream.

Technical Advantages:

2.3 MQTT: Optimizing for the Edge and IoT

While WebSockets are dominant in browser environments, the Mobile and IoT sectors often favor MQTT (Message Queuing Telemetry Transport). MQTT is a lightweight, binary protocol designed for unreliable networks and constrained devices.

Architecture Differences:

Facebook Messenger and WhatsApp have historically utilized variations of MQTT (or protocols inspired by it) to minimize battery drain on mobile devices, leveraging its small packet size to reduce radio wake time.

2.4 Server-Sent Events (SSE): The Unidirectional Alternative

Server-Sent Events (SSE) allows servers to push updates to clients over a standard HTTP connection. Unlike WebSockets, SSE is unidirectional—data flows only from server to client.

2.5 Comparative Analysis of Transport Technologies

The following table summarizes the technical trade-offs faced by architects when selecting a transport layer.

Protocol Directionality Header Overhead Reconnection Logic Binary Support Best Use Case
WebSockets Full Duplex Low (2-14 bytes) Manual Implementation Required Native High-frequency interactive apps (Chat, Gaming)
MQTT Pub/Sub Very Low (2 bytes) Built-in (Keep-Alive/LWT) Native Mobile apps, IoT, Battery-constrained environments
SSE Server → Client Low (Text stream) Automatic (Browser handled) Base64 Encoded Only Notifications, Live Feeds, Dashboards
Long Polling Half Duplex High (HTTP Headers) Manual Implementation Required Native Legacy fallback, Corporate firewalls blocking WS
WebTransport Full Duplex Low (QUIC based) Manual Native Next-gen applications requiring unreliable datagrams (Video)

Insight: While WebSockets are the default choice for web-based chat, the most resilient architectures often implement a "protocol downgrade" strategy. The client attempts to connect via WebSocket/QUIC; if blocked by a corporate firewall or proxy, it automatically degrades to Long Polling to ensure connectivity, albeit with higher latency.

3. Client-Side Engineering: The Typing Indicator Case Study

The typing indicator ("Alice is typing...") is a feature that appears trivial but presents a massive scaling challenge. It represents ephemeral, high-frequency state. Unlike a message, which is immutable and valuable, a typing event is transient and loses value within seconds. If a user types 300 characters a minute, sending a packet for every keystroke would result in 300 requests per minute per user. For 1 million concurrent users, this would generate 5 million requests per second—a DDoS attack by design.

To manage this, client-side engineering must focus on signal reduction through Throttling and Debouncing.

3.1 The "Signaling Storm" and Rate Limiting

The primary goal is to decouple the physical keystroke rate from the network packet rate. We do not need to know what key was pressed, only that activity is occurring.

3.1.1 Debouncing: The Waiting Game

Debouncing ensures that a function is only triggered after a specific period of inactivity.

3.1.2 Throttling: The Regular Pulse

Throttling ensures that a function is executed at most once in a specified time interval, regardless of trigger frequency.

3.1.3 The Hybrid Implementation Strategy

A robust production implementation combines both techniques to manage the state machine effectively.

  1. Event: First Keystroke (t=0):
    • Condition: isTyping == false
    • Action: Send TYPING_START packet immediately. Set isTyping = true. Start Throttle Timer (e.g., 3s).
  2. Event: Continuous Typing (t < 3s):
    • Action: Ignored by Throttle logic. Internal LastKeystrokeTime updated locally.
  3. Event: Continuous Typing (t = 3s):
    • Condition: User still typing.
    • Action: Throttle timer expires. Send TYPING_RENEW packet. Reset Throttle Timer.
  4. Event: User Stops Typing:
    • Action: A separate Debounce Timer (e.g., 1000ms) runs on every keystroke.
    • Trigger: When user stops, Debounce Timer expires.
    • Output: Send TYPING_STOP packet. Set isTyping = false. Cancel Throttle Timer.

This logic ensures the network sees a low-frequency pulse (1 packet every 3s) during activity and a precise stop signal, minimizing load while maximizing UI responsiveness.

3.2 Optimizing Payloads: JSON vs. Protobuf

Once the frequency of events is minimized, the size of each event becomes the next optimization target.

JSON (JavaScript Object Notation):

Protocol Buffers (Protobuf):

Insight: High-scale apps often use JSON for the control plane (creating groups, updating profiles) where flexibility is needed, but switch to Protobuf or custom binary formats for the data plane (messages, presence, typing) where volume is high and schema is stable.

4. Gateway Architecture: Managing Millions of Connections

Scaling a web server to handle millions of persistent connections is fundamentally different from scaling a REST API. In a REST architecture, a server is stateless; any request can be routed to any server. In a chat architecture, the connection is stateful. If User A is connected to Gateway-01, Gateway-01 is the only server that can push a message to User A over that specific WebSocket.

4.1 The Gateway Service Pattern

The Gateway Service (or Connection Layer) is responsible for holding the open TCP/WebSocket connections.

4.2 Handling Reconnections and the "Thundering Herd"

A critical failure mode in messaging architectures is the "Thundering Herd." If a Gateway node holding 100,000 connections crashes, all 100,000 clients effectively disconnect simultaneously. Their retry logic (often just a while(!connected) connect() loop) triggers an immediate storm of reconnection requests to the remaining healthy servers.

Mitigation Strategies:

  1. Exponential Backoff with Jitter: Clients must not reconnect immediately. They should wait a random amount of time (Jitter) that increases exponentially with each failure (e.g., wait = random(0, 2^n * 100ms)). This spreads the load over time.
  2. Shedding Load: Gateways should actively reject new connections if their pending handshake queue exceeds a threshold, prioritizing the stability of existing sessions over new ones.

4.3 The Ephemeral State Problem (Presence)

Tracking who is "Online" is another variant of the typing indicator problem. Writing to a database every time a user connects or disconnects is too slow.

Heartbeat-based Presence:

Gateways maintain a "last heartbeat" timestamp for every connection in local memory. Periodically (e.g., every 10s), the Gateway updates a distributed cache (Redis) with a TTL (Time To Live) of 15s.

5. Backend Concurrency: The Engine Room

Once a connection is established, the backend must route messages efficiently. The choice of programming language and concurrency model dictates the hardware efficiency of the system.

5.1 The Concurrency Challenge: Threads vs. Lightweight Processes

In traditional models (like Java's thread-per-request), each connection requires a dedicated OS thread. An OS thread has a large stack (1MB+) and significant scheduling overhead. Hosting 1 million connections would theoretically require 1TB of RAM just for stack space, and the CPU would spend 100% of its time context switching.

5.2 Erlang and the BEAM VM (The WhatsApp Model)

WhatsApp and Discord (via Elixir) famously utilize the Erlang ecosystem. Erlang solves the C10M problem using the Actor Model.

5.3 Go and CSP (The Modern Alternative)

Go (Golang) approaches concurrency using Goroutines and Channels (Communicating Sequential Processes).

Comparative Concurrency:

Feature Erlang/Elixir (BEAM) Go (Golang) Java (Traditional)
Unit of Concurrency Actor (Process) Goroutine OS Thread
Memory Footprint Very Low (~2KB) Very Low (~2KB) High (~1MB)
Communication Message Passing (Copy) Channels (Ref/Copy) Shared Memory (Locks)
Garbage Collection Per-Process Global Mark-and-Sweep Global (Generational)
Fault Tolerance Supervisor Trees (High) Manual Error Handling Exception Handling

Insight: For chat systems where latency consistency and uptime are paramount (WhatsApp), Erlang/Elixir is often favored. For systems requiring raw throughput and compute power (e.g., video encoding or massive logic processing), Go or Rust is preferred.

6. Message Routing and Brokerage

When Gateway-A receives a message intended for a user on Gateway-B, it needs a mechanism to transfer that data. This is the domain of the Message Broker.

6.1 The "Fan-Out" Problem

The complexity of routing scales with group size.

Most real-time systems use a hybrid Fan-out on Write for active sessions (pushing to connected sockets) and Fan-out on Read for persistence (storing one copy in the DB).

6.2 Distributed Pub/Sub: Redis vs. Kafka

The choice of broker defines the system's reliability and latency profile.

Redis Pub/Sub:

Apache Kafka:

The Hybrid Architecture:

Modern architectures often use both.

  1. Typing/Presence: Routed via Redis Pub/Sub for instant feedback.
  2. Messages: Written to Kafka first. A separate fleet of "Consumer Workers" reads from Kafka and pushes to the Gateways (for delivery) and to the Database (for long-term storage).

7. Data Persistence and Storage Engines

Chat applications generate a write-heavy workload with a specific access pattern: Temporal Locality. Users heavily access the most recent messages, while older history is rarely read.

7.1 Evolution of Storage: The Discord Case Study

7.2 WhatsApp and Mnesia

WhatsApp leverages Mnesia, the distributed database built into Erlang. Mnesia runs in the same memory space as the application, providing microsecond-level access speeds. WhatsApp uses Mnesia primarily for routing tables (User -> Gateway mappings) and transient message queues. For long-term storage, they have historically relied on a sharded setup, minimizing server-side storage by deleting messages once delivered (though this has changed with multi-device support).

8. Distributed Consistency and Race Conditions

Perhaps the most intellectually demanding aspect of messaging engineering is ensuring that all users see the same reality. In a distributed system, "Time" is an illusion, leading to race conditions.

8.1 The Illusion of Time and Ordering

Scenario:

  1. User A sends "Hello" (processed by Server 1).
  2. User B sees "Hello" and replies "Hi" (processed by Server 2).

If Server 1's system clock is 100ms ahead of Server 2's, User B's "Hi" might receive a timestamp earlier than User A's "Hello." If the client sorts by timestamp, the chat will read:

This violation of causality creates confusion.

8.1.1 Logical Clocks

To solve this, engineers rely on Logical Clocks rather than physical wall clocks.

8.2 Identifying and Handling Race Conditions

Scenario 1: The "Ghost" Notification (Out-of-Order Delivery)

Scenario 2: The Group Permission Race

Scenario 3: Concurrent Edits

9. Mobile Optimization and Battery Life

Mobile architecture is dominated by the constraints of the cellular radio. The radio has high-power (Active) and low-power (Idle) states. Every packet sent wakes the radio, consuming significant battery.

9.1 The Radio State Machine and Adaptive Heartbeats

If a chat app sends a "keep-alive" ping every 15 seconds, the radio never sleeps. The battery drains in hours.

However, if the app doesn't ping, the NAT (Network Address Translation) router at the ISP will drop the silent connection mapping, and the user will stop receiving messages.

Optimization: Adaptive Heartbeats

The client algorithmically discovers the maximum timeout of the current network.

  1. Start with a keep-alive interval of 4 minutes.
  2. If the connection dies, reduce to 3 minutes.
  3. If the connection survives, try 5 minutes. This allows the app to find the "sweet spot"—keeping the connection open with the minimum number of wake-ups (e.g., one ping every 9 minutes on T-Mobile, vs. every 4 minutes on AT&T).

9.2 Push Notifications as Fallback

Operating Systems (iOS/Android) aggressively kill background processes. A reliable messaging architecture cannot depend solely on WebSockets.

10. Security and Encryption

The modern standard is not just transport security (TLS/SSL) but End-to-End Encryption (E2EE), popularized by Signal and WhatsApp.

10.1 The Signal Protocol (Double Ratchet)

In E2EE, the server is blind. It routes encrypted blobs.

11. Conclusion

The "zero to hero" journey of building a massive-scale messaging app is a progression from simple data movement to complex distributed state management. It begins with selecting the right transport (WebSockets/MQTT) to minimize overhead. It evolves into a concurrency challenge, handled by the isolated processes of Erlang or the efficient goroutines of Go. It matures into a data storage challenge, requiring specialized databases like ScyllaDB to handle trillions of records.

Ultimately, the polish of the system—the seamless typing indicators, the instant presence, the battery efficiency—relies on identifying and mitigating race conditions and resource constraints at every layer of the stack. The successful architecture is one that assumes the network is unreliable, the clock is wrong, and the user is mobile, and builds a resilient consistency model on top of that chaotic reality.

Works Cited

1. Zeel Suthar. "WebSockets vs SSE vs MQTT." Medium. Accessed February 6, 2026. https://medium.com/@zeelm2014/websockets-vs-sse-vs-mqtt-c4fd2bb4ffbf
2. Himanshu Dhiman. "Go vs. Elixir: The Ultimate Battle for Backend Concurrency." Medium. Accessed February 6, 2026. https://medium.com/@hmnshudhmn24/go-vs-elixir-the-ultimate-battle-for-backend-concurrency-a1a5b15de26d
3. "Adding Typing Indicators to Real Time Chat Applications." DEV Community. Accessed February 6, 2026. https://dev.to/hexshift/adding-typing-indicators-to-real-time-chat-applications-76p
4. "Scaling realtime messaging for live chat experiences: Challenges and best practices." Ably.com. Accessed February 6, 2026. https://ably.com/blog/scaling-realtime-messaging-for-live-chat-experiences
5. Hamza. "WebSockets vs. Real-Time Rivals: A Deep Dive into SSE, Long-Polling, MQTT, and XMPP." DEV Community. Accessed February 6, 2026. https://dev.to/sshamza/websockets-vs-real-time-rivals-a-deep-dive-into-sse-long-polling-mqtt-and-xmpp-4hij
6. "Protocol Comparisons: Choosing the Right Real-Time Technology." WebSocket.org. Accessed February 6, 2026. https://websocket.org/comparisons/
7. "WebSockets vs MQTT: Web vs IoT Communication Protocols." WebSocket.org. Accessed February 6, 2026. https://websocket.org/comparisons/mqtt/
8. "Learn Debounce And Throttle In 16 Minutes." YouTube. Accessed February 6, 2026. https://www.youtube.com/watch?v=cjIswDCKgu0
9. "WebRTC Signaling Protocols: Comparing WebSocket, SIP, XMPP, and MQTT." Sheerbit. Accessed February 6, 2026. https://sheerbit.com/webrtc-signaling-protocols-comparing-websocket-sip-xmpp-and-mqtt/
10. "WebSocket vs MQTT: Performance Comparison for Enterprises." Lightyear.ai. Accessed February 6, 2026. https://lightyear.ai/tips/websocket-versus-mqtt-performance
11. "MQTT vs WebSocket - Which protocol to use when in 2024." Ably Realtime. Accessed February 6, 2026. https://ably.com/topic/mqtt-vs-websocket
12. Jayanth Thalla. "Developer Blog: How WhatsApp Handles Real-Time Messaging." Medium. Accessed February 6, 2026. https://medium.com/@jayanththalla33/developer-blog-how-whatsapp-handles-real-time-messaging-and-why-messages-say-waiting-for-this-0c00231dbfe7
13. "JavaScript Debounce vs. Throttle." Syncfusion Blogs. Accessed February 6, 2026. https://www.syncfusion.com/blogs/post/javascript-debounce-vs-throttle
14. "Debounce - Glossary." MDN Web Docs. Accessed February 6, 2026. https://developer.mozilla.org/en-US/docs/Glossary/Debounce
15. "Debouncing and Throttling Explained Through Examples." CSS-Tricks. Accessed February 6, 2026. https://css-tricks.com/debouncing-throttling-explained-examples/
16. "React Interview Coding Challenges: Debounce & Throttle Explained." YouTube. Accessed February 6, 2026. https://www.youtube.com/watch?v=Z8RP1venCCk
17. "Understanding Debouncing and Throttling in JavaScript – A Comprehensive Guide." Perficient Blogs. Accessed February 6, 2026. https://blogs.perficient.com/2024/11/12/understanding-debouncing-and-throttling-in-javascript-a-comprehensive-guide/
18. Ankit. "Debouncing vs Throttling: When to Use Which." JavaScript in Plain English. Accessed February 6, 2026. https://javascript.plainenglish.io/debouncing-vs-throttling-when-to-use-which-f11d600380e5
19. "Protobuf vs JSON Explained: Speed, Size & When to Use Each." DEV Community. Accessed February 6, 2026. https://dev.to/arnavsharma2711/protobuf-vs-json-explained-speed-size-when-to-use-each-5gg6
20. "How are protocol-buffers faster than XML and JSON?" Stack Overflow. Accessed February 6, 2026. https://stackoverflow.com/questions/52146721/how-are-protocol-buffers-faster-than-xml-and-json
21. "Protobuf vs JSON: Performance, Efficiency & API Speed." Gravitee.io. Accessed February 6, 2026. https://www.gravitee.io/blog/protobuf-vs-json
22. "Protobuf vs. JSON: Choosing the Right Data Format for API Development." AbstractAPI. Accessed February 6, 2026. https://www.abstractapi.com/guides/api-glossary/protobuf-vs-json
23. Hiren. "Protobuf vs JSON: I Cut My API Payload Size by 80%." Medium. Accessed February 6, 2026. https://medium.com/@hiren6997/protobuf-vs-json-i-cut-my-api-payload-size-by-80-heres-how-f04f9d95ccc8
24. Yadav Padiyar. "Scaling Up #5 — Discord: Real-Time Architecture at Internet Scale." Medium. Accessed February 6, 2026. https://medium.com/@yadavmpadiyar/%EF%B8%8F-scaling-up-5-discord-real-time-architecture-at-internet-scale-bef4be6b7198
25. "Real-time Messaging." Slack Engineering. Accessed February 6, 2026. https://slack.engineering/real-time-messaging/
26. Arvind Kumar. "Designing a Real-time Chat App (WhatsApp, Slack)." Medium. Accessed February 6, 2026. https://codefarm0.medium.com/designing-a-real-time-chat-app-whatsapp-slack-bf17912356d7
28. Gyanaa Vaibhav. "Designing a Real-Time Chat App That Actually Scales." Medium. Accessed February 6, 2026. https://medium.com/@gynanrudr0/designing-a-real-time-chat-app-that-actually-scales-no-bullsh-t-just-systems-that-work-0f3a2f1a35e8
30. "Comparing Elixir and Go." CloudBees. Accessed February 6, 2026. https://www.cloudbees.com/blog/comparing-elixir-go
31. "Erlang vs Elixir vs Go for Backend Development." Index.dev. Accessed February 6, 2026. https://www.index.dev/skill-vs-skill/backend-elixir-vs-erlang-vs-go
32. "How Discord Stores Trillions of Messages." Discord Blog. Accessed February 6, 2026. https://discord.com/blog/how-discord-stores-trillions-of-messages
33. "How WhatsApp handles 50 billion messages a day?" GeeksforGeeks. Accessed February 6, 2026. https://www.geeksforgeeks.org/system-design/how-whatsapp-handles-50-billion-messages-a-day/
34. "Go or Elixir which one is best for chat app services?" Elixir Forum. Accessed February 6, 2026. https://elixirforum.com/t/go-or-elixir-which-one-is-best-for-chat-app-services/49577
35. "How WhatsApp Works - Architecture Deep Dive." GetStream.io. Accessed February 6, 2026. https://getstream.io/blog/whatsapp-works/
36. "Redis OSS vs Kafka - Difference Between Pub/Sub Messaging Systems." AWS. Accessed February 6, 2026. https://aws.amazon.com/compare/the-difference-between-kafka-and-redis/
37. Indraneel Sarode. "Building a Scalable Chat App Using WebSockets, Redis, Kafka, and PostgreSQL." Medium. Accessed February 6, 2026. https://medium.com/@indraneelsarode22neel/building-a-scalable-real-time-chat-app-from-single-server-to-distributed-powerhouse-614bb3391fa8
38. Tejas Gupta. "Battle of the Streams: Redis Pub/Sub vs Kafka Streams for Real-Time Systems." Medium. Accessed February 6, 2026. https://medium.com/@2017tejasgupta/battle-of-the-streams-redis-pub-sub-vs-kafka-streams-for-real-time-systems-bdb7f1d18ee9
39. "Redis vs Kafka: A comprehensive comparison for developers." DoubleCloud. Accessed February 6, 2026. https://double.cloud/blog/posts/2024/02/redis-vs-kafka/
40. Alex Aslam. "How Discord Uses Event Sourcing for Message History." DEV Community. Accessed February 6, 2026. https://dev.to/alex_aslam/how-discord-uses-event-sourcing-for-message-history-3n9h
41. "Understanding WhatsApp's Architecture & System Design." CometChat. Accessed February 6, 2026. https://www.cometchat.com/blog/whatsapps-architecture-and-system-design
42. "How does the messenger maintain the sequencing of the messages?" Codemia. Accessed February 6, 2026. https://codemia.io/knowledge-hub/path/how_does_the_messenger_maintain_the_sequencing_of_the_messages_during_chat_and_when_users_log_in_again
43. "Message Ordering." University of Texas. Accessed February 6, 2026. https://users.ece.utexas.edu/~garg/dist/jbkv2/chapter12-order.pdf
44. "System design question - ordered message delivery in a messaging app." Stack Overflow. Accessed February 6, 2026. https://stackoverflow.com/questions/68693285/system-design-question-ordered-message-delivery-in-a-messaging-app
45. "Cloud-Edge Hybrid Applications." UNL Repository. Accessed February 6, 2026. https://run.unl.pt/bitstream/10362/148917/1/Linde_2022.pdf
46. "Sequence numbers in Secret Chats." Telegram APIs. Accessed February 6, 2026. https://core.telegram.org/api/end-to-end/seq_no
47. "Handling Race Conditions / Concurrency in Network Protocol Design." Stack Overflow. Accessed February 6, 2026. https://stackoverflow.com/questions/38381044/handling-race-conditions-concurrency-in-network-protocol-design
48. "Vector Clocks in Distributed Systems." GeeksforGeeks. Accessed February 6, 2026. https://www.geeksforgeeks.org/computer-networks/vector-clocks-in-distributed-systems/
49. "Adaptive Heartbeats For Our Information Superhighway." Gojek Blog. Accessed February 6, 2026. https://www.gojek.io/blog/adaptive-heartbeats-for-our-information-superhighway
50. Deepanshu. "Adaptive Heartbeats For Our Information Superhighway." Medium (Gojek Engineering). Accessed February 6, 2026. https://medium.com/gojekengineering/adaptive-heartbeats-for-our-information-superhighway-26459bf85d62
51. "WebSocket vs Push Notification: Enterprise Communication Comparison." Lightyear.ai. Accessed February 6, 2026. https://lightyear.ai/tips/websocket-versus-push-notification
52. "Best Way to Implement Real-Time Notifications (FCM, WebSockets, or Alternatives)?" Reddit (r/FlutterDev). Accessed February 6, 2026. https://www.reddit.com/r/FlutterDev/comments/1ire2q5/best_way_to_implement_realtime_notifications_fcm/