Discover / Elixir programming / Reading path

Learn Elixir: the best books to read in order

@codesherpaBeginner → Expert
8
Books
58
Hours
4
Stages
Not yet rated

This curriculum takes you from zero Elixir knowledge to production-ready Phoenix applications and deep OTP/concurrency mastery. Each stage builds directly on the last: you first internalize functional thinking and Elixir syntax, then tackle real-world web development with Phoenix, and finally go deep into the concurrent, fault-tolerant internals that make Elixir uniquely powerful.

1

Functional Foundations & Elixir Basics

Beginner

Understand functional programming principles, Elixir syntax, pattern matching, recursion, and the core data structures — the essential vocabulary for everything that follows.

Study plan for this stage

Pace: 4–5 weeks, ~40–50 pages/day. Start with "Programming Elixir ≥ 1.6" (Chapters 1–10, ~2.5 weeks), then transition to "Elixir in Action" (Chapters 1–4, ~2 weeks) for deeper context and practical patterns.

Key concepts
  • Functional programming paradigm: immutability, first-class functions, and avoiding side effects as core design principles
  • Pattern matching: the unified mechanism for destructuring, control flow, and function dispatch in Elixir
  • Recursion and tail-call optimization: how to replace loops and build efficient iterative processes
  • Core data structures: lists, tuples, maps, and strings—their trade-offs and idiomatic usage patterns
  • Pipe operator (|>) and function composition: reading and writing data transformations as readable chains
  • Anonymous functions, higher-order functions, and closures: treating functions as first-class values
  • The Elixir module system and function definitions: organizing code with named functions and guards
  • Atoms, pattern matching in function heads, and the match operator (=): Elixir's approach to control flow and data binding
You should be able to answer
  • Explain the core difference between imperative and functional programming, and why immutability matters in Elixir.
  • How does pattern matching work in Elixir, and how is it used for both data destructuring and control flow?
  • Write a recursive function that processes a list without using built-in iteration functions. Why is tail-call optimization important?
  • Compare lists, tuples, and maps: when would you use each, and what are their performance characteristics?
  • How does the pipe operator (|>) improve code readability, and how does it relate to function composition?
  • What is the difference between anonymous functions and named functions? When would you use each?
Practice
  • Work through all code examples in 'Programming Elixir' Chapters 1–5 in the Elixir REPL; modify them to deepen understanding (e.g., change function parameters, test edge cases).
  • Implement a recursive function to calculate factorial, sum a list, and reverse a list—then rewrite using Enum module functions to see the difference.
  • Build a simple data structure (e.g., a person record using maps/tuples) and write pattern-matching functions to extract and transform its fields.
  • Create a module with 5–6 small functions that use guards, multiple clauses, and pattern matching in function heads; test each with various inputs.
  • Chain 3–4 data transformations using the pipe operator (e.g., parse a list of strings, filter, map, and sort) and compare readability to nested function calls.
  • Write a higher-order function that takes another function as an argument and applies it to a collection; use both named and anonymous functions as arguments.

Next up: This stage equips you with Elixir's syntax and functional building blocks; the next stage will apply these foundations to concurrent programming, process management, and the actor model—where immutability and recursion become essential for writing reliable, scalable systems.

Programming Elixir ≥ 1.6
Dave Thomas · 2018 · 398 pp

The canonical starting point for Elixir — Dave Thomas covers the language from scratch with a functional-first mindset, making it the ideal first book for any Elixir learner.

Elixir in action
Sas̄a Jurić · 2015 · 351 pp

Reinforces and deepens the fundamentals while introducing processes and the BEAM runtime early, bridging the gap between pure syntax and real Elixir thinking.

2

Web Development with Phoenix

Intermediate

Build full-featured web applications using the Phoenix framework, understanding its MVC structure, Ecto for databases, channels for real-time features, and LiveView for interactive UIs.

Study plan for this stage

Pace: 8–10 weeks, ~40–50 pages/day (mix of reading and hands-on coding)

Key concepts
  • LiveView's stateful server-side rendering model and how it eliminates the need for JavaScript for many interactive features
  • The component lifecycle in LiveView: mount, render, handle_event, and handle_info
  • Building real-time features with Phoenix Channels and integrating them with LiveView
  • Ecto's query API, changesets, and validations for robust data handling
  • The relationship between Ecto schemas, migrations, and the database layer in a Phoenix application
  • MVC architecture in Phoenix: routers, controllers, views, and how LiveView fits into this pattern
  • Form handling and two-way data binding in LiveView
  • Testing strategies for LiveView components and Ecto queries
