Forge
1.x
Docs/Forge/Capabilities
Open

Reading7 min
Updated31 Jul 2026
Sourcev1/extensions/features/features.mdx

Boolean Flags01

The simplest and most common use case: on/off feature toggles. IsEnabled returns true if the flag exists, is enabled, and evaluates to true for the given user context. IsEnabledWithDefault allows specifying a fallback value.

svc := features.MustGet(app.Container())
userCtx := features.NewUserContext("user-123")

// Simple boolean check (defaults to false)
if svc.IsEnabled(ctx, "new-checkout", userCtx) {
    // Show new checkout flow
}

// With explicit default
if svc.IsEnabledWithDefault(ctx, "beta-features", userCtx, true) {
    // Enabled by default unless explicitly disabled
}

On error (provider unavailable, flag missing), IsEnabled returns false and IsEnabledWithDefault returns the specified default. Errors are logged as warnings.

Typed Flag Values02

Beyond booleans, flags can carry string, integer, float, or complex JSON values for dynamic configuration:

// String values (e.g., theme, variant)
theme := svc.GetString(ctx, "theme", userCtx, "light")

// Integer values (e.g., limits, counts)
maxItems := svc.GetInt(ctx, "max-upload-size-mb", userCtx, 10)

// Float values (e.g., percentages, multipliers)
discount := svc.GetFloat(ctx, "discount-rate", userCtx, 0.0)

// Complex JSON values (e.g., feature configuration)
config := svc.GetJSON(ctx, "checkout-config", userCtx, map[string]any{
    "steps":   3,
    "showTax": true,
})

Each getter accepts a default value that is returned when the flag is not found, disabled, or the provider returns an error.

User Targeting03

The UserContext provides rich attributes for targeted flag evaluation. Build it using the fluent API:

userCtx := features.NewUserContext("user-123").
    WithEmail("[email protected]").
    WithName("Alice Smith").
    WithGroups([]string{"beta-testers", "premium"}).
    WithIP("192.168.1.100").
    WithCountry("US").
    WithAttribute("plan", "enterprise").
    WithAttribute("signup_date", "2024-01-15")

Available Attributes for Targeting

AttributeSourceTargeting Key
User IDNewUserContext(id)user_id
EmailWithEmail(email)email
NameWithName(name)name
GroupsWithGroups(groups)group
IP AddressWithIP(ip)ip
CountryWithCountry(country)country
CustomWithAttribute(key, val)Any string key

UserContext Methods

// Check group membership
if userCtx.HasGroup("beta-testers") { ... }

// Get a custom attribute
plan, ok := userCtx.GetAttribute("plan")

// Get attribute as string
planStr, ok := userCtx.GetAttributeString("plan")

Targeting Rules04

Targeting rules match user attributes against configured conditions. When a rule matches, its associated value overrides the flag's default value.

features.WithLocalFlags(map[string]features.FlagConfig{
    "premium-feature": {
        Key:     "premium-feature",
        Enabled: true,
        Type:    "boolean",
        Value:   false, // Default: disabled
        Targeting: []features.TargetingRule{
            {
                Attribute: "plan",
                Operator:  "in",
                Values:    []string{"premium", "enterprise"},
                Value:     true, // Enabled for premium/enterprise plans
            },
            {
                Attribute: "email",
                Operator:  "contains",
                Values:    []string{"@company.com"},
                Value:     true, // Enabled for internal users
            },
        },
    },
})

Supported Operators

OperatorBehavior
equalsExact match against first value in Values
containsSubstring match (case-sensitive)
inAttribute value is in the Values list
not_inAttribute value is NOT in the Values list

For the group attribute, the in operator checks if the user belongs to any of the listed groups.

Rules are evaluated in order; the first matching rule determines the flag value.

Percentage Rollouts05

Gradually roll out features to a percentage of users using consistent hashing. The same user always gets the same result for a given flag (no flickering).

