English for Elixir and OTP Developers
Master the vocabulary of Elixir and OTP: GenServer, Supervisor strategies, ETS, Registry, DynamicSupervisor, BEAM VM, hot code reloading, and fault tolerance patterns.
Elixir and OTP (Open Telecom Platform) have a distinctive vocabulary that reflects decades of distributed systems thinking from the Erlang ecosystem. When you join an Elixir team or contribute to an Elixir open source project, knowing how to talk about GenServers, supervision trees, and fault tolerance in English is just as important as knowing how to write the code. This post covers the essential vocabulary for Elixir and OTP discussions.
Key Vocabulary
GenServer — A generic server behaviour in OTP that abstracts the common patterns of a server process: state management, synchronous and asynchronous message handling, and integration with supervisors. Example: “We implemented the session cache as a GenServer that holds state in memory and handles cache invalidation messages.”
handle_call vs handle_cast — Two of the main callback functions in a GenServer. handle_call handles synchronous requests — the caller waits for a reply. handle_cast handles asynchronous messages — the caller does not wait. Example: “We use handle_call for reads that need a response and handle_cast for fire-and-forget writes to the audit log.”
Supervisor strategies:
- one_for_one — If a child process dies, only that process is restarted. The most common strategy. Example: “We use
one_for_onebecause each worker is independent — a crash in one should not affect the others.” - one_for_all — If any child dies, all children are stopped and restarted. Used when processes are tightly coupled. Example: “The database connection pool and the cache process must stay in sync, so we use
one_for_all.” - rest_for_one — If a child dies, that child and all children started after it are restarted. Used when later processes depend on earlier ones. Example: “
rest_for_oneis appropriate here because the router process depends on the connection pool being available.”
ETS (Erlang Term Storage) — An in-memory, process-independent key-value store built into the BEAM VM. ETS tables survive the death of the process that created them (if set up correctly) and can be accessed concurrently. Example: “We store the rate-limiting counters in ETS because it gives us concurrent read access without going through a single GenServer bottleneck.”
Registry — An OTP module for registering processes under human-readable names within a cluster or node, supporting distributed process lookup. Example: “Each user session process registers itself in the Registry under the user ID, so we can look it up and send messages directly.”
DynamicSupervisor — A supervisor that starts child processes dynamically at runtime rather than from a static list defined at startup. Example: “We use a DynamicSupervisor to start a new GenServer for each incoming WebSocket connection, then terminate it when the connection closes.”
OTP application — In Elixir/Erlang, an application is a reusable component with its own supervision tree, configuration, and lifecycle. Your whole Elixir project is typically one or more OTP applications. Example: “The payments module is packaged as a separate OTP application so it can be included as a dependency.”
BEAM VM — The Erlang virtual machine that runs Elixir code. BEAM is designed for high concurrency, fault tolerance, and soft real-time systems. Example: “The BEAM VM schedules millions of lightweight processes across CPU cores, which is why Elixir handles so many concurrent connections efficiently.”
Hot code reloading — The ability to update running code in a BEAM system without stopping the system. Example: “OTP supports hot code reloading — we can deploy a new version of the GenServer without dropping active connections.”
Fault tolerance — The ability of a system to continue operating correctly when individual components fail. In OTP, this is achieved through supervision trees that restart failed processes. Example: “The supervision tree is designed for fault tolerance — if the database connection drops, the supervisor restarts the connection process automatically without affecting the rest of the application.”
How to Use This in Practice
OTP discussions often revolve around the supervision tree — the hierarchy of supervisors and workers that defines how your application recovers from failures. When reviewing or discussing a supervision tree, ask: “What strategy does this supervisor use?” and “What is the expected restart behaviour when this process crashes?”
The choice between handle_call and handle_cast is a common design decision. The rule of thumb: use handle_call when the caller needs the result (reads, validations), use handle_cast when the caller does not (logging, notifications, background writes).
ETS vs GenServer for shared state is another frequent discussion: “If multiple processes need to read this data concurrently, ETS will scale better than routing every read through a single GenServer.” Knowing when to use each shows OTP experience.
Example Conversation
Developer (Serhii): “I’m designing the rate limiter. Should I use a GenServer or ETS for the counters?”
Senior Engineer: “How many concurrent reads are you expecting?”
Serhii: “Potentially thousands per second across all users.”
Senior Engineer: “Then ETS is the better choice. If you go through a GenServer, every read serialises through a single process — that’s a bottleneck. With ETS, you get concurrent reads without a process boundary. Just make sure the table owner is supervised so the data isn’t lost if the owner crashes.”
Serhii: “Makes sense. Should the owner be a dedicated GenServer started by a DynamicSupervisor?”
Senior Engineer: “No — for a single shared table, a regular supervised GenServer under a one_for_one strategy is enough. DynamicSupervisor is for when you need to spin up processes on demand at runtime.”
Practice Tips
-
Read the OTP design principles guide: The official Erlang OTP Design Principles documentation explains GenServer, Supervisor, and Application in depth with diagrams. Read the supervisor strategies section and write a one-sentence description of when you would use each strategy.
-
Trace a supervision tree: Find an open source Elixir project on GitHub (Phoenix, Oban, or Nerves are good examples). Read the
application.exfile and identify the top-level supervisor. Try to describe in English what each child process does and what restart strategy is used. -
Explain handle_call vs handle_cast to a colleague: Without looking at the definitions, explain the difference between the two callbacks to someone (or to yourself out loud). If you can explain it clearly in English without using the words “synchronous” or “asynchronous,” you have a deep enough understanding to use the vocabulary naturally in team discussions.