๐Ÿ“ก

A language tour

The Reliability of Erlang

Built for nine nines. Designed to crash gracefully and recover automatically. Telecom-grade fault tolerance in a language.

scroll

01 โ€” Let It Crash

Failure is a first-class citizen

Erlang's design philosophy inverts conventional wisdom. Don't write defensive code that tries to handle every failure. Instead, let processes crash when they encounter unexpected states, and rely on supervisors to restart them. The result is more reliable than defensive code โ€” simpler too.

"The problem with defensive programming is that you spend all your time handling errors that never happen, and your code gets complicated. In Erlang, you write for the happy path and let supervisors handle the rest."

โ€” Joe Armstrong, creator of Erlang
let_it_crash.erl
% Processes communicate by message passing
% If this crashes, the supervisor restarts it
-module(worker).
-export([start/0, loop/0]).

start() ->
    spawn(fun loop/0).

loop() ->
    receive
        {compute, Pid, X} ->
            % No nil check, no try/catch โ€” let it crash
            Result = X * X,
            Pid ! {result, Result},
            loop();
        stop ->
            ok
    end.

Erlang's message-passing model means processes share no memory. A crashing process cannot corrupt another process's state. Isolation is the foundation of fault tolerance.


02 โ€” Pattern Matching

Clauses that match structure

Pattern matching in Erlang is pervasive โ€” function heads, receive clauses, case expressions, variable binding all use it. It's how Erlang describes what kind of message a process handles, and what to do with each shape of data.

pattern_matching.erl
% Function clauses match on argument shapes
describe(0) -> "zero";
describe(N) when N > 0 -> "positive";
describe(_) -> "negative".

% Destructure tuples and lists
process({ok, Value})    -> {success, Value};
process({error, Reason}) -> {failure, Reason}.

% In receive โ€” each message shape has its handler
handle() ->
    receive
        {ping, From}    -> From ! pong, handle();
        {store, K, V}  -> store(K, V), handle();
        shutdown        -> io:format("Shutting down~n")
    end.

Guards (when N > 0) extend pattern matching with arithmetic and type tests. Erlang function clauses are tried top-to-bottom โ€” the first matching clause executes.


03 โ€” Distributed Systems

Nodes speak the same language

Erlang was designed for distributed telephone switches. Sending a message to a process on another machine looks identical to sending it locally โ€” you use a Pid, and the runtime handles the network. Distributed Erlang is not an afterthought; it's a design goal.

distributed.erl
% Start two Erlang nodes:
% erl -sname node1@localhost
% erl -sname node2@localhost

% Connect nodes
net_kernel:connect_node('node2@localhost').

% Spawn a process on a remote node
RemotePid = spawn('node2@localhost', fun() ->
    receive
        {hello, From} -> From ! world
    end
end).

% Send a message โ€” works identically across the network
RemotePid ! {hello, self()}.
receive
    world -> io:format("Got world from the other node~n")
end.

A process ID (Pid) in Erlang encodes which node it lives on. Pid ! Msg works whether the process is on the same node or on another machine halfway around the world.


04 โ€” Hot Code Reloading

Update running systems without stopping

Erlang can load new versions of modules into a running system while old processes continue using the old code. Telephone switches cannot be taken down for upgrades โ€” so Erlang made this a language feature. Systems can run for years without a restart.

hot_reload.erl
% A server loop โ€” old version
-module(server).
-export([loop/1]).

loop(State) ->
    receive
        {get, Pid} -> Pid ! State, loop(State);
        {set, Val} -> loop(Val);

        % Fully-qualified call triggers code upgrade
        % When a new module is loaded, this picks it up
        upgrade -> server:loop(State)
    end.

% In the Erlang shell:
% > l(server).            % load new version
% > Pid ! upgrade.        % tell process to use it
% The process continues โ€” no restart needed

The fully-qualified call server:loop(State) always calls the current version of the module. A plain loop(State) call stays on the old version. This is how Erlang managed rolling upgrades on telephone switches.


05 โ€” Supervisor Trees

Hierarchy of recovery

OTP supervisors watch worker processes and restart them when they crash. Supervisors can watch other supervisors, forming trees. When a worker fails repeatedly, its supervisor fails too โ€” escalating the failure up the tree until something can actually handle it.

supervisor.erl
-module(my_sup).
-behaviour(supervisor).
-export([start_link/0, init/1]).

start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
    Children = [{
        worker,                  % id
        {worker, start_link, []},  % {M, F, A}
        permanent,              % always restart
        5000,                    % shutdown timeout ms
        worker,                  % type
        [worker]                 % modules
    }],
    {ok, {{one_for_one, 5, 10}, Children}}.

one_for_one means: if one child crashes, restart only that child. {5, 10} means: if a child crashes 5 times in 10 seconds, the supervisor itself fails โ€” escalating to its own supervisor.


06 โ€” The Whole Picture

Why Erlang endures

๐Ÿ“ž

Telecom Heritage

Built at Ericsson for telephone exchanges. The nine nines โ€” 99.9999999% uptime โ€” was a real requirement.

๐Ÿ’ฌ

WhatsApp & Discord

WhatsApp served 2 billion users with a tiny team. Discord handles millions of concurrent connections. Both on the BEAM.

โšก

The BEAM VM

Soft real-time scheduler, per-process GC, preemptive multitasking. The VM design is still state of the art.

๐Ÿ”„

No Downtime Deploys

The only mainstream language where zero-downtime code hot-swapping is a built-in, first-class feature.

๐ŸŒŠ

Elixir's Parent

Elixir runs on the BEAM and inherits all of OTP. Erlang's 35 years of battle-tested infrastructure, modern syntax.

๐Ÿงช

Mnesia

A distributed database built into OTP. Real-time, transactional, replicated โ€” and it's part of the standard library.