Est. 1986 Intermediate

Erlang

A functional, concurrent programming language designed for building massively scalable, fault-tolerant distributed systems - the backbone of telecommunications and messaging platforms like WhatsApp.

Created by Joe Armstrong, Robert Virding, Mike Williams (Ericsson)

Paradigm Multi-paradigm: Functional, Concurrent, Distributed
Typing Dynamic, Strong
First Appeared 1986
Latest Version Erlang/OTP 27 (2024)

Erlang is a functional programming language designed from the ground up for building concurrent, distributed, and fault-tolerant systems. Created at Ericsson for telecommunications switches, it embodies the principle that software should handle failures gracefully rather than try to prevent them.

History & Origins

Erlang’s story begins in 1986 at Ericsson’s Computer Science Laboratory in Stockholm, Sweden. Joe Armstrong, Robert Virding, and Mike Williams were tasked with finding better ways to program telephone switches - systems that needed to handle millions of simultaneous calls, never go down, and be upgraded without service interruption.

The Problem: Telephone Switches

Traditional telephone switches had extreme requirements:

  • 99.999% uptime (about 5 minutes downtime per year)
  • Handling millions of concurrent calls
  • Hot-swapping code without dropping calls
  • Graceful degradation under failure

No existing language could meet these needs, so Armstrong’s team created their own.

From Prolog to Production

The first Erlang implementation was a Prolog interpreter (1987), reflecting Armstrong’s background in logic programming. The name “Erlang” is both a reference to mathematician Agner Krarup Erlang (who invented queuing theory for telephone networks) and a play on “Ericsson Language.”

By 1991, Erlang had its first compiled version using JAM (Joe’s Abstract Machine). The language evolved through real-world use on Ericsson’s AXD301 switch, which achieved legendary reliability.

The Open-Source Era

In 1998, Ericsson made a controversial decision to ban Erlang internally (preferring Java). Rather than let it die, the team convinced management to open-source it. This “Erlang ban” backfired spectacularly - the language found new life in the broader software community, and Ericsson eventually reversed course.

What Makes Erlang Different

1. Lightweight Processes (Not OS Threads)

Erlang processes are incredibly lightweight - you can spawn millions of them:

1
2
3
4
5
6
7
% Spawn a million processes
spawn_many(0) -> done;
spawn_many(N) ->
    spawn(fun() -> timer:sleep(10000) end),
    spawn_many(N - 1).

% This actually works! Try: spawn_many(1000000).

Each process:

  • Has its own heap and garbage collector
  • Uses about 300 bytes of memory
  • Is preemptively scheduled by the BEAM VM
  • Cannot crash other processes

2. Message Passing (No Shared Memory)

Processes communicate only through message passing - there’s no shared mutable state:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
% A simple server process
loop(State) ->
    receive
        {get, From} ->
            From ! {ok, State},
            loop(State);
        {set, NewState} ->
            loop(NewState);
        stop ->
            ok
    end.

% Start the server
Pid = spawn(fun() -> loop(initial_state) end),

% Send messages
Pid ! {set, new_value},
Pid ! {get, self()}.

3. “Let It Crash” Philosophy

Instead of defensive programming with try/catch everywhere, Erlang embraces failure:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
% Supervisor tree - processes watch other processes
-module(my_supervisor).
-behaviour(supervisor).

