flag

package
v0.22.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 3, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package flag is feature flags for smolanalytics — boolean and multivariate, with property targeting and percentage rollouts, evaluated deterministically so the same user always lands in the same bucket. What makes it deeper than a plain flag console (a later increment): a flag flip is auto-recorded as a deploy marker, so the existing deploy-impact engine answers "did flag X move activation?" from your editor, provably. This file is the pure engine — types + evaluation — with no I/O, so it's trivially testable and shared verbatim with any SDK that copies the bucketing.

Index

Constants

View Source
const (
	ExposureEvent = "$feature_flag_called"
	PropFlag      = "$feature_flag"
	PropVariant   = "$feature_flag_response"
)

Exposure/response property keys, mirroring PostHog's $feature_flag_called convention. The "$" prefix means the tracking-plan drift gate treats these as system events, not unplanned ones.

Variables

This section is empty.

Functions

This section is empty.

Types

type Flag

type Flag struct {
	Key         string    `json:"key"`
	Description string    `json:"description,omitempty"`
	Enabled     bool      `json:"enabled"`
	Variants    []Variant `json:"variants,omitempty"`
	Rules       []Rule    `json:"rules,omitempty"`
	Measured    bool      `json:"measured,omitempty"`
	Created     time.Time `json:"created"`
	Updated     time.Time `json:"updated"`
}

Flag is a saved feature flag. Variants empty = a boolean flag (served variant is "on"). Rules empty = on for everyone when Enabled. Measured opts this flag into exposure logging (a later increment) so it can be A/B-analysed without every flag inflating the event count.

func (Flag) Evaluate

func (f Flag) Evaluate(distinctID string, context map[string]any) (string, bool)

Evaluate resolves the flag for one user, given their context properties. Returns the served variant ("on" for a boolean flag, "" when off) and whether the flag is on. Deterministic: the same key + distinct_id always yields the same result, computed only from a stable hash — no randomness, no state — so a client SDK that copies this bucketing agrees byte-for-byte.

type Report added in v0.9.7

type Report struct {
	Flag     string          `json:"flag"`
	Goal     string          `json:"goal"`
	Days     int             `json:"days"`
	Control  string          `json:"control"`
	Variants []VariantResult `json:"variants"`
	Note     string          `json:"note"`
}

Report is the A/B read for one measured flag: for each variant, how many exposed users converted on the goal event AFTER their first exposure, and whether the lift over the control arm is statistically significant. Pure + deterministic (same events → same report), so it is pinnable MCP==API by an agreement test, the same contract as every other report.

func Measure added in v0.9.7

func Measure(evs []event.Event, flagKey, goal string, days int) Report

Measure computes the report from raw events. An exposure is a $feature_flag_called event tagging the user's variant for this flag; a conversion is the user doing `goal` at or after their first exposure (so we never credit behavior that predates the experiment). Only events within the last `days` (0 = all) are considered.

type Rule

type Rule struct {
	Filters    []query.Filter `json:"filters,omitempty"`
	RolloutPct int            `json:"rollout_pct"`
}

Rule is one ordered targeting clause: the user's context must pass all Filters (empty = every user), and RolloutPct (0..100) is the deterministic share of matched users served. Rules are evaluated in order, first match wins.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store persists flags to a JSON file (atomic tmp+rename), same discipline as the cohort and deploy stores. Flags are keyed by their stable Key (e.g. "checkout_v2"), so Save is an upsert: creating or updating the flag with that key. Empty path = in-memory only.

func Open

func Open(path string) (*Store, error)

func (*Store) Delete

func (s *Store) Delete(key string) (found bool, err error)

Delete removes a flag by key. found is true only when a flag actually went away, so callers can report "nothing was deleted" instead of implying a removal that never happened. Deleting a key that isn't there is not an error (retries are fine).

func (*Store) Get

func (s *Store) Get(key string) (Flag, bool)

func (*Store) List

func (s *Store) List() []Flag

func (*Store) Save

func (s *Store) Save(f Flag) (Flag, error)

Save upserts by Key. A new key is created (Created stamped); an existing key is updated in place (Created preserved, Updated bumped). Validates the key and the variant weights.

func (*Store) SetEnabled

func (s *Store) SetEnabled(key string, on bool) (Flag, error)

SetEnabled toggles a flag on/off by key (the common flip). Returns the updated flag. A future increment records this flip as a deploy marker so its impact is measured automatically.

type Variant

type Variant struct {
	Key    string `json:"key"`
	Weight int    `json:"weight"`
}

Variant is one arm of a multivariate flag; Weight is its relative share (need not sum to 100).

type VariantResult added in v0.9.7

type VariantResult struct {
	Key         string  `json:"key"`
	Exposed     int     `json:"exposed"`
	Converted   int     `json:"converted"`
	RatePct     float64 `json:"rate_pct"`
	DeltaPct    float64 `json:"delta_pct"`    // conversion-rate lift vs the control arm (0 for control)
	Significant bool    `json:"significant"`  // 95% two-proportion z-test vs control
	SmallSample bool    `json:"small_sample"` // too few exposed to trust the rate
}

VariantResult is one arm of a measured flag's A/B read.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL