Documentation ¶
Overview ¶
Package feature provides a simple, easy to use abstraction for working with feature flags in Go.
Index ¶
- func Equals[T comparable](a, b T) bool
- func Experiment[T any](ctx context.Context, flag *Flag, experimental func(context.Context) (T, error), ...) (T, error)
- func SetStrategy(strategy Strategy)
- func SetTracer(tracer Tracer)
- func Switch[T any](ctx context.Context, flag *Flag, ifEnabled func(context.Context) (T, error), ...) (T, error)
- type DecisionMap
- type Flag
- type FlagOpt
- type Set
- type Strategy
- type StrategyFunc
- type Tracer
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Equals ¶
func Equals[T comparable](a, b T) bool
Equals returns a function that compares to values of the same type using ==.
This can be used with Experiment when T is a comparable type.
func Experiment ¶ added in v0.3.0
func Experiment[T any](ctx context.Context, flag *Flag, experimental func(context.Context) (T, error), control func(context.Context) (T, error), equals func(new, old T) bool, ) (T, error)
Experiment runs both an experimental and a control function concurrently and compares their results using equals.
If the feature flag is enabled, the result of the experimental function will be returned, otherwise the result of the control function will be returned.
The given equals function is only called if there was no error.
When using values of a type that is comparable using ==, the global function Equals can be used to create the comparison function.
Example ¶
optimizationFlag := feature.New("optimize-posts-loading", feature.WithDescription("enables new query for loading posts")) // later post, err := feature.Experiment(myCtx, optimizationFlag, func(ctx context.Context) (Post, error) { return loadPostOptimized(ctx, postId) }, func(ctx context.Context) (Post, error) { return loadPost(ctx, postId) }, feature.Equals[Post]) if err != nil { panic(err) } fmt.Println(post)
Output:
func SetStrategy ¶
func SetStrategy(strategy Strategy)
SetStrategy sets the Strategy for the global Set.
Example ¶
// Read initial configuration from local file flags := readFlags("flags.json") feature.SetStrategy(feature.DecisionMap(flags)) go func() { // Reload flags on SIGUSR1 signals := make(chan os.Signal, 1) signal.Notify(signals, syscall.SIGUSR1) for range signals { flags := readFlags("flags.json") feature.SetStrategy(feature.DecisionMap(flags)) } }() // Main logic...
Output:
func SetTracer ¶
func SetTracer(tracer Tracer)
SetTracer sets the Tracer used for the global Set.
See Tracer for more information.
func Switch ¶ added in v0.3.0
func Switch[T any](ctx context.Context, flag *Flag, ifEnabled func(context.Context) (T, error), ifDisabled func(context.Context) (T, error), ) (T, error)
Switch checks if the associated flag is enabled and runs either ifEnabled or ifDisabled and returns their result.
Example ¶
optimizationFlag := feature.New("optimize-posts-loading", feature.WithDescription("enables new query for loading posts")) // later post, err := feature.Switch(myCtx, optimizationFlag, func(ctx context.Context) (Post, error) { return loadPostOptimized(ctx, postId) }, func(ctx context.Context) (Post, error) { return loadPost(ctx, postId) }) if err != nil { panic(err) } fmt.Println(post)
Output:
Types ¶
type DecisionMap ¶ added in v0.3.0
DecisionMap implements a simple Strategy that returns a fixed value for each flag by its name.
Checking a flag that is not in the map will panic.
Example ¶
package main import ( "encoding/json" "log" "os" "github.com/nussjustin/feature" ) func main() { staticFlagsJSON, err := os.ReadFile("flags.json") if err != nil { log.Fatalf("failed to read flags JSON: %s", err) } var staticFlags map[string]bool if err := json.Unmarshal(staticFlagsJSON, &staticFlags); err != nil { log.Fatalf("failed to parse flags JSON: %s", err) } strategy := make(feature.DecisionMap, len(staticFlags)) for name, enabled := range staticFlags { strategy[name] = enabled } feature.SetStrategy(strategy) }
Output:
type Flag ¶
type Flag struct {
// contains filtered or unexported fields
}
Flag represents a feature flag that can be enabled or disabled (toggled) dynamically at runtime and used to control the behaviour of an application, for example by dynamically changing code paths (see Experiment and Switch).
A Flag must be obtained using either New or Set.New.
Example ¶
package main import ( "html/template" "log" "net/http" "github.com/nussjustin/feature" ) func main() { // Register flag. Most of the time this will be done globally. newUiFlag := feature.New("new-ui", feature.WithDescription("enables the new web ui")) // Load old and new UI templates oldUI := template.Must(template.ParseGlob("templates/old/*.gotmpl")) newUI := template.Must(template.ParseGlob("templates/new/*.gotmpl")) http.HandleFunc("/ui", func(w http.ResponseWriter, r *http.Request) { // Choose UI based on flag. if newUiFlag.Enabled(r.Context()) { _ = newUI.Execute(w, nil) } else { _ = oldUI.Execute(w, nil) } }) log.Fatal(http.ListenAndServe(":8080", nil)) }
Output:
func Flags ¶ added in v0.3.0
func Flags() []*Flag
Flags returns a slice containing all flags registered with the global Set.
See Set.Flags for more information.
func New ¶ added in v0.3.0
New registers and returns a new Flag with the global Set.
See Set.New for more details.
func (*Flag) Description ¶
Description returns the description of the defined feature.
func (*Flag) Enabled ¶
Enabled returns true if the feature is enabled for the given context.
Example:
if trackingFlag.Enabled(ctx) { trackUser(ctx, user) }
type FlagOpt ¶ added in v0.5.0
type FlagOpt func(*Flag)
func WithDescription ¶ added in v0.5.0
WithDescription sets the description for a new flag.
func WithLabels ¶ added in v0.5.0
WithLabels adds the given labels to a new flag.
type Set ¶
type Set struct {
// contains filtered or unexported fields
}
Set manages feature flags and provides a Strategy (using SetStrategy) for making dynamic decisions about a flags' status.
A Set with no associated Strategy is invalid and checking a flag will panic.
func (*Set) Flags ¶ added in v0.3.0
Flags returns a slice containing all registered flags order by name.
func (*Set) New ¶ added in v0.3.0
New registers and returns a new Flag on s.
If the given name is empty or already registered, New will panic.
func (*Set) SetStrategy ¶
SetStrategy sets the Strategy used by s to make decisions.
type Strategy ¶
type Strategy interface { // Enabled takes the name of a feature flag and returns true if the feature is enabled or false otherwise. Enabled(ctx context.Context, name string) bool }
Strategy defines an interface used for deciding on whether a feature is enabled or not.
A Strategy must be safe for concurrent use.
func FixedStrategy ¶ added in v0.3.0
FixedStrategy returns a Strategy that always returns the given boolean decision.
type StrategyFunc ¶
StrategyFunc implements a Strategy by calling itself.
type Tracer ¶
type Tracer struct { // Decision is called every time [Flag.Enabled] is called. Decision func(ctx context.Context, f *Flag, enabled bool) // Experiment is called at the beginning of every call to [Experiment]. // // The returned function is called after both functions given to [Experiment] have returned and is passed // the values that will be returned as well as a boolean that indicates if the experiment was successful (the // results were equal and no errors occurred). // // The returned function can be nil. Experiment func(ctx context.Context, f *Flag, enabled bool) (context.Context, func(result any, err error, success bool)) // ExperimentBranch is called for each called function during [Experiment] as well as for the function called by [Switch]. // // The returned function is called after the called function has returned with the values returned by the function. // // The returned function can be nil. ExperimentBranch func(ctx context.Context, f *Flag, enabled bool) (context.Context, func(result any, err error)) // Switch is called at the beginning of every call to [Switch]. // // The returned function is called with the result that will be returned. // // The returned function can be nil. Switch func(ctx context.Context, f *Flag, enabled bool) (context.Context, func(result any, err error)) }
Tracer can be used to trace the use of calls to Flag.Enabled as well as the global helper functions Experiment and Switch.
See the documentation on each field for information on what can be traced.
All fields are optional.
A basic, pre-configured Tracer using OpenTelemetry can be found in the otelfeature subpackage.
Directories ¶
Path | Synopsis |
---|---|
Package otelfeature implements tracing and metric collection for feature flag usage using OpenTelemetry.
|
Package otelfeature implements tracing and metric collection for feature flag usage using OpenTelemetry. |