Runtime-configured feature flags, API-compatible with fun_with_flags.
The active store is resolved at runtime (Bandera.Store.active/0), so nothing
about persistence or caching is fixed at compile time.
Summary
Functions
Returns {:ok, names} with every known flag name, or {:error, reason}.
Returns {:ok, flags} with every stored Bandera.Flag, or {:error, reason}.
Removes gates from flag_name, returning :ok.
Disables flag_name, optionally scoped by an option, and returns {:ok, enabled?}.
Enables flag_name, optionally scoped by an option, and returns {:ok, enabled?}.
Returns whether flag_name is enabled.
Looks up a single flag, returning {:ok, %Bandera.Flag{}} or {:error, reason}.
Stores a reusable named constraint set (a segment) under the reserved key
:"bandera_segment:<name>".
Stores a :variant gate for flag_name with the given weights map.
Re-read application env into the runtime config snapshot.
List flags whose last evaluation is older than older_than days (or never
evaluated). Requires Bandera.Usage to be running and attached.
Returns the variant chosen for the flag named flag_name (bucketed by the actor
passed via for:), or options[:default] (nil if not given) when the flag is
missing, has no variant gate, or for: is absent or nil.
Functions
Returns {:ok, names} with every known flag name, or {:error, reason}.
Examples
iex> Bandera.enable(:checkout)
iex> Bandera.all_flag_names()
{:ok, [:checkout]}
@spec all_flags() :: {:ok, [Bandera.Flag.t()]} | {:error, term()}
Returns {:ok, flags} with every stored Bandera.Flag, or {:error, reason}.
Examples
iex> Bandera.enable(:checkout)
iex> {:ok, flags} = Bandera.all_flags()
iex> Enum.map(flags, & &1.name)
[:checkout]
Removes gates from flag_name, returning :ok.
With no options the whole flag (all its gates) is deleted. A scope removes just that gate, letting evaluation fall through to whatever remains:
boolean: true— clear the boolean gatefor_actor: actor— clear one actor gatefor_group: group— clear one group gatefor_percentage: true— clear the percentage gatevariant: true— clear the variant gaterule: true— clear the rule gatefor_segment: name— clear one segment gaterequires: parent— clear one prerequisite gateschedule: true— clear the schedule gate
Accepts by: identity to record who made the change (see Bandera.Audit).
Returns {:error, reason} if the store delete fails.
Examples
iex> Bandera.enable(:checkout)
iex> Bandera.clear(:checkout)
:ok
iex> Bandera.enabled?(:checkout)
false
Disables flag_name, optionally scoped by an option, and returns {:ok, enabled?}.
Accepts the negatable scopes for_actor:, for_group:, and for_percentage_of:
(for a percentage scope, disabling for ratio is equivalent to enabling for
1.0 - ratio). To remove a grant-only gate (variant, rule, segment,
prerequisite, schedule), use clear/2; passing one of those scopes here
returns {:error, :unsupported_scope}. Returns {:error, reason} on a store
write failure.
Accepts by: identity to record who made the change (see Bandera.Audit).
Examples
iex> Bandera.disable(:checkout)
{:ok, false}
iex> Bandera.enable(:beta)
iex> Bandera.disable(:beta)
{:ok, false}
Enables flag_name, optionally scoped by an option, and returns {:ok, enabled?}.
With no options the boolean gate is turned on. Supported scopes:
for_actor: actor— enable for one actorfor_group: group— enable for a named groupfor_percentage_of: {:time, ratio}— enable for a ratio of callsfor_percentage_of: {:actors, ratio}— enable for a ratio of actorswhen: constraints— enable when the evaluation context matches a rulefor_segment: name— enable for a reusable named segmentrequires: parent(or{parent, required_state}) — add a prerequisiteschedule: {from, until}— enable inside an ISO-8601 time window
ratio is a float in 0.0 < r < 1.0. The write goes to the persistent store and
busts/refreshes the cache; returns {:error, reason} if the store write fails.
The returned enabled? is the immediate state for unconditional/percentage gates.
For the conditional scopes (when:, for_segment:, requires:, schedule:)
it is true to signal a successful write — those gates are evaluated per call by
enabled?/2 against the relevant context, actor, time, or parent flag.
Pass by: identity to record who made the change; it is carried in the write
telemetry metadata (see Bandera.Audit) and does not affect the gate written.
Examples
iex> Bandera.enable(:checkout)
{:ok, true}
iex> Bandera.enable(:beta, for_actor: "user-1")
{:ok, true}
iex> Bandera.enable(:gradual, for_percentage_of: {:actors, 0.25})
{:ok, true}
Returns whether flag_name is enabled.
Pass for: actor to evaluate actor, group, and percentage-of-actors gates against
a specific subject (the actor is identified via the Bandera.Actor/Bandera.Group
protocols). The flag is read through the active store (cache included). A missing
flag, or a store lookup error, resolves to false (the error is logged).
Pass default: true to fail open (return true) when the store is unreachable; the
default is false.
Examples
iex> Bandera.enabled?(:unknown_flag)
false
iex> Bandera.enable(:checkout)
iex> Bandera.enabled?(:checkout)
true
iex> Bandera.enable(:beta, for_actor: "user-1")
iex> Bandera.enabled?(:beta, for: "user-1")
true
iex> Bandera.enabled?(:beta, for: "user-2")
false
@spec get_flag(atom()) :: {:ok, Bandera.Flag.t()} | {:error, term()}
Looks up a single flag, returning {:ok, %Bandera.Flag{}} or {:error, reason}.
An unknown flag still returns {:ok, flag} with an empty gate list (a disabled
flag), not an error.
Examples
iex> Bandera.enable(:checkout)
iex> {:ok, flag} = Bandera.get_flag(:checkout)
iex> flag.gates
[%Bandera.Gate{type: :boolean, for: nil, enabled: true}]
iex> {:ok, flag} = Bandera.get_flag(:unknown_flag)
iex> flag.gates
[]
@spec put_segment(atom(), [tuple() | Bandera.Constraint.t()]) :: {:ok, Bandera.Flag.t()} | {:error, term()}
Stores a reusable named constraint set (a segment) under the reserved key
:"bandera_segment:<name>".
Segments are referenced from flags via enable(flag, for_segment: name) and are
expanded at evaluation time so that Flag stays pure. name must be a
developer-defined atom — never untrusted user input.
Examples
iex> {:ok, _} = Bandera.put_segment(:premium, [{"plan", :eq, "premium"}])
iex> {:ok, _flag} = Bandera.get_flag(:"bandera_segment:premium")
@spec put_variants(atom(), %{optional(String.t()) => number()}, keyword()) :: {:ok, Bandera.Flag.t()} | {:error, term()}
Stores a :variant gate for flag_name with the given weights map.
weights is a %{variant_name => weight} map; actors are bucketed proportionally
by weight using a stable SHA-256 hash per actor+flag. Returns {:ok, flag} on
success, {:error, reason} on a store write failure.
The optional third argument is accepted for API uniformity but is ignored.
put_variants does not support by: and is not audited by Bandera.Audit.
Examples
iex> {:ok, flag} = Bandera.put_variants(:hero, %{"blue" => 1, "green" => 1})
iex> flag.name
:hero
@spec reload_config() :: :ok
Re-read application env into the runtime config snapshot.
List flags whose last evaluation is older than older_than days (or never
evaluated). Requires Bandera.Usage to be running and attached.
Returns the variant chosen for the flag named flag_name (bucketed by the actor
passed via for:), or options[:default] (nil if not given) when the flag is
missing, has no variant gate, or for: is absent or nil.
Looks up the flag from the active store and delegates to Flag.variant/2. A missing
flag or store lookup error returns options[:default] (the error is logged).
Examples
iex> Bandera.put_variants(:ab_test, %{"a" => 1, "b" => 1})
iex> Bandera.variant(:ab_test, for: %{id: 1}) in ["a", "b"]
true