features¶
Feature gating and feature flags for Go tools, as values rather than process state.
features is what a binary offers (declared at init into a Registry), what a tool has
chosen (a Set, resolved once from an immutable Snapshot), and how a request asks (an
Evaluator, static by default and dynamic over a Backend when a feature opts in).
// Declared at init by the package that owns the feature.
features.MustDeclare(features.Default(), reportsFeature{})
// Resolved once, when the tool builds itself.
set, err := features.Resolve(features.Default().Snapshot(), features.Apply(nil, features.Enable("reports")))
// Read everywhere something static is decided.
if set.Enabled("reports") { /* ... */ }
It is a library, not a command: no binary, no configuration file, no environment variable,
no vendor SDK. A flag backend is an adapter module implementing Backend.
Start here¶
- Getting started: ten minutes from an empty
mainto a tool that gates a command on a feature and evaluates a dynamic flag with a fallback.
Three questions, three roles¶
- Declare features and resolve a set:
Registry,Snapshot,Resolver,Set. Declaration is process-wide because a blank import can only reach package state; everything aftermainis a value. - Evaluate dynamic flags through a backend:
Evaluator,Backend,Dynamic. The static state is the fallback for every failure, and a feature that has not opted in never reaches a backend. - Write a backend adapter: what
Resolve,Init,Ready,WatchandClosemust do, and why the fallback is an input.
Why it is shaped this way¶
- Snapshots, not seals: how a process-wide registry can be read safely by several roots without a seal that panics.
- Static and dynamic: the rule that keeps
--helpdeterministic offline, and where a dynamic answer is allowed.