Using Erlang Supervision Trees to Embrace the Let‑It‑Crash Philosophy
Learn how Erlang’s Let it Crash philosophy uses supervision trees to isolate faults, restart workers safely, and keep systems self‑healing without complex defensive code.
19 Aug 2026, 04:48 UTC

The Cost of Defensive Code
In many languages developers wrap risky calls in try…catch or extensive guard clauses, hoping to prevent any crash. This often leaves a process in an inconsistent zombie state where it is still alive but its internal data is corrupted, making bugs hard to reproduce.
Erlang’s Let it Crash philosophy flips this instinct: instead of trying to handle every error inside the worker, you let the worker fail and delegate recovery to a dedicated supervisor. The thesis is simple: isolate failure to the smallest process unit and restart it to a known good state.
How Supervision Trees Work
A supervision tree is a hierarchy of supervisor processes that monitor worker processes. When a worker terminates abnormally, the supervisor is notified via a link (bidirectional) or a monitor (unidirectional). The supervisor then applies a restart strategy to return the affected workers to a clean state.
Choosing a Restart Strategy
- one_for_one: restarts only the crashed worker; appropriate when workers are independent.
- one_for_all: if any worker in the group crashes, the supervisor terminates and restarts all workers to preserve a consistent group state.
- rest_for_one: restarts the crashed worker and any workers that were started after it, preserving start‑order dependencies.
Worked Example: Supervising a Parser Process
Suppose a parser worker crashes when it receives malformed input. We want the supervisor to also restart a dependent formatter worker that runs after the parser, using rest_for_one.
- Create two modules (parser.erl and formatter.erl) in the current directory.
- Compile them: erlc parser.erl formatter.erl (run in a shell with read/write access to the directory; no special privileges needed).
- Start an Erlang shell: erl.
- Define the supervisor specification:
Spec = #{
strategy => rest_for_one,
max_restarts => 5,
max_seconds => 10,
children => [
{parser, {parser, start_link, []}, permanent, 5000, worker, [parser]},
{formatter, {formatter, start_link, []}, transient, 5000, worker, [formatter]}
]
}.
Note: max_restarts and max_seconds limit the restart intensity to avoid infinite loops.
- Start the supervisor:
{ok, SupPid} = supervisor:start_link({local, parser_sup}, supervisor, Spec).
- Send malformed data to the parser to provoke a crash:
parser:parse(bad_input).
The parser process exits; the supervisor receives the termination signal and, because the strategy is rest_for_one, it also terminates and restarts the formatter (which was started after the parser).
- Verify the restart:
supervisor:which_children(parser_sup).
The output shows new PIDs for both parser and formatter (the PIDs differ from those before the crash). If you observe only the parser’s PID changed, double‑check that the formatter was started after the parser in the child list.
Risk: If the parser crashes immediately on start‑up and max_restarts is too high, the supervisor could exhaust CPU cycles. Always set a sensible intensity limit.
Trade-offs and Limitations
- State loss: A crash discards the process’s heap; any in‑memory state must be rebuilt or persisted elsewhere (e.g., in a gen_server state passed on restart or an external database).
- Restart loops: Misconfigured intensity values can lead to a supervisor that never settles. The max_restarts/max_seconds tuple is the guard.
- Observability: Because the preferred recovery is a restart, logging the reason for the crash becomes essential; otherwise you lose visibility into the root cause.
Actionable Closing
Identify the most failure‑prone parts of your system (e.g., protocol handlers, external‑call wrappers). Extract each into its own process, link it to a supervisor, and pick a restart strategy that matches the dependency order. Start with one_for_one for independent workers and move to rest_for_one or one_for_all only when you need coordinated restarts. This keeps your code focused on the happy path while the supervision tree guarantees a return to a known safe state.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.