Elixir: Foundations

Elixir is a dynamic, functional language that runs on the Erlang virtual machine (BEAM). This opening lesson explains why the language exists, what the BEAM gives you that other runtimes do not, where Elixir runs in production today, and how the rest of this track is organized.

Why Elixir Exists

Elixir was created by José Valim in 2011–2012 with a specific observation behind it: the Erlang platform had solved the hardest problems in server software — concurrency, distribution, and fault tolerance — decades before anyone else, but its syntax and tooling felt alien to programmers raised on mainstream languages. Elixir keeps the runtime and swaps the surface: a Ruby-inspired syntax, a modern standard library, a build tool (mix), a package registry (hex), and first-class documentation tooling (ExDoc).

Three design commitments follow from that history, and every chapter of this track builds on them:

  • Immutability by default. Data never changes under you; every transformation returns a new value. Whole categories of race conditions and defensive copies disappear.
  • Processes, not threads. Concurrency is expressed as thousands of isolated lightweight processes that share nothing and talk only through messages.
  • Let it crash. Instead of defensive error handling everywhere, code that hits an unexpected state dies immediately and a supervisor restarts it in a clean state.

What you give up

Honesty about trade-offs is part of learning a language well. Elixir is not the fastest language for raw numeric loops (Rust, C, and even Julia beat it), it is garbage-collected (you do not control memory the way Rust's ownership model does), and its dynamic typing means fewer compile-time guarantees than statically typed languages — although the gradual set-theoretic type system introduced in Elixir 1.18 and expanded since now catches many errors at compile time. The comparison lesson examines these trade-offs in detail.

The BEAM Runtime

The Erlang virtual machine — the Bogdan/Björn's Erlang Abstract Machine, or BEAM — is the real foundation of every Elixir program. It was designed at Ericsson in the 1980s to run telephone switches that could never go down, and that goal shaped every layer:

Layers of the BEAM runtime: Elixir code compiled to BEAM bytecode, on a preemptive scheduler with per-process heaps
Fig. 1 — Elixir compiles to BEAM bytecode like Erlang does; OTP supplies the process and supervision machinery both languages share.
  • Preemptive scheduling. Each process gets a small budget of reductions (roughly function calls). A tight loop cannot starve other processes — the scheduler preempts it. There is no async/await split: every function is written synchronously.
  • Isolated heaps. Every process owns its memory and is garbage-collected individually, so a GC pause in one process never freezes another. Heaps are tiny (a few KB) and collected in microseconds.
  • Message passing. Processes share no state. Data sent between processes is copied (except large binaries, which are reference-counted on a shared heap).
  • Distribution. Two BEAM nodes that share a cookie form a cluster and can message each other transparently — distribution is a language-level feature, not a library you bolt on.

A first process

This is a complete, runnable Elixir script. It spawns a process, sends it a message, and prints the reply. Do not worry about every keyword yet — the point is that concurrency fits in twelve readable lines with no locks, no thread pool, and no callback pyramid.

# counter.exs — run with: elixir counter.exs
defmodule Counter do
  # A process loops forever, waiting for one message at a time.
  def loop(count) do
    receive do
      # A synchronous request: the caller includes its own pid (`self()`).
      {:increment, caller} ->
        send(caller, {:ok, count + 1})
        loop(count + 1)              # tail-call: keeps the process alive
    end
  end
end

pid = spawn(Counter, :loop, [0])   # start with count = 0
send(pid, {:increment, self()})    # self() is this process's pid

receive do                         # wait for the reply
  {:ok, new_count} -> IO.puts("Count is now #{new_count}")
end

Where Elixir Runs Today

Elixir is a production language with public, documented deployments at serious scale. These case studies (from the official case-study page) are worth reading once you finish phase 4:

ProductWhat Elixir does thereWhy the BEAM fits
DiscordReal-time messaging fan-out across millions of concurrent usersMillions of lightweight processes map naturally onto millions of connections
Supabase RealtimeWebSocket broadcast, presence, and Postgres change streamsLong-lived connections plus fan-out are the BEAM's home turf
Plausible AnalyticsThe entire analytics stack, cookie-free and self-hostableSmall team, high throughput, boring operations
RemoteGlobal employment platform, one Elixir monolith, ~300 engineersContexts and process boundaries scale with the org chart
BBCWeb infrastructure serving most of its trafficTraffic spikes handled with graceful degradation
FirezoneOpen-source remote-access platform (Elixir + WireGuard)Connection management and policy checks per client

How the Track Is Organized

The track moves through seven phases. Each phase assumes the previous one; each lesson ends with practice tasks and links to runnable demos in the Lab Examples page.

PhaseContentsAfter it you can…
1. FoundationsOverview, comparison with Python/Rust, toolchain, syntaxRun Elixir programs and explain when to choose it
2. Language CoreData, pattern matching, functions, collections, polymorphism, errors, binariesWrite idiomatic Elixir for everyday logic
3. Metaprogramming & ToolingMacros, Mix projects, testing, IEx and static analysisShip and maintain a real codebase
4. Concurrency & OTPProcesses, tasks, GenServer, supervision, applications, distributionDesign fault-tolerant concurrent systems
5. Production & EcosystemEcto, Phoenix, LiveView, architecture, performance, numerical workBuild and operate production services
6. Practice & Demos36 single-file demos, study projects, open-source reading listConsolidate everything by reading and modifying real code
7. ReferenceVerified docs, tutorials, and community linksKeep learning after the track ends

Practice

  1. Run the counter script above (it only needs Elixir installed — see Install & Toolchain), then change it so the process also answers :get messages with the current count.
  2. Explain in two sentences why a GC pause in one BEAM process cannot freeze another.
  3. Pick one case study from the table and name the property of the BEAM it depends on most.
  4. Skim the comparison lesson and write down one thing Elixir does that Python cannot do without extra machinery.

Next: Elixir vs Python vs Rust