Skip to content

Compared to Alternatives

Dials occupies a specific spot: operator-owned runtime values with per-scope dimensions. Each neighbor below is excellent at its own spot, and the right answer is often "both, for different values".

ultra_settings

ultra_settings resolves configuration through layers — environment variables → runtime settings → YAML — with typed fields declared in code and a read-only web UI.

  • Its problem: deploy/environment configuration — values that differ by environment and change with deploys, unified behind one API.
  • What it lacks for the dials problem: dimensions (its only axis is the Rails environment), write attribution, enforced validation, and a first-class override/clear model.
  • What dials lacks for its problem: ENV and YAML layers, deliberately — a dial changing per-environment via ENV would undermine "the change log is the history".

Use ultra_settings for DATABASE_POOL_SIZE; use dials for checkout_fee_bps.

super_settings

super_settings is the closest relative: database-backed runtime settings, editable UI, change history, and a polling cache (its design validated ours). Differences that matter:

  • Registry: super_settings' settings are created at runtime in the UI; dials must be declared in code — key, type, constraints, dimensions — so review owns the shape and the value can't outlive its reader.
  • Dimensions: super_settings has one value per key; per-market values land you back in key-naming conventions (fee_bps_ke, fee_bps_ng) with no fallback semantics. Per-scope overrides are dials' reason to exist.
  • Defaults: super_settings defaults live at the call site (Setting.fetch(key, default)), dials' at the declaration — one place, reviewed, validated at boot.

Flipper (feature flags)

Flipper answers "who gets this behavior?" — booleans with actors, groups, and percentage rollouts, built for gradual release and experimentation. Dials answers "what is the value here?" — typed quantities with per-scope fallback. The overlap is the plain boolean kill switch, which either tool handles; if you're already running Flipper, keep your flags there and bring dials in when the values stop being booleans. Percentage-of-actors on a dial is a non-goal.

ENV vars / Rails credentials

Deploy-shaped and secret-shaped configuration respectively. Neither is operator-adjustable at runtime, neither has attribution or history, and that's correct for what they hold. The pattern boundary page draws this line in detail.

A hand-rolled settings table

The default alternative, because it's an afternoon's work. But the afternoon covers only reading and writing a value; the rest gets built later, piecemeal, usually during an incident: type casting, value constraints, false vs NULL, per-market rows and their sentinel, fallback reads, cache invalidation across processes, attribution, and "who changed this at 3am". Dials is that list, built once and tested.

Constants you can turn without a deploy.