Take one language all the way to a project
Elixir and Erlang stay separate across Scratch, Foundation, Intermediate, and Project. Finish Foundation in either language to enter the BEAM mainline.
Start with values, types, and functions. Once the syntax feels familiar, see how processes work together.Elixir and Erlang each have their own beginner path. Pick one. You do not need to learn both at once.
The language paths teach code, tests, and project tools. The BEAM path deals with concurrency, failure, and recovery. After the capstone, Case Chat shows those choices inside a working voice service.
Elixir and Erlang stay separate across Scratch, Foundation, Intermediate, and Project. Finish Foundation in either language to enter the BEAM mainline.
Each station starts with a failure: mismatched replies, mailbox growth, restart scope, or exhausted capacity. Switch the code between Elixir and Erlang.
Combine a bounded queue, supervision, idempotency, retry, and node loss in one project. Prove it with failure experiments.
Follow one voice session through a real project, then read Mix, supervision, WebSockets, recorder processes, and browser code in context. The case study is currently written in Chinese.
Every lesson has code you can change. If the output surprises you, read it, then change one thing. That makes the cause easier to find.
Every new idea comes with a small example for IEx or erl. The page also shows messages moving between processes.
Solve the same problem in Elixir and Erlang. The syntax differs, but both run on the BEAM.
Answer a question, translate a snippet, or change one condition. A wrong answer is still useful: it tells you what to try next.
Delay a reply, fill a mailbox, or stop a process. Watch what happens—and notice what the experiment cannot prove.
Once you know one language, you do not need to relearn everything. Spot the same values, patterns, and functions, then see how both use BEAM processes and messages.
Start with the data and functions you already understand.
Find the same atoms, tuples, patterns, and modules.
Learn processes, messages, and supervision together.
:okthe same atom labelokFoo.bar()the same module underneath'Elixir.Foo':bar():lists.reverse(xs)the same Erlang toollists:reverse(Xs)fn x -> x * 2 endboth create an anonymous functionfun(X) -> X * 2 endGenServerthe same OTP behaviourgen_serverThe core has 7 stations, plus 2 placement reviews and 2 bilingual extensions. Finish processes, OTP, capacity, and the capstone in your familiar language. Add shared terms and adapters when you need interoperability.
Check the tools, then use a first process to separate functions, state ownership, PIDs, and messages.
[Prerequisite diagnostic] Why can this computer not find Erlang, Elixir, or Mix: is a tool missing, are the versions incompatible, or is PATH stale?
[Prerequisite diagnostic] When mix test fails, how can you first tell whether the problem belongs to the command, project directory, runtime, or code?
When several senders update a counter at once, who owns the state, and how can we spot messages that the mailbox never handles?
Review the other language only when you need it. A single-language learner can keep moving.
[Optional language review] How can we split a log cleaner that meets blank lines, unknown levels, and malformed input into independently testable stages?
[Optional language review] How can we rebuild the same log tool in Erlang and make both implementations obey one input-output contract?
Enter OTP through late replies, mailbox growth, restart scope, and capacity limits.
When concurrent replies can be late or out of order and the service process can exit, how do we make sure each caller gets its own result?
After replacing a hand-written loop with GenServer, how should call and cast be divided, and how can slow work be kept from filling the mailbox?
When Registry, a worker supervisor, and a dispatcher depend on one another, which restart strategy recovers enough without restarting too much?
When ten thousand URL checks arrive in a burst, how do we limit running and waiting work while returning timeout, failure, or busy clearly?
The core runs from node loss to the capstone. Take the shared-term and adapter branch only when two languages must work together.
If a remote worker disconnects midway through a request, what result does the caller get, and how does the system mark stale data, retry, and avoid duplicate execution?
When an order receives pay, ship, cancel, and duplicate events, how can Elixir and Erlang follow the same state-machine protocol?
Between an Elixir API and an Erlang worker, how do we fix the text, data-shape, and error protocol so record or Unicode changes do not spread?
How do we keep capacity and retries bounded through queue-full, worker exit, timeout, and node loss—and state honestly what is still missing across a full VM restart?
Every process has a mailbox. Send a few messages, then handle them one by one. Turn on “drop reply” to see what happens when work is handled but no answer comes back.
01Waiting for the client to send a message
This browser model shows how messages move. It is not a real Erlang VM. Use it to watch messages queue up and receive replies. The lesson pages also include code you can run in IEx or erl.
Build the core in either Elixir or Erlang: bound the queue, name each failure outcome, and cap retries. Add the other language only after the interoperability lessons.
See the requirementsUse the official docs, a tutorial, or a community. Each link says what it is good for, so you do not have to open them all.
This preview shows two from each group. Search the full toolbox by name or purpose.
Open the learning toolbox