features
Feature Flags System
Feature flags allow enabling/disabling optional functionality with per-feature configuration.
Configuration
Features are configured via JSON passed through environment variable or command-line argument:
# Environment variable
export FEATURES='{"vector_search":{"enabled":true,"model":"text-embedding-3-small"}}'
# Command-line argument
./server --features='{"vector_search":{"enabled":true}}'
Architecture
┌─────────────────────────────────────────────────────────┐
│ internal/features/ │
├─────────────────────────────────────────────────────────┤
│ features.go - Features struct, CheckPanic() │
│ vector_search.go - VectorSearchConfig │
│ (future features...) - Additional feature configs │
└─────────────────────────────────────────────────────────┘
Features Struct
// internal/features/features.go
type Features struct {
VectorSearch VectorSearchConfig `json:"vector_search"`
}
// CheckPanic validates all features and panics if:
// - Validation fails (invalid config values)
// - Required dependencies missing (env vars, etc.)
func (f *Features) CheckPanic() {
// 1. ozzo validation for all features
// 2. Check required env vars for enabled features
}
Adding a New Feature
- Create config struct in
internal/features/:
// internal/features/my_feature.go
type MyFeatureConfig struct {
Enabled bool `json:"enabled"`
Option1 string `json:"option1"`
Option2 int `json:"option2"`
}
func (c MyFeatureConfig) Validate() error {
return ozzo.ValidateStruct(&c,
ozzo.Field(&c.Option1, ozzo.When(c.Enabled, ozzo.Required)),
ozzo.Field(&c.Option2, ozzo.When(c.Enabled, ozzo.Min(1))),
)
}
- Add to Features struct:
type Features struct {
VectorSearch VectorSearchConfig `json:"vector_search"`
MyFeature MyFeatureConfig `json:"my_feature"` // add here
}
- Add validation in CheckPanic():
func (f *Features) CheckPanic() {
err := ozzo.ValidateStruct(f,
ozzo.Field(&f.VectorSearch),
ozzo.Field(&f.MyFeature), // add here
)
if err != nil {
panic(fmt.Sprintf("features validation failed: %v", err))
}
// Check required env vars
if f.MyFeature.Enabled {
if os.Getenv("MY_FEATURE_API_KEY") == "" {
panic("MY_FEATURE_API_KEY required when my_feature.enabled=true")
}
}
}
- Use in application:
// cmd/server/main.go
cfg.Features.CheckPanic()
// anywhere via env
if env.Features().MyFeature.Enabled {
// feature-specific logic
}
Usage in Code
Access features through the app's Env interface:
// In use case
type Env interface {
Features() features.Features
}
func Resolve(ctx context.Context, env Env, input Input) (Payload, error) {
if env.Features().VectorSearch.Enabled {
// perform vector search
}
// fallback to regular search
}
Default Values
When FEATURES env var is not set or empty, all features are disabled by default:
func DefaultFeatures() Features {
return Features{
VectorSearch: VectorSearchConfig{
Enabled: false,
Model: "text-embedding-3-small",
},
}
}
Validation Rules
- Struct validation - ozzo-validation for field constraints
- Dependency validation - CheckPanic() verifies required env vars
- Startup enforcement - Server panics if validation fails (fail-fast)
Environment Variables per Feature
| Feature | Required Env Vars |
|---|---|
vector_search |
OPENAI_API_KEY |