# ADR-002: Reverse-Order Shutdown **Status:** Accepted **Date:** 2026-03-18 ## Context When stopping an application, components must be stopped in the reverse of the order they were started. A component that depends on another (e.g. an HTTP server that uses a database connection pool) must be stopped before the component it depends on. If the database pool were closed first, any in-flight request handled by the HTTP server would fail with a connection error rather than completing gracefully. ## Decision `stopAll` iterates `l.components` from the last index to the first: ```go for i := len(l.components) - 1; i >= 0; i-- { // stop l.components[i] } ``` Components are typically registered in dependency order (dependencies first, then dependents). Reverse-order shutdown therefore stops dependents before dependencies. Each component's `OnStop` runs in its own goroutine. A `time.After` ticker enforces the per-component `ComponentStopTimeout` (default 15 seconds). If a component does not return within the timeout, the launcher logs an error and moves on to the next component — it does not block the remainder of the shutdown sequence. `Shutdown(ctx context.Context) error` provides a caller-side wait: it closes `shutdownCh` (idempotent via `sync.Once`) and then blocks until `doneCh` is closed (when `Run` returns) or until `ctx` is done. The caller's context controls how long the caller is willing to wait for the entire shutdown; `ComponentStopTimeout` controls how long each individual component gets. ## Consequences - Shutdown order mirrors startup order in reverse, which is correct for any DAG of component dependencies without requiring an explicit dependency graph. - Each component's timeout is independent — a single slow component does not block others from stopping. - `Shutdown` is idempotent: calling it multiple times from multiple goroutines (e.g. an OS signal handler and a health-check endpoint) is safe. - If `ComponentStopTimeout` is exceeded, the component is abandoned — resources may not be fully released. This is the correct trade-off for a graceful shutdown with a deadline; the alternative (waiting indefinitely) is worse.