Welcome to the first article in the Elixir Lessons series! This comprehensive series will explore the Elixir ecosystem, from basic language concepts to advanced OTP patterns and Phoenix framework development.
🎯 Series Overview
This is Lesson 01 of a series where I’ll try to cover part of the Elixir ecosystem. My plan so far (may change in the future):
🧠 Elixir Language Fundamentals (Lessons 1-8)
- Introduction to Elixir and BEAM, data types, pattern matching, functions and modules, recursion, pipes, immutability, control flow
🔄 Concurrency and Processes (Lessons 9-13)
- Processes, supervisors, tasks, agents, GenServer, OTP
💾 Persistence with Ecto (Lessons 14-20)
- Mix fundamentals, installation, schemas, CRUD, relationships, queries, changesets, transactions
🌐 Web Development with Phoenix (Lessons 21-28)
- Structure, routes, views, contexts, forms, WebSockets, LiveView, security
⚙️ Testing and Best Practices (Lessons 29-31)
- ExUnit, mocks, coverage
🚀 Ash Framework (Lessons 32-37)
- Declarative philosophy, resources, APIs, authorization, integrations
🌍 Production and DevOps (Lessons 38-42)
- Configuration, releases, telemetry, logging, deployment
🧠 Advanced / Mastery (Lessons 43-47)
- Metaprogramming, DSLs, NIFs, performance optimization, distributed architectures
📚 Cross-cutting Topics (integrated from the beginning)
- IEx: Commands, debugging, help (from Lesson 1)
- Mix: Project management, dependencies (from Lesson 14)
- Documentation: @moduledoc, @doc, doctests (from Lesson 1)
✨ What is Elixir?
Elixir is a dynamic, functional, and concurrent programming language designed for building maintainable and scalable applications. Created by José Valim in 2011, Elixir leverages the proven Erlang Virtual Machine (BEAM) to provide:
- Fault tolerance through the “let it crash” philosophy
- Massive concurrency with lightweight processes
- Real-time systems with soft real-time guarantees
- Distributed computing capabilities from the start
- Hot code swapping for zero-downtime deployments
🌟 Elixir’s Key Features
- Functional: Functions are at the center of the language, with no mutable state
- Concurrent: You can create thousands of lightweight processes for concurrent tasks effortlessly
- Scalable: Designed to handle thousands of users or requests simultaneously
- Fault-tolerant: Thanks to the “let it crash” architecture and supervisors
- Productive: With tools like
iex,mix, and clean syntax, developing in Elixir is very pleasant - Immutable: All data structures are immutable by default
⚙️ The BEAM Virtual Machine
The BEAM (Bogdan/Björn’s Erlang Abstract Machine) is the virtual machine that runs Elixir code. It was originally designed for telecommunications, where reliability and performance were critical. Understanding BEAM is crucial for Elixir developers because:
Key BEAM Features:
- Actor Model: Everything runs in lightweight processes
- Preemptive Scheduling: Processes can’t monopolize the scheduler
- Per-Process Garbage Collection: Prevents global pauses
- Fault Isolation: Process crashes don’t affect other processes
- Distribution: Built-in support for distributed systems
Why BEAM Matters:
# Example: Creating millions of processes is trivial
for i <- 1..1_000_000 do
spawn(fn ->
receive do
msg -> IO.puts("Process #{i} received: #{msg}")
end
end)
end
Thanks to BEAM:
- Processes are isolated and can fail without affecting others
- There’s per-process garbage collection, which improves efficiency
- You can have thousands or millions of concurrent processes
🏗️ Elixir’s Design Principles
1. Immutability
All data structures in Elixir are immutable by default:
# This creates a new list, doesn't modify the original
original_list = [1, 2, 3]
new_list = [0 | original_list]
# original_list is still [1, 2, 3]
2. Pattern Matching
Pattern matching is one of Elixir’s most powerful features:
# Pattern matching examples
{:ok, result} = {:ok, "success"}
[head | tail] = [1, 2, 3, 4]
%{name: name, age: age} = %{name: "John", age: 30}
3. Fault Tolerance
The “let it crash” philosophy encourages failing fast and recovering quickly:
# Supervisor will restart this process if it crashes
defmodule MyWorker do
use GenServer
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
def init(state) do
{:ok, state}
end
# If this crashes, supervisor will restart it
def handle_call(:dangerous_operation, _from, state) do
# Some risky operation that might fail
{:reply, :ok, state}
end
end
🔍 Use Cases Where Elixir and BEAM Shine
Perfect for:
- Chat and messaging systems (like WhatsApp): Many users, real-time messages
- APIs and microservices: Highly concurrent and scalable
- IoT and data processing: Handling large event streams
- Trading platforms or real-time games: Low latency and massive concurrency
- Industrial automation and telecommunications: Robustness and fault tolerance
- Web applications with Phoenix framework
- Distributed systems
- Financial systems requiring fault tolerance
Industry Adoption:
- Discord: Handles millions of concurrent users
- WhatsApp: Built on Erlang/OTP (Elixir’s foundation)
- Pinterest: Uses Elixir for notification systems
- Bleacher Report: Real-time sports updates
- Lonely Planet: Travel platform backend
📚 Installing Elixir
Recommended Installation with asdf
It’s recommended to use asdf, a version manager for many languages:
# Install asdf (macOS with Homebrew)
brew install asdf
# Add plugins
asdf plugin-add erlang
asdf plugin-add elixir
# Install compatible versions
asdf install erlang 28.0.1
asdf install elixir 1.18.4-otp-28
asdf global erlang 28.0.1
asdf global elixir 1.18.4-otp-28
Alternative Installation
# macOS with Homebrew
brew install elixir
# Ubuntu/Debian
sudo apt-get install elixir
✍️ Your First Elixir Program
Introduction to IEx (Interactive Elixir)
IEx is Elixir’s interactive shell, a fundamental tool you’ll use constantly during development. It’s your best friend for experimenting, testing code, and debugging.
Basic IEx Commands:
- Open IEx:
iexThis will open the Elixir REPL (interactive shell).
- Hello World:
IO.puts("Hello World")You’ll see on screen:
Hello World :ok - Useful IEx commands:
# See help h() # Help about a specific function h(IO.puts) # See information about a type i("hello") # Clear screen clear() # Recompile code (useful for development) recompile() # Exit IEx # Ctrl+C twice or System.halt() - Variables and evaluation:
# Variables are assigned and evaluated immediately name = "Elixir" age = 14 # years since 2011 IO.puts("#{name} is #{age} years old") # The value of the last expression is shown automatically 5 + 3 # → 8
Hello World with Modules
Modules organize code in Elixir. From the first module, it’s important to document our code well:
# hello_world.ex
defmodule HelloWorld do
@moduledoc """
Module that provides different ways to greet.
This is our first module in Elixir, designed to
demonstrate basic documentation and function concepts.
"""
@doc """
Greets a person informally.
## Examples
iex> HelloWorld.greet("Ana")
"Hello, Ana!"
iex> HelloWorld.greet("Elixir")
"Hello, Elixir!"
"""
def greet(name) do
"Hello, #{name}!"
end
@doc """
Greets formally using first and last name.
## Parameters
- first_name: String with the person's first name
- last_name: String with the person's last name
## Examples
iex> HelloWorld.greet_formal("Ana", "García")
"Good morning, Ana García"
"""
def greet_formal(first_name, last_name) do
"Good morning, #{first_name} #{last_name}"
end
end
How to use this module in IEx:
# Compile the file
c("hello_world.ex")
# Use the functions
HelloWorld.greet("Elixir")
HelloWorld.greet_formal("José", "Valim")
# See the documentation
h(HelloWorld)
h(HelloWorld.greet)
Introduction to Documentation in Elixir:
- @moduledoc: Documents what the entire module does
- @doc: Documents specific functions
- Examples with doctests: Examples in documentation also serve as tests
- h() command: In IEx, you can see documentation for any module or function
📚 Key Concepts - Preview
In upcoming articles, we’ll dive deep into:
Pattern Matching and Guards
defmodule Calculator do
def divide(a, b) when b != 0 do
{:ok, a / b}
end
def divide(_, 0) do
{:error, "Division by zero"}
end
end
Process Communication
# Actor model in action
pid = spawn(fn ->
receive do
{:add, a, b} -> IO.puts("Result: #{a + b}")
end
end)
send(pid, {:add, 5, 3})
OTP Behaviors
# GenServer example
defmodule Counter do
use GenServer
def start_link(initial_value) do
GenServer.start_link(__MODULE__, initial_value)
end
def increment(pid) do
GenServer.cast(pid, :increment)
end
def get_value(pid) do
GenServer.call(pid, :get_value)
end
end
🎯 What’s Next?
In Lesson 02: Basic Data Types (coming soon), we’ll explore:
- Atoms, numbers, strings, and binaries
- Lists, tuples, and maps
- Booleans and nil
- Immutable data structures
- Basic operations with each type
💡 Practical Exercises
🤔 Theoretical Questions
- What is Elixir and what is it built on?
- What are some key characteristics of BEAM?
- Mention three situations where Elixir is especially useful.
- What does the “let it crash” philosophy mean?
- Why is immutability important in Elixir?
💻 Practical Exercises
- Open
iexand execute an addition:3 + 4(Basic) - Show a message with your name using
IO.puts(Basic) - Use
IO.inspectto show the result of5 * 6(Basic) - Execute
System.version()to see the Elixir version (Basic) - Declare a variable
nameand assign your name (Basic) - Use
String.upcase/1to convert your name to uppercase (Intermediate) - Check if
:okis an atom usingis_atom(:ok)(Intermediate) - Calculate the result of
10 / 2and observe the data type (Intermediate) - Create a list with
[1, 2, 3]and assign it to a variable (Intermediate) - Use
String.length/1to get the length of your name (Intermediate) - Create a simple module with a function that adds two numbers (include @moduledoc and @doc) (Advanced)
- Use the
h()command to see help forString.upcase/1(Advanced) - Compile a .ex file in IEx using
c/1(Advanced) - Use
i/1to inspect the data type of different values (Advanced) - Experiment with recompile() in IEx (Advanced)
- Create a module with doctests and verify they work (Advanced)
- Use pattern matching to extract the first element of a list (Advanced)
- Experiment with the pipe operator
|>to chain operations (Advanced)
✔️ Answers
Theoretical:
- Elixir is a functional and concurrent language that runs on BEAM (Erlang virtual machine)
- Massive concurrency, isolated processes, per-process garbage collection, fault tolerance
- Chat applications, microservices, real-time systems, IoT, financial platforms
- It means allowing processes to fail and be restarted by supervisors, instead of trying to handle all possible errors
- Immutability prevents side effects, makes code more predictable, and facilitates concurrency
Practical:
3 + 4→7IO.puts("Hello, I'm Maykell")→ prints “Hello, I’m Maykell”IO.inspect(5 * 6)→ prints30System.version()→ returns something like"1.18.4"-
name = "Maykell" String.upcase(name)→"MAYKELL"is_atom(:ok)→true10 / 2→5.0(float)list = [1, 2, 3]-
String.length(name)→7 -
defmodule Calculator do @moduledoc "Module for basic mathematical operations" @doc "Adds two numbers" def add(a, b), do: a + b end h(String.upcase)→ shows the function documentationc("file.ex")→ compiles the file and loads the modulei(42)→ shows it’s Integer,i("hello")→ shows it’s Stringrecompile()→ recompiles all modified modules- Doctests are the examples in @doc that can be run as tests
[first | rest] = [1, 2, 3]→first = 1,rest = [2, 3]"hello" |> String.upcase() |> String.length()→5
🎓 Final Challenge
Create a module called Greeting that:
- Has a
greet/1function that receives a name and returns a greeting - Has a
greet_time/2function that receives a name and time, and returns an appropriate greeting - Uses pattern matching to handle different cases
# Usage example:
# Greeting.greet("Ana") → "Hello, Ana!"
# Greeting.greet_time("Ana", :morning) → "Good morning, Ana!"
# Greeting.greet_time("Ana", :evening) → "Good evening, Ana!"
💭 Final Thoughts
Elixir represents a paradigm shift from traditional object-oriented programming to a functional, concurrent, and fault-tolerant approach. The BEAM virtual machine provides a robust foundation that has been battle-tested in telecommunications systems for decades.
As someone who has worked with Java, Ruby, and other languages for over 20 years, I find Elixir’s approach to concurrency and fault tolerance particularly compelling for modern distributed systems.
🔗 Useful Resources
- Official Elixir Website
- Elixir School
- asdf installation guide
- Online playground (Livebook)
- Programming Elixir by Dave Thomas
- The Little Elixir & OTP Guidebook
This is Part 1 of the Elixir Lessons series. Follow along as we explore the fascinating world of functional programming and the BEAM ecosystem.
Next Article: Elixir Lessons: 02 - Basic Data Types
What aspects of Elixir are you most excited to learn about? Share your thoughts in the comments below!