Documentation
¶
Overview ¶
Package feature provides a comprehensive feature flag management system for Go applications.
The feature package enables controlled feature rollouts through flexible strategies including user targeting, percentage-based rollouts, environment-based activation, and composite rules. It follows a provider-based architecture allowing for different backend implementations while maintaining a consistent API.
Architecture ¶
The package is built around three core concepts:
1. Flags - Configuration units that define features and their rollout rules 2. Strategies - Evaluation logic that determines feature availability 3. Providers - Backend storage and retrieval implementations
Feature evaluation happens in two stages: first checking if a flag is globally enabled, then evaluating its strategy against the provided context. This allows for both simple on/off toggles and sophisticated rollout rules.
The Provider interface is organized into three logical method groups:
- Evaluation methods: IsEnabled, GetFlag
- Management methods: ListFlags, CreateFlag, UpdateFlag, DeleteFlag
- Lifecycle methods: Close
Usage ¶
Basic feature flag setup:
import "github.com/dmitrymomot/saaskit/pkg/feature"
// Create a provider with initial flags
provider, err := feature.NewMemoryProvider(
&feature.Flag{
Name: "new-ui",
Description: "Enable redesigned user interface",
Enabled: true,
Strategy: feature.NewAlwaysOnStrategy(),
},
)
if err != nil {
log.Fatal(err)
}
defer provider.Close()
// Check if feature is enabled
enabled, err := provider.IsEnabled(ctx, "new-ui")
if err != nil {
// Handle error
}
if enabled {
// Show new UI
}
Strategies ¶
The package provides several built-in strategies:
AlwaysStrategy - Returns a constant value (on/off) TargetedStrategy - Enables features for specific users, groups, or percentages EnvironmentStrategy - Activates features in specific environments CompositeStrategy - Combines multiple strategies with AND/OR logic
Example of percentage-based rollout:
percentage := 25
flag := &feature.Flag{
Name: "experimental-feature",
Enabled: true,
Strategy: feature.NewTargetedStrategy(
feature.TargetCriteria{
Percentage: &percentage,
},
feature.WithUserIDExtractor(getUserID),
),
}
Context Extractors ¶
The package uses extractor functions to retrieve evaluation data from context, maintaining decoupling between the feature system and application logic:
func getUserID(ctx context.Context) string {
if user, ok := ctx.Value("user").(*User); ok {
return user.ID
}
return ""
}
strategy := feature.NewTargetedStrategy(
criteria,
feature.WithUserIDExtractor(getUserID),
)
Error Handling ¶
The package defines specific errors for different failure scenarios:
flag, err := provider.GetFlag(ctx, "unknown")
if errors.Is(err, feature.ErrFlagNotFound) {
// Flag doesn't exist
}
All errors follow consistent naming patterns and can be checked using errors.Is.
Performance Considerations ¶
The MemoryProvider uses read-write locks for thread-safe concurrent access. For high-throughput applications, consider caching IsEnabled results at the application level to reduce lock contention.
Percentage-based rollouts use consistent hashing (FNV-1a) to ensure users always receive the same feature state across evaluations.
The package includes comprehensive benchmarks for performance monitoring:
- Provider operations (IsEnabled, ListFlags) under various scenarios
- Strategy evaluation performance for all strategy types
- Concurrent access patterns
Memory optimizations include pre-allocated slice capacity in ListFlags when filtering by tags, reducing allocations during filtering operations.
Run benchmarks with: go test -bench=. ./pkg/feature/...
Examples ¶
See the package examples and README.md for detailed usage patterns including composite strategies, environment-based features, and user targeting.
Index ¶
- Variables
- type AlwaysStrategy
- type CompositeStrategy
- type EnvironmentExtractor
- type EnvironmentStrategy
- type EnvironmentStrategyOption
- type Flag
- type MemoryProvider
- func (m *MemoryProvider) Close() error
- func (m *MemoryProvider) CreateFlag(ctx context.Context, flag *Flag) error
- func (m *MemoryProvider) DeleteFlag(ctx context.Context, flagName string) error
- func (m *MemoryProvider) GetFlag(ctx context.Context, flagName string) (*Flag, error)
- func (m *MemoryProvider) IsEnabled(ctx context.Context, flagName string) (bool, error)
- func (m *MemoryProvider) ListFlags(ctx context.Context, tags ...string) ([]*Flag, error)
- func (m *MemoryProvider) UpdateFlag(ctx context.Context, flag *Flag) error
- type Provider
- type Strategy
- func NewAlwaysOffStrategy() Strategy
- func NewAlwaysOnStrategy() Strategy
- func NewAndStrategy(strategies ...Strategy) Strategy
- func NewEnvironmentStrategy(environments []string, opts ...EnvironmentStrategyOption) Strategy
- func NewOrStrategy(strategies ...Strategy) Strategy
- func NewTargetedStrategy(criteria TargetCriteria, opts ...TargetedStrategyOption) Strategy
- type TargetCriteria
- type TargetedStrategy
- type TargetedStrategyOption
- type UserGroupsExtractor
- type UserIDExtractor
Constants ¶
This section is empty.
Variables ¶
var ( ErrFlagNotFound = errors.New("feature flag not found") ErrInvalidFlag = errors.New("invalid feature flag parameters") ErrProviderNotInitialized = errors.New("feature provider not initialized") ErrInvalidContext = errors.New("invalid context for feature evaluation") ErrInvalidStrategy = errors.New("invalid feature rollout strategy") ErrOperationFailed = errors.New("feature operation failed") )
Functions ¶
This section is empty.
Types ¶
type AlwaysStrategy ¶
type AlwaysStrategy struct {
Value bool
}
type CompositeStrategy ¶
type EnvironmentExtractor ¶
Extractor function types for retrieving data from context. These allow users to define how to extract feature flag evaluation data from their application's context, maintaining decoupling from the feature package.
type EnvironmentStrategy ¶
type EnvironmentStrategy struct {
EnabledEnvironments []string
// contains filtered or unexported fields
}
type EnvironmentStrategyOption ¶
type EnvironmentStrategyOption func(*EnvironmentStrategy)
func WithEnvironmentExtractor ¶
func WithEnvironmentExtractor(extractor EnvironmentExtractor) EnvironmentStrategyOption
type Flag ¶
type Flag struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Enabled bool `json:"enabled"`
Strategy Strategy `json:"strategy,omitempty"`
Tags []string `json:"tags,omitempty"`
CreatedAt time.Time `json:"created_at,omitzero"`
UpdatedAt time.Time `json:"updated_at,omitzero"`
}
Flag represents a feature flag with its configuration.
type MemoryProvider ¶
type MemoryProvider struct {
// contains filtered or unexported fields
}
MemoryProvider is an in-memory implementation of the Provider interface. All operations create deep copies to prevent external modification of stored flags.
func NewMemoryProvider ¶
func NewMemoryProvider(initialFlags ...*Flag) (*MemoryProvider, error)
func (*MemoryProvider) Close ¶
func (m *MemoryProvider) Close() error
func (*MemoryProvider) CreateFlag ¶
func (m *MemoryProvider) CreateFlag(ctx context.Context, flag *Flag) error
func (*MemoryProvider) DeleteFlag ¶
func (m *MemoryProvider) DeleteFlag(ctx context.Context, flagName string) error
func (*MemoryProvider) UpdateFlag ¶
func (m *MemoryProvider) UpdateFlag(ctx context.Context, flag *Flag) error
type Provider ¶
type Provider interface {
// Evaluation methods
IsEnabled(ctx context.Context, flagName string) (bool, error)
GetFlag(ctx context.Context, flagName string) (*Flag, error)
// Management methods
ListFlags(ctx context.Context, tags ...string) ([]*Flag, error)
CreateFlag(ctx context.Context, flag *Flag) error
UpdateFlag(ctx context.Context, flag *Flag) error
DeleteFlag(ctx context.Context, flagName string) error
// Lifecycle methods
Close() error
}
Provider is the interface that all feature flag providers must implement.
type Strategy ¶
type Strategy interface {
// Evaluate determines if the feature should be enabled for a specific context.
// Context should contain data required by the strategy (user ID, groups, etc.).
Evaluate(ctx context.Context) (bool, error)
}
Strategy defines different ways to roll out a feature.
func NewAlwaysOffStrategy ¶
func NewAlwaysOffStrategy() Strategy
func NewAlwaysOnStrategy ¶
func NewAlwaysOnStrategy() Strategy
func NewAndStrategy ¶
func NewEnvironmentStrategy ¶
func NewEnvironmentStrategy(environments []string, opts ...EnvironmentStrategyOption) Strategy
func NewOrStrategy ¶
func NewTargetedStrategy ¶
func NewTargetedStrategy(criteria TargetCriteria, opts ...TargetedStrategyOption) Strategy
type TargetCriteria ¶
type TargetCriteria struct {
UserIDs []string `json:"user_ids,omitempty"`
Groups []string `json:"groups,omitempty"`
Percentage *int `json:"percentage,omitempty"`
// AllowList overrides all other criteria except DenyList
AllowList []string `json:"allow_list,omitempty"`
// DenyList overrides all other criteria (highest precedence)
DenyList []string `json:"deny_list,omitempty"`
}
TargetCriteria defines targeting criteria for a flag.
type TargetedStrategy ¶
type TargetedStrategy struct {
Criteria TargetCriteria
// contains filtered or unexported fields
}
func (*TargetedStrategy) Evaluate ¶
func (s *TargetedStrategy) Evaluate(ctx context.Context) (bool, error)
Evaluate determines feature enablement using a strict precedence hierarchy: 1. DenyList (highest) - blocks access regardless of other criteria 2. AllowList - grants access overriding user/group/percentage rules 3. UserIDs - direct user targeting 4. Groups - group membership targeting 5. Percentage - consistent hash-based rollout (lowest precedence)
type TargetedStrategyOption ¶
type TargetedStrategyOption func(*TargetedStrategy)
func WithUserGroupsExtractor ¶
func WithUserGroupsExtractor(extractor UserGroupsExtractor) TargetedStrategyOption
func WithUserIDExtractor ¶
func WithUserIDExtractor(extractor UserIDExtractor) TargetedStrategyOption
type UserGroupsExtractor ¶
Extractor function types for retrieving data from context. These allow users to define how to extract feature flag evaluation data from their application's context, maintaining decoupling from the feature package.
type UserIDExtractor ¶
Extractor function types for retrieving data from context. These allow users to define how to extract feature flag evaluation data from their application's context, maintaining decoupling from the feature package.