Erlang was built for telephone switches, systems that were expected to run for years without stopping. Its answer to failure was unusual: do not try to prevent every crash. Let a process fail, notice the failure, and start a fresh process in its place. The system as a whole keeps running because no single failure takes everything down.
The unit of work in Erlang is a process, and it is far lighter than an operating system process. A program can run thousands or millions of them. Processes do not share memory. They communicate by sending messages to each other, and each process handles its own mailbox in turn. That isolation is the point. A bug in one process cannot silently corrupt another process's state, because there is no shared state to corrupt.
Here is a small example. It spawns a new process that prints a greeting, while the calling function returns ok straight away.
-module(greet). -export([hello/0]). hello() -> spawn(fun() -> io:format("Hello from a process~n") end), ok. The module and export lines declare a public function called hello, which takes no arguments. Inside, spawn starts a new process running the given function, and io:format prints text, with ~n as a newline. The original function does not wait for the new process. It returns ok immediately, and the greeting appears from the spawned process in its own time. That pattern, start work and carry on, is everywhere in Erlang code.
Supervisors take the idea further. A supervisor is a process whose job is to watch other processes and restart them according to a strategy when they crash. You write the normal case plainly, and you write the restart strategy separately, instead of scattering defensive checks through every function. The result is code that is easier to read and a system that recovers on its own.
Erlang is also the foundation of Elixir, which runs on the same virtual machine and keeps the same process model with a different syntax. If you know one, the other's concurrency ideas will feel familiar.
The honest trade
Erlang's strength is fault tolerance, not raw speed for a single computation. Message passing and process isolation add overhead, and the language itself is not the first choice for number crunching or for a user interface. Its syntax can also look unfamiliar at first, with pattern matching and recursion doing work that loops do in other languages. In return, you get a system that is designed to stay up, scale across cores and machines, and recover from failures without a restart of the whole service.
A system that expects trouble
The most useful idea to take from Erlang is not a syntax trick. It is the attitude: assume parts will fail, isolate them, and plan the restart. Even in another language, designing with that attitude, with small independent pieces and a clear recovery path, makes a system harder to break.
Originally published at https://practicaldevblog.blogspot.com/2026/11/erlang-explained-processes-that-fail.html
![Why Object.keys(user) Returns string[], Not (keyof User)[]: TypeScript Means It](https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2q4w48b9bzs43tynyhsf.png)