init([]) ->
    Children = [
        #{id => worker1,
          start => {worker, start_link, []},
          restart => permanent}
    ],
    {ok, {#{strategy => one_for_one}, Children}}.

% When worker1 crashes, supervisor automatically restarts it

This pattern, called “supervision trees,” means:

  • Individual processes can crash without bringing down the system
  • Supervisors restart failed processes automatically
  • Complex systems self-heal

4. Hot Code Swapping

Change running code without stopping the system:

1
2
3
4
5
6
7
8
9
% Version 1 of a module
-module(counter).
-export([loop/1]).

loop(N) ->
    receive
        tick -> loop(N + 1);  % Old behavior: increment by 1
        {get, From} -> From ! N, loop(N)
    end.
1
2
3
4
5
6
% Version 2 - change behavior at runtime
loop(N) ->
    receive
        tick -> loop(N + 10);  % New behavior: increment by 10
        {get, From} -> From ! N, loop(N)
    end.

Compile and load the new version - running processes pick it up on their next function call.

5. Pattern Matching Everywhere

Pattern matching is fundamental to Erlang:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
% Function clauses match on arguments
factorial(0) -> 1;
factorial(N) when N > 0 -> N * factorial(N - 1).

% Match in receive
receive
    {ok, Result} -> process_result(Result);
    {error, Reason} -> handle_error(Reason);
    timeout -> retry()
after 5000 ->
    timeout
end.

% Match in assignments
{Name, Age, _Email} = {<<"Alice">>, 30, <<"[email protected]">>}.

6. Immutable Data

All data in Erlang is immutable - there’s no assignment, only binding:

1
2
3
4
5
6
% This creates a new list, X is unchanged
X = [1, 2, 3],
Y = [0 | X],      % Y is [0, 1, 2, 3]

% This is a compile error - can't rebind X
% X = [4, 5, 6].

7. Distribution Built In

Erlang nodes can communicate transparently across machines:

1
2
3
4
5
% On machine 1 (node@host1)
register(my_server, spawn(fun() -> server_loop() end)).

% On machine 2 (node@host2)
{my_server, 'node@host1'} ! {request, self()}.

The same message-passing syntax works whether processes are local or remote.

The BEAM Virtual Machine

Erlang runs on the BEAM (Bogdan’s Erlang Abstract Machine), a sophisticated VM optimized for:

  • Soft real-time performance - Consistent response times
  • Massive concurrency - Efficient scheduling of millions of processes
  • Garbage collection - Per-process GC means no global pauses
  • Hot code loading - Upgrade code while running
  • Distributed computing - Built-in clustering

The BEAM is why Erlang systems achieve remarkable reliability and why Elixir chose to run on it.

OTP: The Secret Weapon

OTP (Open Telecom Platform) is Erlang’s standard library and framework:

Behaviors (Design Patterns)

OTP provides battle-tested implementations of common patterns:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
% gen_server - generic server
-module(key_value_store).
-behaviour(gen_server).

init([]) -> {ok, #{}}.

handle_call({get, Key}, _From, State) ->
    {reply, maps:get(Key, State, undefined), State};
handle_call({put, Key, Value}, _From, State) ->
    {reply, ok, State#{Key => Value}}.

Common behaviors include:

  • gen_server - Client-server pattern
  • gen_statem - Finite state machines
  • supervisor - Process monitoring and restart
  • application - Application packaging

Applications

Erlang organizes code into applications - self-contained units with:

  • Supervision trees
  • Configuration
  • Dependency management
  • Start/stop procedures
1
2
3
4
5
6
7
8
% Application specification
{application, my_app,
 [{description, "My Application"},
  {vsn, "1.0.0"},
  {modules, [my_module1, my_module2]},
  {registered, [my_server]},
  {applications, [kernel, stdlib]},
  {mod, {my_app, []}}]}.

Erlang vs Other Languages

FeatureErlangGoJavaElixir
Concurrency ModelActor (processes)CSP (goroutines)ThreadsActor (processes)
Shared StateNoneChannelsSynchronizedNone
Fault ToleranceBuilt-in (OTP)ManualFrameworksBuilt-in (OTP)
Hot Code SwapYesNoLimitedYes
DistributionNativeManualLibrariesNative
RuntimeBEAMNativeJVMBEAM

Common Patterns

Ping-Pong Example

The classic Erlang concurrency demonstration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-module(pingpong).
-export([start/0, ping/2, pong/0]).

ping(0, Pong_PID) ->
    Pong_PID ! finished,
    io:format("Ping finished~n");
ping(N, Pong_PID) ->
    Pong_PID ! {ping, self()},
    receive
        pong -> io:format("Ping received pong~n")
    end,
    ping(N - 1, Pong_PID).

pong() ->
    receive
        finished -> io:format("Pong finished~n");
        {ping, Ping_PID} ->
            io:format("Pong received ping~n"),
            Ping_PID ! pong,
            pong()
    end.

start() ->
    Pong_PID = spawn(pingpong, pong, []),
    spawn(pingpong, ping, [3, Pong_PID]).

Simple Key-Value Store

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-module(kvstore).
-export([start/0, put/2, get/1]).

start() ->
    register(kvstore, spawn(fun() -> loop(#{}) end)).

put(Key, Value) ->
    kvstore ! {put, Key, Value}.

get(Key) ->
    kvstore ! {get, Key, self()},
    receive
        {ok, Value} -> Value
    after 5000 ->
        timeout
    end.

loop(Store) ->
    receive
        {put, Key, Value} ->
            loop(Store#{Key => Value});
        {get, Key, From} ->
            From ! {ok, maps:get(Key, Store, undefined)},
            loop(Store)
    end.

The Erlang Shell

Erlang’s interactive shell is powerful for development:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ erl
Erlang/OTP 27 [erts-15.0]

1> 2 + 2.
4
2> List = [1, 2, 3, 4, 5].
[1,2,3,4,5]
3> [H|T] = List.
[1,2,3,4,5]
4> H.
1
5> T.
[2,3,4,5]
6> Fun = fun(X) -> X * 2 end.
#Fun<erl_eval.42.3316493>
7> lists:map(Fun, List).
[2,4,6,8,10]

Useful shell commands:

  • c(module). - Compile and load a module
  • l(module). - Load a compiled module
  • m(module). - Module info
  • h(). - Help
  • q(). - Quit

Tooling

Build Tools

  • rebar3 - Standard build tool (like Maven/Gradle)
  • mix - If using with Elixir projects
  • erlang.mk - Makefile-based alternative

Package Management

  • hex.pm - Package repository (shared with Elixir)
  • rebar3 - Dependency management

Development

  • observer - GUI for monitoring running systems
  • dialyzer - Static type analyzer
  • debugger - Built-in graphical debugger
  • common_test - Testing framework

Getting Started

Erlang files use the .erl extension. A minimal module:

1
2
3
4
5
6
% hello.erl
-module(hello).
-export([world/0]).

world() ->
    io:format("Hello, World!~n").

Compile and run:

1
2
3
4
5
6
$ erl
1> c(hello).
{ok,hello}
2> hello:world().
Hello, World!
ok

Or use escript for scripts:

1
2
3
#!/usr/bin/env escript
main(_) ->
    io:format("Hello, World!~n").

Why Learn Erlang Today?

  1. Understand concurrency - Erlang’s model influences modern languages
  2. Build reliable systems - OTP patterns apply everywhere
  3. Use Elixir better - Understanding Erlang deepens Elixir knowledge
  4. Career opportunities - High demand, limited supply of Erlang developers
  5. Think differently - Immutability and message-passing change how you design systems

The Future

While Erlang itself sees steady, conservative development, the BEAM ecosystem is thriving:

  • Elixir brings modern tooling and syntax
  • Gleam adds static typing
  • BEAM languages share the same powerful runtime

The principles Erlang pioneered - actor model, supervision trees, “let it crash” - now appear in Akka (Scala/Java), Orleans (.NET), and influence Go and Rust designs.

Continue to the Hello World tutorial to write your first Erlang program.

Timeline

1986
Joe Armstrong begins development of Erlang at Ericsson's Computer Science Laboratory
1987
First Erlang implementation as a Prolog interpreter
1991
First compiled version of Erlang using JAM (Joe's Abstract Machine)
1998
Ericsson open-sources Erlang/OTP under an open-source license after internal ban
2006
Native SMP (Symmetric Multiprocessing) support added for multi-core processors
2009
WhatsApp chooses Erlang, eventually handling billions of messages daily with small team
2012
Elixir 1.0 released, bringing modern syntax to the Erlang VM
2019
Joe Armstrong, creator of Erlang, passes away
2024
Erlang/OTP 27 released with improved JIT compilation and documentation

Notable Uses & Legacy

WhatsApp

Handles billions of messages per day with just ~50 engineers using Erlang's concurrency model.

Ericsson

Powers telecommunications infrastructure with nine-nines (99.9999999%) availability in AXD301 switch.

RabbitMQ

Popular message broker written in Erlang, used by millions for reliable messaging.

Discord

Uses Erlang for real-time communication infrastructure serving millions of concurrent users.

Riak

Distributed NoSQL database designed for high availability and fault tolerance.

CouchDB

Document-oriented database with multi-master replication built on Erlang.

Language Influence

Influenced By

Prolog Smalltalk PLEX Lisp

Influenced

Elixir Akka Clojure Rust Go

Running Today

Run examples using the official Docker image:

docker pull erlang:alpine

Example usage:

docker run --rm -v $(pwd):/app -w /app erlang:alpine escript hello.erl

Topics Covered

Last updated: