Elixir: IEx, Formatting & Analysis

Elixir's tooling compensates for dynamic typing with runtime introspection: the shell can open docs, inspect any process, and attach to production nodes; Dialyzer finds type errors statically. This lesson turns those tools into a daily workflow.

IEx Helpers That Matter

iex> h Enum.reduce          # full documentation for any function
iex> t String.t             # typespecs used by the function
iex> i %Date{}              # EVERYTHING about a value: type, impls, size
iex> open String            # jump to the source (editors hook in here)
iex> recompile()            # reload code after edits — keep iex open while coding
iex> v 3                    # the result of line 3

# Breakpoints and stepping — no debugger UI required:
iex> break! Stats.summarize/3
iex> continue               # run; stop at the breakpoint
iex> whereami               # show source around the stop

IEx.pry/0 inside code pauses that process at the call site when you accept the pry prompt — for those bugs where a breakpoint is worth a hundred prints.

Static Analysis

Dialyzer

Dialyzer analyzes success typings — what a function can return across all calls — and finds impossible matches, dead code, and type errors without running anything. dialyxir wraps it for mix projects. It is sound-ish but gradual: it only reports what it can prove wrong, so a clean run is meaningful but not complete.

mix deps.get && mix dialyzer        # first run builds the PLT cache

Credo

Credo is a linting and consistency tool with severity levels — refactor suggestions, warnings, and design smells. Teams pick a subset in .credo.exs and treat mix credo --strict as a merge gate.

Observing the Runtime

mix format first: it is non-negotiable in teams, and .formatter.exs can also import_deps: so embedded DSLs (Ecto queries, Surface) format correctly.

mix format                   # format everything
mix format --check-formatted # CI gate

Runtime inspection commands

# From inside the project (or attach to a release with bin/app remote):
:observer.start()             # GUI: processes, apps, memory, charts

# The same data programmatically:
:erlang.system_info(:process_count)          # how many processes
:erlang.memory(:processes)                   # total process memory
Enum.count(Process.list())                   # same count, Elixir side

:observer is the fastest way to see an Elixir system: process trees, message queues, and memory per process update live. Use it while studying the OTP chapters — supervision trees become concrete shapes rather than diagrams.

Compile-Time Checks

mix xref graph --sink lib/explorer.ex   # what forces this module to recompile
mix deps.tree                            # dependency versions and conflicts
mix app.tree                             # the OTP application boot order

Long compile cycles in Elixir usually come from runtime dependencies between modules that the compiler cannot see are unnecessary. mix xref names the culprits, which turns build-time tuning from folklore into data.

Practice

  1. Set a breakpoint with break!, step through a function, and print a binding with b/0.
  2. Run mix credo --strict on a project and fix the three most severe findings.
  3. Open :observer, spawn 10,000 sleeping processes, and watch the process count and memory graphs.

Next: The Process Model