We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
`dbg/2` shows every step of your pipeline
almirsarajcic
Debugging a pipeline usually means breaking it apart with IO.inspect/2 calls between every stage, labelling each one so you can tell them apart, then deleting them all afterwards. Elixir has shipped a macro since 1.14 that does the whole thing with one call at the end of the pipe.
%{"tags" => "elixir, phoenix ,beam"}
|> Map.fetch!("tags")
|> String.split(",")
|> Enum.map(&String.trim/1)
|> Enum.reject(&(&1 == ""))
|> dbg()
That prints every stage with its intermediate value:
[tags.exs:6: (file)]
%{"tags" => "elixir, phoenix ,beam"} #=> %{"tags" => "elixir, phoenix ,beam"}
|> Map.fetch!("tags") #=> "elixir, phoenix ,beam"
|> String.split(",") #=> ["elixir", " phoenix ", "beam"]
|> Enum.map(&String.trim/1) #=> ["elixir", "phoenix", "beam"]
|> Enum.reject(&(&1 == "")) #=> ["elixir", "phoenix", "beam"]
dbg/2 is a macro, not a function, so it can see the syntax of what it was piped into. It rewrites the pipeline to capture each step, prints the source of every stage next to its result, and returns the final value unchanged — dropping it into an existing pipe changes nothing about what the code does.
Why the output beats IO.inspect/2
The labels come from the source, so they cannot drift out of sync the way a hand-written label: does. String.split(",") is shown as String.split(","), not as "after split" that you wrote three refactors ago and which no longer describes the step. And because it is one call at the end rather than N calls interleaved, removing it is a single-line deletion — no risk of shipping a stray inspect that survived the cleanup.
Bare dbg() dumps your bindings
Called with no arguments, it inspects binding() for you:
base = 10
rate = base * 1.5
dbg()
# binding() #=> [base: 10, rate: 15.0]
Truncating noisy values
The second argument takes Inspect options, so the usual controls still apply:
1..100 |> Enum.map(&(&1 * 2)) |> dbg(limit: 5)
# 1..100 #=> 1..100
# |> Enum.map(&(&1 * 2)) #=> [2, 4, 6, 8, 10, ...]
One thing to know about scope: the pipeline breakdown is what you get in scripts, tests, and mix run. Inside iex, dbg/2 is backed by IEx.Pry instead and can stop at the call to let you step through the pipeline interactively. Same call, different capability depending on where it runs.
copied to clipboard