The Supervisor That Does Almost Nothing
Three marimo in a jar. No filter, no heater, no CO₂ injection. Water change every two weeks — or three, they don’t complain. Direct sunlight turns them brown; ambient window light keeps them green. That’s the entire care sheet.
I keep staring at them, waiting to feel useful.
Erlang’s designers at Ericsson faced a similar problem in the late 1980s: telephone switches that needed 99.999% uptime. Five nines means five minutes of downtime per year. You can’t achieve that by preventing all failures — you achieve it by making failure recovery automatic and fast. The philosophy crystallized as “let it crash.”
A supervisor process watches worker processes. If a worker dies, the supervisor restarts it according to a strategy: one-for-one (restart just the failed process), one-for-all (restart everything if anything fails), or rest-for-one (restart the failed process and everything started after it). The supervisor itself does almost nothing. It doesn’t run application logic. It doesn’t make decisions. It just watches, and when something stops, it starts it again.
-module(marimo_sup).
-behaviour(supervisor).
-export([start_link/0, init/1]).
start_link() -> supervisor:start_link(?MODULE, []).
init([]) ->
{ok, {{one_for_one, 3, 60}, [
#{id => marimo_1, start => {marimo, start_link, []}},
#{id => marimo_2, start => {marimo, start_link, []}},
#{id => marimo_3, start => {marimo, start_link, []}}
]}}.
The tuple {3, 60} means: if more than three restarts happen within sixty seconds, give up — something systemic is wrong. Otherwise, restart quietly and carry on.
class Supervisor {
constructor(maxRestarts = 3, windowSec = 60) {
this.maxRestarts = maxRestarts;
this.window = windowSec * 1000;
this.restarts = [];
}
async supervise(startFn) {
while (true) {
const now = Date.now();
this.restarts = this.restarts.filter(t => now - t < this.window);
if (this.restarts.length >= this.maxRestarts)
throw new Error('Too many restarts');
this.restarts.push(now);
try { await startFn(); }
catch (e) { console.log('Restarting after:', e.message); }
}
}
}
Most of the code is bookkeeping. The actual supervision is one line: catch exception, loop back, try again.
The marimo rotated slightly overnight — the bottom got darker, so it rolled to expose that side to the light. Self-correcting without intervention. I topped up the jar with dechlorinated water and went back to work.
Joe Armstrong, one of Erlang’s creators, used to say that error handling was just normal code that happened to run when things went wrong. The supervisor doesn’t distinguish between a crash and a shutdown. It doesn’t need to understand why something stopped. It just needs to know how to start it again.
My aquascaping tank requires daily ammonia tests and weekly parameter adjustments. The espresso machine needs pressure tuning and gasket maintenance. The marimo need a jar, some water, and benign neglect. Not every living system rewards attention. Some reward its absence.