Try to think about long flow as recipe of multiple steps and build a pipeline.
🍳 Recipe 5: Pipelines with Clean Function Chains
defmodule User do
def process(data) do
data
|> normalize()
|> validate()
|> store()
end
defp normalize(data), do: String.trim(data)
defp validate(data), do: if String.length(data) > 0, do: data, else: raise "Invalid"
defp store(data), do: IO.puts("Saved: #{data}")
end
benefit 1: each stage in a pipeline can be inspected easily
data
|> normalize()
|> tap(&IO.inspect(&1, label: "After normalize"))
|> validate()
|> tap(&IO.inspect(&1, label: "After validate"))
|> store()
benefit 2: each function becomes a clean, composable step that:
1. Takes input
2. Returns output
This encourages writing pure functions and makes testing of each one easier.
benefit 3: functional thinking
Pipelines match Elixir’s functional model: 1. No mutation 2. No side effects (except at the end, like IO.puts) 3. Emphasis on transformation of data
benefi 4: scales better with more steps
imagine this:
data
|> step1()
|> step2()
|> step3()
|> step4()
|> step5()
vs
step5(step4(step3(step2(step1(data)))))
during reviewing code, pipelines help developers think in clean, linear transformations - and that’s a big win in Elixir
🔥 Real-world example