You should be able to answer
  • How does LiveView's server-side rendering model differ from traditional client-side JavaScript frameworks, and what are the trade-offs?
  • Explain the lifecycle of a LiveView component: what happens during mount, render, and when a user event is handled?
  • How do you use Ecto changesets to validate and transform user input before persisting to the database?
  • What is the role of Phoenix Channels in real-time communication, and how can you integrate channels with LiveView?
  • How do you structure a multi-step form or complex UI interaction using LiveView's stateful approach?
  • What are the key differences between Ecto queries using the query API versus raw SQL, and when would you use each?
Practice
  • Build a simple real-time counter or todo list app using LiveView, implementing mount, render, and handle_event callbacks
  • Create a multi-step form in LiveView with validation at each step using Ecto changesets
  • Write Ecto migrations and schemas for a small project (e.g., blog with posts and comments), then query them using the Ecto query API
  • Implement a presence feature using Phoenix Channels to show which users are currently viewing a page
  • Build a search feature in LiveView that filters results in real-time as the user types, backed by Ecto queries
  • Create a LiveView component that manages its own state and can be reused across multiple pages in your application
  • Write tests for a LiveView component covering mount, render, and event handling scenarios
  • Implement a real-time notification system using Channels and LiveView that updates the UI when events occur

Next up: This stage equips you with the core skills to build interactive, data-driven web applications in Phoenix; the next stage will likely deepen your expertise in deployment, performance optimization, advanced testing patterns, and architectural decisions for scaling production systems.

Programming Phoenix LiveView
Bruce Tate · 2022 · 370 pp

The most modern and practical Phoenix book, covering both the core framework and LiveView — read this first in the web stage to get a complete, up-to-date picture of Phoenix development.

Programming Ecto
Darin Wilson · 2019 · 244 pp

Ecto is Phoenix's database layer and deserves dedicated study; this book goes far deeper than any Phoenix overview, giving you mastery of queries, changesets, and data modeling.

3

Concurrency, OTP & Fault Tolerance

Intermediate

Deeply understand Elixir processes, GenServer, Supervisors, and the OTP framework to design robust, concurrent, and self-healing systems.

Study plan for this stage

Pace: 8–10 weeks, ~40–50 pages/day (mix of reading and hands-on coding)

Key concepts
  • Elixir processes as lightweight, isolated units of concurrency with message-passing communication
  • GenServer behavior module for building stateful, request-response concurrent components
  • Supervisor trees and hierarchical fault tolerance: letting processes fail and restart automatically
  • OTP principles: isolation, fault tolerance, and the 'let it crash' philosophy
  • Linking and monitoring processes to detect failures and trigger recovery
  • State management and message handling patterns in concurrent systems
  • Building production-ready systems with OTP abstractions rather than raw processes
  • Designing systems that self-heal through supervisor restart strategies
You should be able to answer
  • What is a process in Elixir, and how does message passing differ from shared memory concurrency?
  • When and why would you use GenServer instead of spawning raw processes?
  • How do Supervisors enable fault tolerance, and what are the different restart strategies?
  • What is the difference between linking and monitoring a process, and when would you use each?
  • How does the OTP framework guide you to design systems that are robust and maintainable?
  • What does 'let it crash' mean, and how does it change the way you think about error handling?
Practice
  • Build a simple counter GenServer that handles increment/decrement messages and maintains state across calls
  • Create a supervisor that manages multiple worker processes and experiment with different restart strategies (one_for_one, one_for_all, rest_for_one)
  • Implement a linked process pair where killing one process triggers the other to crash, then modify it to use monitoring instead
  • Design a small OTP application with a root supervisor, multiple GenServers, and a simple CLI to interact with them
  • Write a GenServer that processes a queue of tasks, handles failures gracefully, and logs all state transitions
  • Build a fault-tolerance test: deliberately crash a supervised process and verify that the supervisor restarts it correctly

Next up: This stage equips you with the mental models and patterns to build concurrent, self-healing systems; the next stage will likely deepen your ability to scale these systems, handle distribution across multiple nodes, and manage complex state and communication patterns in production environments.

