Part 4 · 1 chapters · ~8 min
Idioms
with for happy paths, multi-clause functions over conditionals, assertive code, Phoenix contexts as domain boundaries, when to use processes (and when not), avoiding single-process bottlenecks, naming and module structure, @moduledoc and doctests, Credo, and common code-review comments.
5
Happy paths and boundaries
code
def create_transfer(params, user) do
with {:ok, attrs} <- validate(params),
{:ok, account} <- Accounts.fetch_for(user, attrs.from),
:ok <- Limits.check(account, attrs.amount_kobo),
{:ok, transfer} <- Ledger.hold_and_create(account, attrs) do
{:ok, transfer}
else
{:error, :not_found} -> {:error, :not_found}
{:error, :limit_exceeded} = e -> e
{:error, %Ecto.Changeset{} = cs} -> {:error, cs}
end
end
# anti-pattern: a single GenServer every request calls becomes a bottleneck (it handles one message at a time)
# fix: ETS for shared reads, a pool or a process per key (Registry) for writesIDIOMATIC ELIXIR
what reviewers look for
swipe the figure sideways, or tap expand for full screen
1/4
with
with chains steps that return {:ok, _}; the first failure falls to else, keeping the happy path flat.
flat happy pathsone error branch