Supervision

A child service crashes. Detect it, restart it, and stop restarting if it keeps failing. The foundation of fault-tolerant systems.

Hew

Supervisors are a language construct. Three lines per child: restart policy, budget, and strategy. permanent means always restart, transient means restart only on error. budget(5, 60s) caps restarts to 5 within 60 seconds before the supervisor gives up.

supervision.hew
supervisor ServiceCluster {
    child db: DatabasePool
        restart(permanent)
        budget(5, 60s)
        strategy(one_for_one);
    child cache: CacheLayer
        restart(permanent);
    child api: ApiHandler
        restart(transient);
}

Erlang/OTP

Erlang invented the supervision model that Hew adopts. The same concepts exist — one_for_one, intensity/period, permanent/transient — but expressed through OTP behaviour callbacks, maps, and tuples rather than language-level declarations.

service_cluster_sup.erl
-module(service_cluster_sup).
-behaviour(supervisor).
-export([start_link/0, init/1]).

start_link() ->
    supervisor:start_link({local, ?MODULE}, ?MODULE, []).

init([]) ->
    SupFlags = #{strategy => one_for_one,
                 intensity => 5,
                 period => 60},
    Children = [
        #{id => db,
          start => {database_pool, start_link, []},
          restart => permanent,
          type => worker},
        #{id => cache,
          start => {cache_layer, start_link, []},
          restart => permanent,
          type => worker},
        #{id => api,
          start => {api_handler, start_link, []},
          restart => transient,
          type => worker}
    ],
    {ok, {SupFlags, Children}}.

Go

Go has no standard supervision pattern. Every team writes their own restart loop. The example below is a simplified version — a production implementation would also need graceful shutdown, time-windowed budgets, and strategy variants.

supervision.go
type Supervisor struct {
    maxRestarts int
    restarts    int
    mu          sync.Mutex
}

func (s *Supervisor) monitor(name string, start func() error) {
    for {
        err := start()
        if err == nil {
            return // Clean exit
        }
        s.mu.Lock()
        s.restarts++
        if s.restarts > s.maxRestarts {
            s.mu.Unlock()
            log.Fatalf("supervisor: %s exceeded restart limit", name)
        }
        s.mu.Unlock()
        log.Printf("supervisor: restarting %s after error: %v", name, err)
    }
}

func main() {
    sup := &Supervisor{maxRestarts: 5}
    go sup.monitor("db", startDatabasePool)
    go sup.monitor("cache", startCacheLayer)
    go sup.monitor("api", startApiHandler)
    select {} // Block forever
}

What this shows

Developer experience

Hew brings Erlang's proven supervision model to a statically typed, compiled language with minimal ceremony. Erlang invented the concept but requires module declarations, behaviour callbacks, and configuration maps. Go has no standard supervision — teams write ad-hoc restart loops that often miss edge cases.

Debugging

Hew's supervisor declarations are visible in source code — you can see every child's restart policy at a glance. In Go, supervision logic is scattered through goroutine management code, and it's common for restart limits to be hardcoded constants buried in a different package.

Trade-offs

Erlang's OTP supervision is battle-tested in production for decades at companies like Ericsson and WhatsApp. Hew's supervision is the same model but younger. Go's approach is more explicit — you see exactly what happens on failure, with no framework magic. That explicitness is a feature when debugging unfamiliar code.