The Little Elixir & OTP Guidebook
Benjamin Tan Wei Hao · 2016 · 296 pp

A gentle but thorough introduction to OTP concepts — GenServers, Supervisors, and application design — making it the perfect first step into the concurrency stage.

Designing Elixir Systems With OTP
II James Edward Gray · 2019 · 248 pp

Focuses on how to architect larger Elixir systems using OTP patterns and layers, building directly on the foundational OTP knowledge from the previous book.

4

Advanced Elixir & Production Mastery

Expert

Master metaprogramming, performance, distributed systems, and the craft of writing idiomatic, production-grade Elixir at scale.

Study plan for this stage

Pace: 8–10 weeks, ~40–50 pages/day (with 2–3 days per week for hands-on exercises and projects)

Key concepts
  • Macros, compile-time code generation, and quote/unquote mechanics in Elixir
  • Domain-specific languages (DSLs) and metaprogramming patterns for reducing boilerplate
  • Performance profiling, optimization strategies, and benchmarking in production Elixir
  • Building robust GraphQL APIs with Absinthe: schema design, resolvers, and middleware
  • Subscriptions, real-time data, and WebSocket handling in GraphQL
  • Error handling, validation, and authentication patterns in production GraphQL services
  • Distributed systems concepts: supervision trees, fault tolerance, and scaling Elixir applications
  • Idiomatic Elixir patterns: code organization, testing strategies, and maintainability at scale
You should be able to answer
  • What is the difference between quote and unquote, and when would you use each in macro development?
  • How can you use macros to build a domain-specific language (DSL) that reduces boilerplate in your application?
  • What are the key performance bottlenecks in Elixir applications, and what tools and techniques can you use to identify and fix them?
  • How do you design a GraphQL schema in Absinthe that is both flexible and maintainable, and what role do resolvers play?
  • How would you implement real-time features in a GraphQL API using Absinthe subscriptions, and what are the architectural considerations?
  • What patterns and best practices should you follow when building authentication, authorization, and error handling into a production GraphQL service?
  • How do you structure a distributed Elixir application to handle failures gracefully and scale horizontally?
Practice
  • Build a custom macro that generates CRUD functions for a given module, reducing boilerplate code by at least 50%.
  • Create a small DSL using quote/unquote that allows developers to define validation rules in a readable, declarative syntax.
  • Profile an existing Elixir application using tools like :fprof or :eprof; identify at least one bottleneck and implement an optimization.
  • Design and implement a GraphQL schema for a real-world domain (e.g., e-commerce, social media) using Absinthe, including at least 5 types and 10 fields.
  • Implement a GraphQL mutation with nested input types, custom error handling, and validation; ensure it returns meaningful error messages.
  • Build a GraphQL subscription that pushes real-time updates to multiple clients; test with a WebSocket client.
  • Implement middleware in Absinthe to handle authentication, rate limiting, or request logging across multiple resolvers.
  • Refactor a monolithic GraphQL resolver into smaller, testable functions following idiomatic Elixir patterns; write comprehensive tests.

Next up: This stage equips you with the advanced metaprogramming and production-grade API design skills needed to architect complex, scalable systems; the next stage will likely focus on deploying, monitoring, and maintaining these systems in real-world environments at enterprise scale.

Metaprogramming Elixir
Chris McCord · 2015 · 128 pp

Written by the creator of Phoenix, this book unlocks Elixir's macro system — essential for understanding how Phoenix and many libraries work under the hood, and for writing expressive DSLs.

Craft GraphQL APIs in Elixir with Absinthe: Flexible, Robust Services for Queries, Mutations, and Subscriptions
Bruce Williams · 2018 · 304 pp

A real-world capstone that applies advanced Elixir and Phoenix knowledge to building production GraphQL APIs, solidifying architectural thinking with a concrete, modern use case.

Discussion

Keep reading

Paths that share books, cover the same subject, or open a related topic.

More on Clojure programming

Learn Clojure: the best books in order

Beginner8books59 hrs4 stages
More on Flutter and Dart development

Learn Flutter and Dart: the best books in order

Beginner4books22 hrs4 stages
More on GraphQL APIs

Learn GraphQL: the best books to read in order

Beginner3books5 hrs4 stages