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.
In Practice: Navigating Nuance – A Focus on Feedback & Collaboration
For non-native English speakers learning professional terminology in software development, particularly within the Elixir and OTP ecosystem, it’s not just about knowing what a term means; it’s profoundly about understanding how it’s used, the subtle implications of phrasing, and how to effectively communicate technical ideas. The initial vocabulary – GenServer, Supervisors, ETS – provides a solid foundation, but translating that knowledge into clear, constructive communication is where many developers struggle. A simple command like mix run doesn’t require much nuanced explanation, but a code review comment or a request for changes can be fraught with potential misunderstanding if the language isn’t precise and empathetic.
Consider this scenario: you’ve submitted a pull request containing a new feature for user authentication. The reviewer, a senior engineer named Sarah, leaves a comment on your PR description: “This is good, but could we use a more robust approach to password hashing? Consider Argon2 or bcrypt – it’s crucial for security best practices.” The immediate reaction might be defensiveness, especially if the original implementation was deemed sufficient. However, Sarah’s phrasing isn’t an accusation; it’s a suggestion framed within a critical concern: security. The key difference lies in the implied reasoning and the gentle introduction of alternative solutions. It’s about conveying risk assessment, not simply pointing out a perceived error. Similarly, in Slack conversations discussing performance bottlenecks, saying “This code is slow” isn’t helpful. Instead, phrasing it as “The latency on this endpoint exceeds acceptable thresholds during peak load – let’s investigate potential caching strategies” immediately shifts the discussion towards a problem-solving approach.
Furthermore, OTP concepts like Supervisors and fault tolerance naturally lend themselves to more complex sentence structures. Explaining that “We’re using a ‘one-for-one’ supervisor strategy with a restart: of type except to ensure resilience against process failures” requires careful articulation, avoiding jargon where possible and focusing on the outcome – maintaining system availability. It’s about translating technical logic into understandable terms for colleagues who may not be deeply familiar with the intricacies of OTP. Learning how to frame discussions around potential impact— “This change might introduce a dependency on external libraries” or “We need to consider the implications of this approach on resource utilization”— is paramount to successful collaboration.
# Example: A simplified Elixir Supervisor setup (illustrative)
defmodule MySupervisor do
use Supervisor
def start_link(opts \\ []) do
Supervisor.start_link(__MODULE__, opts, name: __MODULE__)
end
def handle_info(message, pid) when is_atom(message) do
# Handle the message - could be a restart request, etc.
IO.puts "Received message: #{inspect(message)}"
Supervisor.stop_link(__MODULE__, :restart) # Example restart action
end
end
Ultimately, mastering professional English in Elixir and OTP isn’t just about vocabulary; it’s about developing a communication style that fosters trust, encourages collaboration, and ensures everyone understands the technical challenges and solutions being discussed. It’s recognizing that clear, constructive feedback is an investment in project success, not a criticism of individual work.