Feature Flags
The featureflags package provides feature flag evaluation with support for boolean flags, percentage-based rollouts, user targeting, and multiple flag providers. It enables safe, incremental feature releases.
Import
import "github.com/gofastadev/gofasta/pkg/featureflags"Key Types
FlagManager
type FlagManager interface {
IsEnabled(ctx context.Context, flag string) bool
IsEnabledFor(ctx context.Context, flag string, userID string) bool
GetVariant(ctx context.Context, flag string, userID string) string
AllFlags(ctx context.Context) map[string]Flag
Refresh(ctx context.Context) error
}Flag
type Flag struct {
Name string `json:"name"`
Enabled bool `json:"enabled"`
Description string `json:"description"`
Percentage float64 `json:"percentage,omitempty"`
Variants []Variant `json:"variants,omitempty"`
AllowList []string `json:"allow_list,omitempty"`
DenyList []string `json:"deny_list,omitempty"`
}Variant
type Variant struct {
Name string `json:"name"`
Weight float64 `json:"weight"`
}FlagConfig
type FlagConfig struct {
Provider string `yaml:"provider" env:"FLAGS_PROVIDER"`
FilePath string `yaml:"file_path" env:"FLAGS_FILE_PATH"`
RefreshRate time.Duration `yaml:"refresh_rate" env:"FLAGS_REFRESH_RATE"`
Endpoint string `yaml:"endpoint" env:"FLAGS_ENDPOINT"`
}Key Functions
| Function | Signature | Description |
|---|---|---|
NewFlagManager | func NewFlagManager(cfg FlagConfig) (FlagManager, error) | Creates a flag manager using the configured provider |
NewFileFlagProvider | func NewFileFlagProvider(path string) (FlagManager, error) | Loads flags from a JSON/YAML file |
NewHTTPFlagProvider | func NewHTTPFlagProvider(endpoint string, refreshRate time.Duration) (FlagManager, error) | Loads flags from a remote HTTP endpoint |
Middleware | func Middleware(fm FlagManager) func(http.Handler) http.Handler | Injects the flag manager into the request context |
FromContext | func FromContext(ctx context.Context) FlagManager | Retrieves the flag manager from context |
Flag Definition File
flags.yaml:
flags:
new_dashboard:
enabled: true
description: "New dashboard UI"
percentage: 50
allow_list:
- "user-123"
- "user-456"
dark_mode:
enabled: true
description: "Dark mode support"
checkout_v2:
enabled: true
description: "New checkout flow"
variants:
- name: control
weight: 50
- name: variant_a
weight: 30
- name: variant_b
weight: 20Usage
Basic Flag Checking
fm, err := featureflags.NewFlagManager(featureflags.FlagConfig{
Provider: "file",
FilePath: "flags.yaml",
})
if err != nil {
log.Fatalf("failed to load flags: %v", err)
}
if fm.IsEnabled(ctx, "dark_mode") {
// serve dark mode UI
}User-Targeted Flags
// Percentage rollout -- deterministic per user
if fm.IsEnabledFor(ctx, "new_dashboard", userID) {
// show new dashboard
}
// Users in the allow_list always see the feature
// fm.IsEnabledFor(ctx, "new_dashboard", "user-123") -> trueA/B Testing with Variants
variant := fm.GetVariant(ctx, "checkout_v2", userID)
switch variant {
case "variant_a":
// show variant A
case "variant_b":
// show variant B
default:
// show control
}Using in Controllers
func (c *DashboardController) Index(w http.ResponseWriter, r *http.Request) {
fm := featureflags.FromContext(r.Context())
claims := auth.ClaimsFromContext(r.Context())
if fm.IsEnabledFor(r.Context(), "new_dashboard", claims.UserID) {
httputil.JSON(w, http.StatusOK, newDashboardData)
return
}
httputil.JSON(w, http.StatusOK, legacyDashboardData)
}Refreshing Flags
Flags loaded from an HTTP provider are refreshed automatically at the configured interval. You can also trigger a manual refresh.
err := fm.Refresh(ctx)Configuration via config.yaml
feature_flags:
provider: file # "file" or "http"
file_path: flags.yaml
refresh_rate: 30sRelated Pages
- Config — Feature flag configuration
- Middleware — Inject flag manager into request context
- Observability — Track flag evaluation metrics
Last updated on