Why Real-Time Products Require a Different Kind of Architecture

Why Real-Time Products Require a Different Kind of Architecture

Notifications. Live dashboards. Collaborative workspaces. Messaging. Vehicle tracking. Connected devices.

These features appear very different from a product perspective, but they create a similar architectural challenge: the system must respond to events as they happen and distribute updates to many users or devices without becoming unreliable or prohibitively complex.

That is a different workload from the one most conventional web applications were designed to handle.

Ruby on Rails helped a generation of companies build and validate products quickly. It remains a productive choice for many web applications. But when continuous interaction, high concurrency, and resilience become strategically important, technology leaders should consider whether the architecture beneath the product still matches the job it is being asked to do.

Elixir and Phoenix offer another option — one designed around large numbers of concurrent activities, long-lived connections, message passing, and controlled failure.

 

When “real time” becomes part of the product

A traditional web interaction is short-lived. A user requests a page or submits a form, the server processes the request, returns a response, and the connection can end.

Real-time products behave differently.

A logistics dashboard must update when a vehicle changes position. A collaboration tool must show edits made by other participants. A messaging application must deliver new messages immediately. An industrial platform may need to reflect the state of thousands of connected sensors. A notification service must reach the right users when an important business event occurs.

The server cannot simply wait for every user to request the latest information. It needs a way to send changes as soon as they are available.

This distinction matters because real-time functionality is rarely just a user-interface feature. It changes how the system communicates, holds state, manages resources, handles failure, and scales.

 

Persistent connections change the operating model

Most conventional web systems are organized around stateless request-and-response traffic. Each request is relatively independent. The application authenticates it, retrieves the necessary data, performs some work, returns a response, and releases its resources.

Real-time communication commonly uses a persistent connection, often a WebSocket. Instead of creating a new interaction for every update, the connection remains open so that the client and server can send messages in either direction.

For a single user, that sounds simple. At product scale, it means the application may need to manage tens or hundreds of thousands of active connections at the same time.

Each connection can have its own:

  • Identity and authorization context
  • Subscriptions to particular topics
  • Current state
  • Incoming and outgoing messages
  • Timeouts and reconnection behaviour
  • Failure conditions

The architectural problem is therefore not just processing more requests. It is coordinating a large number of activities that remain alive, change independently, and frequently communicate with one another.

That is a concurrency problem.

 

Why concurrency is more than traffic volume

Concurrency describes the system’s ability to make progress on many independent activities during the same period.

Consider a fleet-management platform. Thousands of vehicles may be reporting their positions while dispatchers view live maps, customers track deliveries, and automated rules detect delays. A slow external map service should not prevent position updates from being received. A failed connection should not interrupt unrelated customers. A burst of events from one fleet should not make every dashboard unresponsive.

Many mainstream web stacks can support real-time features. The question is what additional machinery is required to make them reliable at the scale and importance the business expects.

A team may begin with a WebSocket server and gradually add:

  • A message broker to distribute events
  • A cache or shared data store for connection state
  • Background workers for asynchronous processing
  • Retry and timeout mechanisms
  • A separate service for notifications
  • Load-balancer configuration for long-lived connections
  • Monitoring for queues, brokers, workers, and connection servers
  • Custom logic for partial failures and reconnections

Every component may be justified. Together, however, they create more operational boundaries, more failure modes, and more knowledge the organization must retain.

Real-time functionality has then become a distributed system assembled around an application that was originally designed for short-lived requests.

 

A runtime designed for concurrent systems

Elixir runs on the Erlang virtual machine, commonly called the BEAM. This runtime was designed for systems that must handle large numbers of concurrent activities and continue operating when individual parts fail.

Instead of modelling concurrent work primarily through comparatively heavy operating-system processes or shared-memory threads, Elixir uses lightweight, isolated processes. These are not operating-system processes. They are small units of execution managed by the BEAM runtime.

Each process owns its state and communicates with other processes by sending messages. If one process fails, its failure can remain isolated rather than corrupting shared state or automatically bringing down unrelated work.

The Erlang documentation describes these processes as lightweight, fast to create and terminate, and designed for massive concurrency. Its OTP design principles add supervision trees: supervisors monitor groups of worker processes and can restart work according to explicit recovery policies.

For management teams, the significance is not the terminology. It is the alignment between the application’s structure and the operational reality of a real-time product:

  • Connections can be represented as isolated processes.
  • Independent tasks do not need to share mutable state.
  • Slow work can be prevented from blocking unrelated activity.
  • Failures can be contained within deliberate boundaries.
  • Recovery behaviour can be part of the application’s design.
  • The same model can be used for connections, background work, workflows, and device sessions.

The architecture begins with concurrency and failure as normal conditions rather than treating them as exceptions to be added later.

 

What Phoenix adds

Phoenix is the web framework most commonly used with Elixir. Its real-time capabilities are built around the same process and message-passing model as the underlying runtime.

Phoenix Channels provide long-lived, bidirectional communication. Clients connect through transports such as WebSockets and subscribe to topics — for example, a customer account, delivery, chat room, or device group. Messages can then be routed to the relevant subscribers rather than broadcast indiscriminately.

Each channel can be backed by a lightweight BEAM process. Phoenix PubSub distributes messages across application nodes, allowing clients connected to different servers to receive updates for the same topic. The official Phoenix documentation describes Channels as supporting soft real-time communication with millions of connected clients, with capacity expanded by adding nodes to a cluster. Actual production capacity will, of course, depend on message size, frequency, business logic, infrastructure, and testing.

Phoenix LiveView offers a related option for interactive web interfaces. It keeps relevant state on the server and sends efficient page updates over a persistent connection. This can allow teams to build dashboards, operational interfaces, and collaborative experiences without automatically creating a separate, highly complex front-end application for every real-time feature.

Channels and LiveView solve different product needs, but both benefit from an architecture in which persistent connections, messaging, and concurrent state are first-class concepts.

 

The management implication: complexity moves somewhere

No architecture eliminates complexity. It determines where that complexity lives and who must manage it.

In a conventional stack, real-time capabilities may depend on several external systems and custom coordination layers. The organization must operate those systems, understand their interactions, diagnose failures across them, and maintain the boundaries between teams and services.

With Elixir and Phoenix, more of the concurrency, messaging, fault isolation, and recovery model can live within one coherent platform. External services are still appropriate where they provide clear value, but they do not have to compensate for a runtime that was designed primarily around another workload.

This can have several organizational consequences.

 

Fewer accidental service boundaries

Teams sometimes extract real-time functionality into microservices because the existing application cannot handle it comfortably — not because the business domain requires a separate service.

A runtime designed for concurrent work can let an organization preserve clearer domain boundaries and avoid creating extra services solely as performance workarounds.

 

Less operational coordination

Every broker, worker system, connection gateway, and shared cache adds deployment, observability, security, and incident-response responsibilities.

Reducing the number of moving parts can shorten the path from a customer-visible problem to its cause. It can also reduce the number of specialists required to understand the complete system.

 

Better fault containment

In an always-connected product, some failure is inevitable. Mobile networks disappear. Devices reconnect. APIs time out. Messages arrive late. Individual sessions encounter unexpected data.

The important question is whether such failures remain local. Process isolation and supervision give teams explicit tools for preventing a single problematic activity from destabilizing the wider application.

 

More predictable product development

When real-time communication is a standard capability of the application platform, new live features do not always require a new architectural project.

A team that already has a coherent model for connections, topics, presence, and recovery can apply it to the next dashboard, notification flow, or collaborative feature. This turns real time from a special initiative into a repeatable product capability.

 

This is not an argument for rewriting everything

Elixir and Phoenix are not necessary for every product with a notification badge or an occasionally refreshed dashboard.

A conventional application may remain the right choice when connection volumes are modest, updates are infrequent, temporary delays are acceptable, or a managed real-time service meets the business need at an acceptable cost.

Nor does adopting Elixir require replacing an entire Ruby system.

For many organizations, the sensible first step is a bounded area where the architectural requirements are clear: a live operations dashboard, a messaging service, device connectivity, event distribution, or a new collaborative workflow. The existing application can continue handling the business capabilities it already serves well.

This is an architectural portfolio decision, not a language contest.

Ruby helped companies build products quickly. Elixir and Phoenix become particularly relevant when the product must coordinate many simultaneous activities, deliver updates continuously, and remain dependable when individual components fail.

 

Questions to ask your technical team

Leadership does not need to prescribe a language. It does need to ensure that architectural decisions reflect the future shape of the product.

Useful questions include:

  1. Which upcoming features require the server to push updates rather than wait for users to request them?
  2. How many simultaneous users, devices, subscriptions, or active workflows should we design for over the next three years?
  3. What additional infrastructure does our current stack require to support persistent connections and event distribution?
  4. Where is connection and subscription state stored, and what happens when a server restarts?
  5. Can a slow integration, overloaded customer, or malformed event affect unrelated users?
  6. How are failures isolated, detected, and recovered without manual intervention?
  7. How many systems must an engineer inspect to diagnose a delayed or missing update?
  8. Are we creating new services because the business domain requires them, or because the existing runtime struggles with concurrency?
  9. Could we test Elixir and Phoenix in one bounded, strategically relevant capability without rewriting the core product?
  10. What evidence would determine whether that pilot was successful — latency, connection capacity, recovery time, infrastructure cost, delivery speed, or operational workload?

 

Architecture should follow the product experience

Real-time features can make a product feel immediate, intelligent, and connected. They can also expose architectural assumptions that were invisible when every interaction was a short-lived request.

Once persistent connections and continuous event flows become central to the customer experience, concurrency and resilience are no longer implementation details. They are product capabilities — and sources of business risk if handled poorly.

Technology leaders should therefore ask more than whether their current stack can deliver real-time functionality. Most stacks can.

The more useful question is whether it can do so with an architecture the organization can operate, evolve, and trust.

When the answer is becoming complicated, Elixir and Phoenix deserve serious consideration.

 

Additional resources

Contact us to discover how our services can elevate your application and amplify its efficiency and performance.