I’m a Senior engineer onboarding to Elixir, functional programming, BEAM, and the Erlang way. What follows is a little study guide put together with Claude Sonnet. Any feedback on how it could be improved would be very welcome.
Scope: server-side with a RESTful HTTP boundary. LiveView and real-time is out of scope for this exercise Pace: 6–12 hrs/week alongside full-time work and family. Total: ~14–20 weeks.
How to use this
- Work through phases in order; phase 2 makes no sense without phase 1
- Check off items as you go
- Each phase has a “done when” test; don’t move on until you pass it
- Notes fields are for your own observations
Phase 1 — Unlearning OOP
Duration: 2–3 weeks · ~6 hrs/week Goal: Accept immutability, pattern matching, and data transformation pipelines. Stop reaching for objects and mutating state.
Resources
- Programming Elixir ≥ 1.6 — Dave Thomas (O’Reilly)
- Ch 1–7: syntax, pattern matching, recursion
- Ch 8–10: maps, structs, protocols
- Skip the macros chapter for now
- Elixir School — elixirschool.com
- Basics + Collections + Enum + Pattern Matching modules only
- ACM Queue — search “[[Why functional programming matters]]” (Hughes, 1989 — via ACM DL)
Focus areas
|>pipe operator — use it everywhere until it’s instinctive- Pattern matching in function heads, not
if/else EnumandStream— replace every loop you’d have written- Immutability: every “change” returns a new value
Done when
You can write a data transformation pipeline using only |>, Enum, and pattern-matched function heads, with no imperative control flow.
Notes
Phase 2 — BEAM and OTP
Duration: 3–4 weeks · 6–8 hrs/week Goal: Think in processes, supervisors, and fault tolerance. Understand the Erlang way before leaning on Elixir abstractions.
Resources
- The Little Elixir & OTP Guidebook — Benjamin Tan Wei Hao (Manning, on O’Reilly)
- Best practical intro to OTP for someone coming from OOP
- Designing for Scalability with Erlang/OTP — Cesarini & Thompson (O’Reilly)
- Read ch 1–4 and ch 7 (supervisors) — don’t need to finish cover to cover
- Joe Armstrong’s Erlang thesis (2003) — free PDF online
- ~40 pages. The philosophical foundation. Read it once, slowly.
- Erlang stdlib source — read
gen_server.erlandsupervisor.erl$(erl -eval 'io:format("~s~n",[code:lib_dir(stdlib)])' -s init stop 2>/dev/null)/src/
Focus areas
GenServer— implement several from scratch before using frameworks- Supervision trees —
one_for_onevsone_for_allvsrest_for_one - “Let it crash” — a design philosophy, not negligence
- Message passing: processes share nothing, communicate via mailboxes
- OTP behaviours: understand what
use GenServeris actually doing
Key mental shift
Error handling in OOP: defend at every layer, catch exceptions, return nil. Error handling in BEAM: let the process crash, supervisor restarts clean state. The crash boundary is the design.
Done when
You can draw a supervision tree for a non-trivial system from memory, explain what happens when each process crashes, and implement a GenServer with init/1, handle_call/3, and handle_cast/2 without looking anything up.
Notes
Phase 3 — Real Systems (REST focus)
Duration: 4–5 weeks · 8–10 hrs/week Goal: Phoenix as a REST framework, Ecto for data, OTP for server-side state. Clean HTTP boundary, no LiveView.
Resources
- Phoenix in Action — Geoffrey Lessel (Manning, on O’Reilly)
- More REST/MVC oriented than the Pragmatic books — better fit for this scope
- Plug docs — hexdocs.pm/plug
- Read before going deep on Phoenix. Phoenix is Plug pipelines all the way down.
- Phoenix routing + controllers hexdocs — hexdocs.pm/phoenix
- The official guides are well written. Read Routing, Controllers, and JSON API sections in full.
- Ecto docs — hexdocs.pm/ecto
- Read Getting Started, Associations, and Changesets. Ecto is not an ORM — treat it as three separate tools:
Repo,Schema,Changeset.
- Read Getting Started, Associations, and Changesets. Ecto is not an ORM — treat it as three separate tools:
- Elixir Forum — forum.elixir-lang.org
- Browse “TIL” and “Show and Tell” threads — faster than blog posts for picking up idioms
Libraries to understand
Jason— JSON encoding/decoding, ubiquitousPlug.Router— understand before Phoenix abstracts it awayTaskandTask.Supervisor— ad-hoc async work without a full GenServerCachexorConCache— ETS-backed caching, useful early
Build project (required — don’t skip this)
Pick one with meaningful server-side state behind a REST boundary:
- Rate limiter API — token bucket per client, GenServer per key, HTTP endpoint to check/consume
- Job queue — workers, backpressure, dead-letter supervision, REST interface for submission and status
- URL shortener with analytics — redirect counts, per-URL stats, GenServer for hot-path caching
The project matters more than finishing the books. All three force real OTP usage with a clean HTTP surface.
What to deliberately skip (for now)
- Phoenix LiveView
- Phoenix Channels / PubSub
- Phoenix Presence
- GraphQL (Absinthe)
These are worth learning — just not yet.
Done when
Your side project is running locally, handles process crashes gracefully under load, and you can explain to a colleague why your GenServer-backed rate limiter is more reliable than a Redis-based one.
Notes
Phase 4 — Deep BEAM (ongoing)
Duration: 4–6+ weeks · fit around real work Goal: Scheduler internals, memory model, distribution, tracing, NIFs. Production-grade understanding.
Resources
- Erlang in Anger — Fred Hebert — free at erlang-in-anger.com
- Production war stories, tracing, memory debugging. Read chapter by chapter.
- BEAM Wisdoms — beam-wisdoms.clau.se
- Scheduler, GC, process internals. Bookmark and return repeatedly.
- ElixirConf talks (YouTube)
- Saša Jurić: “The Soul of Erlang and Elixir” (2019) — watch this first
- José Valim keynotes — yearly, track language direction
- ACM DL — search “BEAM garbage collection” and “Erlang scheduler”
- Rickard Green’s scheduler papers are worth the time
Focus areas
:observer— know every tab. Use it on your side project.:recon(from Erlang in Anger) — process inspection in production- Reduction counting — why BEAM preempts, why latency is predictable
- Per-process heap GC — why BEAM has predictable pauses when the JVM doesn’t
:sys.traceand:dbg— message tracing without print statements- Distribution:
Node.connect/1,:rpc, and why distributed Erlang is not magic
Done when
You can diagnose a memory leak and a process bottleneck in a running system using only BEAM tools, with no application restarts.
Notes
Appendix: Nerves (after Phase 3)
Nerves is Elixir for embedded systems — Raspberry Pi, BeagleBone, and similar hardware. It is not a separate paradigm. A Nerves app is an OTP application. Everything from phases 1–3 transfers directly.
What it is
Nerves uses Linux purely as a hardware abstraction layer — drivers and OS primitives — then boots the BEAM. You write Elixir OTP applications that run directly on the device. Firmware is immutable, minimal (20–30 MB), and deployed over-the-air via SSH.
Why it fits your REST instinct
The natural Nerves architecture is exactly what you’ve been building toward: OTP processes managing device state, a Phoenix REST API as the HTTP boundary, hardware as the downstream concern. The same mental model, different hardware layer underneath.
Hardware needed
- Raspberry Pi 3 or 4 (most common, best community support)
- MicroSD card (8 GB+)
- Wired ethernet for initial setup
Where to start
- Nerves Livebook — github.com/nerves-livebook/nerves_livebook
- Flash to a Pi, get an IEx prompt in your browser within minutes
- Note: Livebook here is a learning tool on the device, not a production pattern
- Nerves Getting Started — hexdocs.pm/nerves/getting-started.html
- Official guide, well maintained
- Underjord blog — underjord.io (Lars Wikman)
- Best narrative writing on Nerves in the ecosystem. Start with the Nerves series.
- Embedded Elixir — embedded-elixir.com
- Practical posts on Circuits, NIFs on-device, hardware drivers
Key libraries
Circuits.GPIO— digital I/O pinsCircuits.I2C/Circuits.SPI— sensor protocolsVintageNet— networking, wifi configurationNervesHub— OTA firmware updates (production)
Timing
Don’t start Nerves until your Phase 3 side project is working. Hardware friction on top of language friction stalls progress. With your hours available, target Nerves as a Phase 5 — a reward for having internalised OTP properly.
Notes
Key mental shifts (revisit monthly)
| OOP instinct | BEAM replacement |
|---|---|
| Objects hold state | Processes hold state |
| Catch exceptions everywhere | Let it crash, supervise the restart |
| Threads share memory | Processes share nothing |
| Mutate data in place | Return transformed data |
| Inheritance for reuse | Composition, behaviours, protocols |
| Lock shared resources | Eliminate sharing entirely |
| Debug with a debugger | Trace messages with :sys and :recon |
ACM-specific resources
- ACM Queue — Joe Armstrong, “Erlang” (2010) — motivation essay, not technical
- ACM DL — Ulf Wiger, “Four-fold increase in productivity and quality” — Erlang at Ericsson
- ACM DL — Rickard Green, BEAM scheduler papers
- Communications of the ACM — search “actor model” for Hewitt’s original framing
Weekly rhythm suggestion
Given full-time work + young family, protect one fixed slot. Irregular “when I have time” sessions don’t compound.
- Weekday slot (45–60 min): reading or short exercises
- Weekend slot (2–3 hrs): project work or longer reading
- Skip the guilt: a week off doesn’t reset progress. Pick up where you left off.
Checkpoints
- Phase 1 done
- Phase 2 done
- Side project running behind a REST boundary (Phase 3)
- Can diagnose live system with BEAM tools (Phase 4)
- Read Erlang in Anger cover to cover
- Nerves: Pi booting a Nerves firmware image
- Nerves: REST API controlling hardware on-device