flags

package module
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Jun 25, 2026 License: MIT Imports: 9 Imported by: 0

README

togo

togo-framework/flags

marketplace pkg.go.dev MIT

Feature flags + A/B testing for togo — rollouts, targeting, experiments.

Install

togo install togo-framework/flags

flags is the togo answer to Laravel Pennant / Flipper / django-waffle. Define flags with a master on/off, a percentage rollout, and targeting rules, then evaluate them per subject with deterministic bucketing — a subject stays in the same bucket across calls, so partial rollouts are stable. Flags with variants drive A/B experiments.

Usage

import "github.com/togo-framework/flags"

s, _ := flags.FromKernel(k)

// A 25% rollout of a new checkout.
s.Set(flags.Flag{Key: "new-checkout", Enabled: true, Rollout: 25})

// Targeted to pro/enterprise plans only.
s.Set(flags.Flag{
    Key:     "beta-api",
    Enabled: true, Rollout: 100,
    Rules:   []flags.Rule{{Attribute: "plan", Values: []string{"pro", "enterprise"}}},
})

// An A/B experiment.
s.Set(flags.Flag{Key: "cta-copy", Enabled: true, Rollout: 100,
    Variants: []string{"control", "test"}})

// Evaluate (deterministic per subject):
subj := flags.Subject{ID: user.ID, Attributes: map[string]string{"plan": user.Plan}}
if s.Enabled(ctx, "new-checkout", subj) {
    // ...new flow
}
variant := s.Variant(ctx, "cta-copy", subj) // "control" or "test"

Gate a route behind a flag

k.Router.With(s.Middleware("new-checkout", func(r *http.Request) flags.Subject {
    return flags.Subject{ID: userID(r)}
})).Get("/checkout/v2", handler) // 404 when the flag is off

REST API

Mounted automatically under /api/flags:

Method Path Purpose
GET /api/flags list flags
POST /api/flags create/replace a flag (JSON body)
GET /api/flags/{key} get a flag
DELETE /api/flags/{key} delete a flag
POST /api/flags/{key}/evaluate evaluate for a subject → {enabled, variant}
GET /api/flags/{key}/results evaluation counts (A/B results)

How evaluation works

  1. Flag missing or enabled=falseoff.
  2. Targeting rules must all match the subject's attributes.
  3. rollout >= 100 → on; otherwise the subject's deterministic bucket (0–99, FNV hash of key:subjectID) must be < rollout.
  4. Variant assigns one of variants deterministically per subject for A/B tests.

Every evaluation is counted (Results(key)) so you can read experiment outcomes.

Configuration

No required env. Flags live in the kernel service (in-memory, fast); manage them via the Go API or the REST endpoints. The flags service registers at PriorityService and mounts its routes on boot.


Premium sponsors

ID8 Media  ·  One Studio

Support togo — become a sponsor.

Documentation

Overview

Package flags is a feature-flag + A/B-testing plugin for togo (Laravel Pennant / Flipper / django-waffle for Go).

Define flags with a master on/off, a percentage rollout, and targeting rules; evaluate them per subject with deterministic bucketing (a subject stays in the same bucket across calls). Flags with variants drive A/B experiments.

s, _ := flags.FromKernel(k)
s.Set(flags.Flag{Key: "new-checkout", Enabled: true, Rollout: 25})
if s.Enabled(ctx, "new-checkout", flags.Subject{ID: user.ID}) { ... }

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Flag

type Flag struct {
	Key         string   `json:"key"`
	Enabled     bool     `json:"enabled"`            // master on/off
	Rollout     int      `json:"rollout"`            // 0-100 percentage when enabled
	Rules       []Rule   `json:"rules,omitempty"`    // targeting (all must match)
	Variants    []string `json:"variants,omitempty"` // A/B variants (deterministic)
	Description string   `json:"description,omitempty"`
}

Flag is a feature-flag definition.

type Rule

type Rule struct {
	Attribute string   `json:"attribute"`
	Values    []string `json:"values"`
}

Rule is a targeting rule: the subject's Attribute must equal one of Values. A flag matches only when ALL its rules match.

type Service

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

Service is the flags runtime stored on the kernel (k.Get("flags")).

func FromKernel

func FromKernel(k *togo.Kernel) (*Service, bool)

FromKernel returns the flags Service registered on the kernel.

func (*Service) All

func (s *Service) All() []Flag

All returns every flag, sorted by key.

func (*Service) Delete

func (s *Service) Delete(key string)

Delete removes a flag.

func (*Service) Enabled

func (s *Service) Enabled(ctx context.Context, key string, subj Subject) bool

Enabled evaluates a flag for a subject. Deterministic: the same (key,subject) always yields the same bucket, so a partial rollout is stable per subject.

func (*Service) Get

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

Get returns a flag definition.

func (*Service) Middleware

func (s *Service) Middleware(key string, subjectFn func(*http.Request) Subject) func(http.Handler) http.Handler

Middleware gates a handler behind a flag. subjectFn extracts the Subject from the request (e.g. the authenticated user); when the flag is off it responds 404. Use to dark-launch routes.

func (*Service) Results

func (s *Service) Results(key string) map[string]int64

Results returns the recorded evaluation counts for a flag (result -> count), e.g. {"true": 120, "false": 380} or per-variant counts for A/B experiments.

func (*Service) Set

func (s *Service) Set(f Flag)

Set defines or replaces a flag. An enabled flag with Rollout==0 is treated as 100% (fully on); set Rollout explicitly for a partial rollout.

func (*Service) Variant

func (s *Service) Variant(ctx context.Context, key string, subj Subject) string

Variant returns the deterministic A/B variant for a subject, or "" when the flag is missing/disabled, has no variants, or the subject doesn't match.

func (*Service) WithStore added in v0.1.2

func (s *Service) WithStore(store Store) *Service

WithStore swaps in a durable Store. Flags already in the Store are loaded into the in-memory cache; subsequent Set/Delete write through to it. Returns the Service for chaining.

type Store added in v0.1.2

type Store interface {
	Save(Flag)
	Get(key string) (Flag, bool)
	All() []Flag
	Delete(key string)
}

Store is a durable backing for flags. When set via WithStore, the in-memory map acts as a write-through read cache and Set/Delete persist to the Store.

type Subject

type Subject struct {
	ID         string            `json:"id"`
	Attributes map[string]string `json:"attributes,omitempty"`
}

Subject is who a flag is evaluated for.

Jump to

Keyboard shortcuts

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