Languages
Elixir
Concurrent, fault-tolerant real-time systems on the BEAM, built by engineers who run what they ship.
Overview
Elixir is a modern, functional language that runs on the Erlang virtual machine, the BEAM. That lineage is the whole point. Erlang was built inside a telecoms company to run switches that stayed up while the code beneath them was replaced, and it has spent decades proving that a single machine can juggle enormous numbers of simultaneous connections without falling over. Elixir keeps every bit of that machinery and wraps it in a friendly, Ruby-inspired syntax with a first-class tooling story. You write clear, functional code; underneath, you inherit a runtime that was designed from the outset for concurrency, isolation and staying alive.
The defining feature is how the BEAM handles concurrency. Instead of operating-system threads, it runs its own lightweight processes — you can have millions of them on one machine — each with its own isolated memory, scheduled pre-emptively so no single process can starve the rest. They never share state; they communicate by passing messages, the actor model in practice. Because each process is isolated, one crashing cannot corrupt another, and that is what makes the famous "let it crash" philosophy sane rather than reckless. Processes are organised into supervision trees: when a process dies, its supervisor restarts it in a known-good state, so faults are contained and recovered automatically instead of cascading into an outage. This is the OTP framework, and it is decades old and battle-tested.
We reach for Elixir when a system has to hold many connections open, react in soft real time, and stay up. Phoenix, the web framework, is fast and pragmatic, and LiveView lets us build rich, real-time interfaces rendered on the server with very little JavaScript — a genuine shift in how much front-end complexity a small team has to carry. We are equally blunt about the other side. Elixir is not the tool for CPU-bound number crunching or machine learning, its ecosystem and hiring pool are smaller than the mainstream, and for a plain CRUD application a more conventional stack is usually the more pragmatic, easier-to-staff choice. We only recommend it where the BEAM’s model is a real fit for the problem.
Best for — Highly concurrent, real-time, always-on systems — messaging, live dashboards, IoT and streaming backends — where the BEAM’s fault tolerance and lightweight processes are a genuine fit for the problem, not a preference.
Why teams choose Elixir
Massive concurrency on modest hardware
The BEAM runs its own lightweight processes rather than OS threads, so one machine can hold hundreds of thousands or millions of simultaneous connections with per-process isolation. Systems that would need a fleet of servers and a queue of infrastructure in a thread-per-request model often fit on a fraction of the hardware in Elixir.
Fault tolerance that is built in, not bolted on
Supervision trees and process isolation mean a failure is contained to one process and recovered automatically by restarting it in a clean state. You design for the fact that things fail, and the runtime does the recovering — so a bug in one request does not take down the service handling the others.
Predictable low latency under load
The BEAM schedules processes pre-emptively and has no stop-the-world garbage-collection pause across the whole system — each process is collected independently. That gives soft real-time behaviour: consistent, low tail latency even when the machine is busy, which is exactly what real-time systems live or die by.
Why businesses choose Elixir
- Your product is fundamentally real-time — connections stay open, updates push out, many users interact at once — and you want that concurrency to be the runtime’s job rather than a scaling problem you fight forever.
- Downtime is expensive and you want fault tolerance designed into the system, so individual failures are isolated and recovered automatically instead of cascading into outages.
- You want a small senior team to ship a rich, interactive product without carrying a large separate front-end codebase, using Phoenix and LiveView to keep the moving parts few.
- You value predictable latency under load over raw single-threaded speed, and your workload is I/O- and connection-heavy rather than compute-heavy.
What we build with Elixir
The capabilities this technology is genuinely strong at — and what we most often build with it.
Lightweight BEAM processes and the actor model
We model concurrent work as isolated processes that hold no shared memory and communicate only by passing messages. Because each has its own heap and is scheduled pre-emptively, we can spin up one process per connection, per device, or per game session without the cost that thread-per-connection carries elsewhere.
OTP supervision trees and "let it crash"
We structure applications as supervision trees, where supervisors watch worker processes and restart them to a known state on failure. This turns error handling from defensive, defensive code into a clear recovery strategy: let a broken process die, and let its supervisor bring back a clean one.
Phoenix, the web framework
Phoenix gives us a fast, productive foundation for web services and APIs — routing, channels for WebSockets, contexts for domain boundaries, and Ecto for the database. It is pragmatic and quick to build in, and its channel abstraction makes real-time features first-class rather than an add-on.
LiveView for real-time server-rendered UI
LiveView renders interactive interfaces on the server and sends only minimal diffs to the browser over a WebSocket. We use it to build live dashboards, forms, and collaborative screens with a fraction of the JavaScript a single-page application would need — state stays on the server, where it is easier to reason about.
GenServer, tasks and the OTP toolkit
We build stateful concurrency with the OTP behaviours — GenServer for stateful processes, Task for one-off asynchronous work, Registry and dynamic supervisors for managing fleets of processes. These are the tried, standard building blocks that keep concurrent Elixir code idiomatic and maintainable rather than ad hoc.
Hot code upgrades and pattern matching
The BEAM can, where it earns its keep, upgrade running code without dropping connections — a capability few runtimes offer. Day to day, exhaustive pattern matching and immutable data make the code’s intent explicit and its state easy to trace, which is a large part of why Elixir systems stay debuggable as they grow.
Use cases
Chat and real-time messaging
Systems that hold a persistent connection open per user and fan messages out with presence and typing indicators. One process per connection and Phoenix channels make this the workload Elixir was almost designed for.
Live dashboards and collaborative tools
Operations dashboards, live scoreboards, and multi-user editors where the screen must update the moment data changes. LiveView pushes diffs to every connected client from the server, without a bespoke front-end state layer.
IoT and device backends
Backends that keep tens of thousands of devices connected at once, each modelled as its own supervised process, with soft real-time handling of the messages they send. Per-process isolation means one misbehaving device does not affect the rest.
Streaming and always-on services
Event streaming, notification delivery, and services where uptime is the headline requirement. Supervision trees and predictable latency under concurrent load keep these systems serving while individual parts fail and recover.
When Elixir is the right choice
- You are building a system that holds many connections open at once and must react in soft real time — chat and messaging, live dashboards, collaborative tools, presence, streaming, IoT and device backends, or a multiplayer layer. This is the BEAM’s home ground, and nothing mainstream matches it here without far more moving parts.
- Availability is a hard requirement: the system must keep serving while individual parts fail. Supervision trees, per-process isolation and the "let it crash" model mean a fault is contained and recovered automatically rather than taking the whole service down. If an outage is genuinely expensive, this is worth paying for.
- You want to serve a rich, interactive UI from a small team without building and maintaining a large single-page-application front end. Phoenix LiveView keeps state and rendering on the server and pushes only diffs over a WebSocket, which removes a whole tier of JavaScript for many products.
- Wrong for: CPU-bound heavy computation or machine-learning workloads — video transcoding, large-scale numerical work, model training. The BEAM is optimised for concurrency and message passing, not raw single-threaded throughput. Nx is emerging for numerical work, but this is not why you would choose Elixir, and Python or Rust remain the honest answer.
- Wrong for: a straightforward CRUD application, an internal admin tool, or a project where the deciding factor is how quickly you can hire and staff a team. A mainstream stack — TypeScript, Python, or similar — will ship the same feature just as well, from a far larger talent pool, and reaching for Elixir there buys you concurrency you do not need.
Elixir: pros and cons
Strengths
- Concurrency is the language’s core competence: millions of isolated lightweight processes, message passing, and the actor model, all first-class rather than a library you bolt on.
- Fault tolerance via OTP supervision trees and "let it crash" is decades-proven telecom-grade engineering, so recovery from failure is a designed-in property rather than an afterthought.
- Phoenix and LiveView deliver fast, real-time, server-rendered interactive UIs with minimal JavaScript, removing a whole tier of front-end complexity for many products.
- Predictable low latency: per-process garbage collection and pre-emptive scheduling mean no system-wide pauses, giving consistent tail latency under heavy concurrent load.
Trade-offs
- A smaller ecosystem and talent pool than mainstream languages. There are fewer libraries, fewer off-the-shelf answers, and hiring experienced Elixir engineers is genuinely harder — a real staffing risk you should weigh before committing.
- It is the wrong tool for CPU-bound heavy computation and machine learning. The BEAM is built for concurrency, not raw single-threaded number crunching; Nx is emerging but this is not Elixir’s strength, and pretending otherwise would be dishonest.
- Functional programming, immutability and the OTP process model are a real learning curve for teams coming from imperative or object-oriented backgrounds. You do not get productive on day one, and unlearning shared-mutable-state habits takes time.
- For a simple CRUD app, the BEAM’s power is unused and a mainstream stack is more pragmatic — easier to staff, more libraries, more familiar to the next team that inherits it. Choosing Elixir where the concurrency is not needed is a cost, not a gain.
Architecture
We design Elixir systems around processes and supervision, not around a request-response monolith with concurrency bolted on. The first architectural questions are which parts of the system are naturally concurrent and long-lived — a connection, a device, a session, a job — and how those map to isolated processes. We then draw the supervision tree: which processes supervise which, what the restart strategy is when one fails, and what "a known-good state" means for each. Getting that tree right is what turns "let it crash" from a slogan into a reliability property, because it decides exactly how far a failure can propagate before it is contained.
Above the processes, Phoenix contexts give us clear domain boundaries and Ecto keeps the database layer explicit and typed against the schema. For real-time features we lean on channels and PubSub rather than reinventing message distribution, and for interactive UI we default to LiveView so state lives on the server where the rest of the system can see it. When a component genuinely needs to scale beyond one machine, the BEAM’s distribution primitives let processes on different nodes talk as if they were local — but we treat that as a deliberate step, not a default, because distribution adds real operational weight.
Performance
Elixir’s performance profile is specific and worth being precise about. It excels at concurrent, I/O-bound and connection-heavy work: holding many connections open, passing messages between processes, and keeping latency predictable while the machine is busy. Because each process is garbage-collected independently, there is no system-wide pause, so tail latency stays consistent under load in a way thread-and-lock runtimes struggle to match. For the real-time systems we build in it, that consistency matters more than peak single-threaded speed.
It is not fast at raw single-threaded computation, and we will not pretend otherwise. CPU-bound hot loops — heavy numerical work, encoding, cryptographic grinding — are slower in Elixir than in a compiled systems language, and the honest answer there is to push that work into a native implementation via a NIF or a separate service, or to choose a different tool entirely. We profile before optimising, keep the concurrent paths clean, and are candid when a workload sits on the wrong side of that line.
Security
Process isolation gives Elixir a helpful security property for free: a fault or bad input in one process cannot read or corrupt another’s memory, so the blast radius of many bugs is naturally small. On top of that we apply the ordinary discipline — every input validated at the boundary with Ecto changesets, authentication and authorisation enforced on the server rather than assumed in the UI, and secrets kept out of the codebase and out of compiled releases.
Phoenix ships sensible defaults — CSRF protection, output escaping in templates, secure session handling — and we keep them on rather than working around them. LiveView’s server-side state model actually reduces a class of front-end tampering, because the client cannot manipulate state it never holds, but it makes authorising each event on the server non-negotiable. We keep the dependency tree small, watch for known vulnerabilities in the packages we do pull in, and treat the smaller ecosystem as a reason to vet dependencies more carefully, not less.
Scalability
Elixir scales up before it scales out, and that is a genuine advantage. A single BEAM node can hold a volume of concurrent connections that would need a cluster of servers in a thread-per-request model, so the first answer to growth is often "a bigger machine", which is far simpler to operate than a distributed system. Lightweight processes mean concurrency grows with demand without the memory and scheduling overhead that OS threads impose, and the pre-emptive scheduler keeps one heavy tenant from starving the others.
When one machine is genuinely not enough, the BEAM’s built-in distribution lets nodes form a cluster and processes communicate across them transparently, with libraries for distributed presence and process registration built on top. We plan for that horizontally-scaled shape only when the numbers demand it, because clustering brings its own operational cost. The point is that Elixir gives you an unusually high ceiling on a single node first, so you reach that decision later and with clearer evidence than most stacks allow.
Elixir integrations & ecosystem
The technologies we most often pair with it — each links to how we work with it.
How we work
We start by testing whether Elixir is actually the right fit, because recommending it where the concurrency is not needed would be doing you a disservice. If the problem is genuinely real-time, connection-heavy and availability-critical, we design the process and supervision model first — that is the load-bearing decision, and it is far cheaper to get right early than to retrofit. From there we build in thin vertical slices: a real feature, running in production, exercising the concurrency and failure paths against reality rather than a diagram.
The engineers who design the system are the ones who write it and keep it running, which is what we mean by operating what we build. With Elixir that matters especially, because the whole "let it crash" model only pays off if the supervision strategy was designed by someone who has watched these systems fail and recover in production. You get idiomatic OTP, honest advice about the smaller talent pool you will be hiring into, and a system your team can actually operate.
The service behind it
Delivered throughCustom Software DevelopmentWhat we build with Elixir
The disciplines this technology most often shows up in — from a first build to taking over and stabilising an existing one.
How we deliver
- 01
Discover
We map the system, the constraints and the business it serves — including the parts nobody documented.
Architecture brief
- 02
Architect
Decisions get made, written down and defended before a line of production code exists.
Decision records
- 03
Build
Short cycles against working software. You see progress in the product, not in a status deck.
Shipping increments
- 04
Operate
Monitoring, incident response and iteration. The system is alive, so the engagement is too.
Runbooks & SLOs
Industries we use Elixir in
Domain knowledge changes what gets built. A few of the sectors we know before the first meeting.
Also in Languages
Why teams choose us for Elixir
Senior engineers only
OTP and the supervision model reward experience and punish guesswork — a badly drawn supervision tree undermines the very reliability you chose Elixir for. The people designing yours have built and run these systems before, and there are no juniors learning the BEAM on your project.
We operate what we build
We run the concurrent, always-on systems we ship, so we design the failure and recovery paths for the three-in-the-morning reality, not the demo. "Let it crash" is only sound when someone who lives with the consequences drew the tree.
Honest about the smaller ecosystem
We will tell you plainly where Elixir’s talent pool and library ecosystem are thinner than the mainstream, and what that means for hiring and maintenance — before you commit, not after. If a conventional stack serves you better, we will say so.
We only recommend Elixir where the BEAM fits
We reach for Elixir when a system is genuinely real-time, high-concurrency and availability-critical. For a plain CRUD app or CPU-bound work, we will point you at the more pragmatic tool rather than sell you concurrency you do not need.
Typical timeline
- 01
Fit and architecture
One to two weeks confirming Elixir is the right choice and designing the process, supervision and data model — the decisions that are cheap to settle up front and expensive to change later.
- 02
First vertical slice
Two to three weeks delivering one real feature end to end in production, exercising the concurrency and failure-recovery paths against real load rather than a plan.
- 03
Iterative build
Feature-by-feature delivery in short cycles, each shippable, with the supervision strategy, latency and real-time behaviour verified as we go rather than assumed.
- 04
Hardening and handover
Load testing the concurrent paths, tightening the supervision tree, and documenting the OTP design so your team can operate and extend it — including honest guidance on staffing.
How pricing works
- Fixed-scope builds for well-defined real-time systems — a messaging backend, a live dashboard, an IoT gateway — quoted once we understand the concurrency profile and the availability requirements.
- Monthly senior engagement for ongoing product work on a live Elixir system, where scope evolves and you want continuity from the people who built it rather than a fixed deliverable.
- Focused assessments for teams weighing Elixir against a mainstream stack, or rescues of an existing Phoenix or OTP codebase, priced by the review.
Hire Elixir engineers
Need Elixir capacity on your own team? We embed named senior engineers into your existing team — reporting to your leads, working in your rituals — so you add capacity without a hiring cycle.
Hire Elixir engineersCommon questions
What is the difference between Elixir and Erlang?
They run on the same virtual machine — the BEAM — and share the same concurrency and fault-tolerance model, OTP. Erlang is the older language with a syntax that many find unfamiliar; Elixir is a modern language with a friendlier, Ruby-inspired syntax and much better tooling built on top of that same runtime. Choosing Elixir gets you Erlang’s decades of telecom-grade reliability with a far more approachable developer experience, and the two can call each other’s code freely.
Is Elixir fast?
For the work it is built for, yes — concurrent, connection-heavy, I/O-bound systems where it holds huge numbers of connections with predictable low latency and no system-wide pauses. For raw single-threaded computation it is not fast, and we would not choose it for CPU-bound number crunching or machine learning. The honest framing is that Elixir optimises for consistent latency under concurrent load, not peak throughput on one core.
What does "let it crash" actually mean — is it as reckless as it sounds?
No, it is the opposite of reckless. Because each BEAM process is isolated, a process that hits an error can simply die without corrupting anything else, and its supervisor restarts it in a clean, known-good state. Rather than writing defensive code to handle every conceivable failure inline, you let a broken process fail fast and let the supervision tree recover it. The reliability comes from designing that tree well, which is a senior engineering task, not from ignoring errors.
When should we not use Elixir?
When the workload is CPU-bound — heavy numerical work, video processing, model training — because the BEAM is built for concurrency, not raw computation. And when the deciding factor is hiring: Elixir’s talent pool is smaller than mainstream languages, so if you need to staff a large team quickly or hand the codebase to whoever is available, a conventional stack is the more pragmatic choice. For a simple CRUD app, the BEAM’s power goes unused and a mainstream framework will serve you just as well.
Can Elixir and LiveView replace our React front end?
For many real-time and interactive products, yes, and that is one of Elixir’s genuine advantages. LiveView renders on the server and sends minimal diffs to the browser, so you get live, interactive UI without a large separate JavaScript application to build and maintain. It is not right for everything — offline-first apps, heavily client-side interactions, or teams already invested in a React ecosystem may still want a dedicated front end — and Elixir integrates cleanly with a React front end over an API when that is the better fit.
Building on Elixir?
A technical conversation with the engineers who would do the work. If we are not the right fit, we will say so on the call.