case, cond, and if
Elixir's three branching constructs, and how to pick the right one based on what you're actually deciding between.
2 min read
Elixir has three main ways to branch: case, cond, and if. They overlap in what they can do, but each one reads best for a different kind of decision.
case -- branching on shape
case matches a value against a series of patterns, using the same pattern-matching rules from the previous lesson:
response = {:ok, "data loaded"}
case response do
{:ok, data} -> IO.puts("Success: #{data}")
{:error, reason} -> IO.puts("Failed: #{reason}")
_ -> IO.puts("Unknown response")
endEach branch is a pattern, and case runs the first one that matches, binding any variables in it just like a standalone match would. This is the most idiomatic choice whenever you're branching on the shape of a value — especially the ubiquitous {:ok, _} / {:error, _} tuples returned throughout the standard library.
cond -- branching on conditions
cond is closer to a chain of else if statements: it evaluates a list of boolean expressions in order and runs the first one that's truthy.
age = 20
cond do
age < 13 -> IO.puts("Child")
age < 20 -> IO.puts("Teenager")
age < 65 -> IO.puts("Adult")
true -> IO.puts("Senior")
endThe final true -> branch acts as a catch-all default — without one, a cond where nothing matches raises an error. Reach for cond when you're checking a series of independent conditions rather than matching a value's structure.
if and unless -- the simple case
For a single true/false condition, if is the most readable option:
temperature = 30
if temperature > 25 do
IO.puts("It's warm")
else
IO.puts("It's cool")
endunless is the inverse, for when the "not" case reads more naturally:
unless File.exists?("config.json") do
IO.puts("Config file is missing!")
endUnlike some languages, if/unless in Elixir are just expressions — they evaluate to a value:
label = if temperature > 25, do: "warm", else: "cool"That's the same if written on one line using do:/else: keyword syntax, which is common for short conditionals.
Every branch is an expression
All three constructs return the value of whichever branch ran, which means you can bind the result directly:
message =
case response do
{:ok, _data} -> "All good"
{:error, _reason} -> "Something broke"
end
IO.puts(message)There's no separate "statement" form to worry about — everything in Elixir evaluates to something, including branches.
Picking the right tool
A practical rule: reach for case when you're inspecting the structure of a value (especially {:ok, _}/{:error, _} tuples), cond when you have multiple independent boolean conditions to check in order, and if/unless for a single yes/no decision. Overusing cond where a case would pattern-match more precisely is a common early mistake — if you find yourself writing cond do value == :a -> ... end, a case is almost always the clearer choice.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.