features.WithLocalFlags(map[string]features.FlagConfig{
    "new-search": {
        Key:     "new-search",
        Enabled: true,
        Type:    "boolean",
        Value:   false,
        Rollout: &features.RolloutConfig{
            Percentage: 25,       // 25% of users
            Attribute:  "user_id", // Hash on user ID (default)
        },
    },
})

The rollout mechanism:

  1. Takes the configured attribute value from the user context (default: user_id).

  2. Computes an MD5 hash of the attribute value.

  3. Maps the hash to a 0--99 range.

  4. Enables the flag if the hash value is below the Percentage threshold.

This ensures deterministic, consistent bucketing -- the same user always gets the same result.

Local Provider06

The local provider stores flags in memory from configuration. It requires no external infrastructure and is ideal for development, testing, and simple production deployments.

app.RegisterExtension(features.NewExtension(
    features.WithProvider("local"),
    features.WithLocalFlags(map[string]features.FlagConfig{
        "feature-a": {Key: "feature-a", Enabled: true, Type: "boolean", Value: true},
        "max-items": {Key: "max-items", Enabled: true, Type: "integer", Value: 100},
    }),
))

Local provider capabilities:

  • Flag types: boolean, string, integer, float, json.

  • Targeting rules: attribute-based targeting with equals, contains, in, not_in operators.

  • Percentage rollouts: consistent hash-based rollout.

  • Dynamic updates: UpdateFlag(key, flag) and RemoveFlag(key) for runtime changes.

  • Thread-safe: all operations are protected by sync.RWMutex.

Evaluation Order

The local provider evaluates flags in this priority order:

  1. Targeting rules -- first matching rule wins.

  2. Rollout -- percentage-based bucketing.

  3. Flag value -- the configured Value field.

  4. Defaults -- the DefaultFlags map from config.

External Providers07

The extension supports external feature flag platforms through the Provider interface:

ProviderConfig StructStatus
LaunchDarklyLaunchDarklyConfig (SDK key, timeout)Provider implemented, not yet wired
UnleashUnleashConfig (URL, API token, app name, env)Provider implemented, not yet wired
FlagsmithFlagsmithConfig (API URL, environment key)Provider implemented, not yet wired
PostHogPostHogConfig (API key, host, personal API key)Provider implemented, not yet wired

External provider implementations exist in the providers/ package but are not yet wired into the extension's createProvider() factory. Using them directly requires constructing the provider manually.

Flag Caching08

Optional in-memory caching reduces calls to external providers:

features.WithCache(true, 10*time.Minute)
  • Default: enabled with 5-minute TTL.

  • When a cached value exists and hasn't expired, it's returned without calling the provider.

  • Cache is cleared on Refresh().

  • Caching is most beneficial with external providers to reduce network calls and latency.

Periodic Refresh09

The service runs a background goroutine that periodically refreshes flag values from the provider:

features.WithRefreshInterval(1 * time.Minute)
  • Default: 30 seconds.

  • Set to 0 to disable periodic refresh.

  • The goroutine is cleanly shut down when the service stops.

  • Manual refresh is available via svc.Refresh(ctx).

Default Values10

Configure fallback values used when the provider returns no value or is unavailable:

features.WithDefaultFlags(map[string]any{
    "new-dashboard": true,
    "max-items":     100,
    "theme":         "light",
})

Default values are checked after the provider returns an error or when a flag is not found. They act as a safety net for service resilience.

Bulk Flag Listing11

Retrieve all flag values at once for debugging, management UIs, or client-side hydration:

allFlags, err := svc.GetAllFlags(ctx, userCtx)
// Returns: map[string]any{
//   "new-dashboard": true,
//   "dark-mode":     false,
//   "max-items":     100,
// }

Provider Health12

Each provider implements Health(ctx) error for integration with the Forge health check system. The local provider always reports healthy. External providers check connectivity to their upstream service.

// Health is checked automatically by Forge if health checks are enabled
// The service delegates to: provider.Health(ctx)