Feature Flags

Runtime feature toggling without deployments. Define flags with defaults, check them anywhere, and toggle via API or admin panel.

Setup

import "github.com/CodeSyncr/nimbus/flags"

app.Use(flags.NewPlugin())

Defining Flags

// Boolean flag with default
flags.Define("dark_mode", false)

// Integer flag
flags.Define("max_upload_mb", 10)

// String flag
flags.Define("maintenance_message", "")

// Boolean for beta features
flags.Define("beta_features", false)

Checking Flags

// Boolean check
if flags.Enabled("dark_mode") {
    // Show dark theme
}

// Get typed values
maxUpload := flags.Get[int]("max_upload_mb")
message := flags.Get[string]("maintenance_message")

// Toggle at runtime (via API or admin panel)
flags.Set("dark_mode", true)
flags.Set("max_upload_mb", 25)

Real-Life Examples

A/B Testing

func (ctrl *ProductController) Show(c *http.Context) error {
    product := getProduct(c.Param("id"))

    if flags.Enabled("new_product_page") {
        return c.View("products/show_v2", product)
    }
    return c.View("products/show", product)
}

Gradual Rollout

func (ctrl *CheckoutController) Store(c *http.Context) error {
    if flags.Enabled("new_payment_flow") {
        return processPaymentV2(c)
    }
    return processPayment(c)
}

Maintenance Mode

func MaintenanceMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if flags.Enabled("maintenance_mode") {
            msg := flags.Get[string]("maintenance_message")
            http.Error(w, msg, http.StatusServiceUnavailable)
            return
        }
        next.ServeHTTP(w, r)
    })
}

Best Practices

  • Always define flags with sensible defaults
  • Use descriptive names: new_checkout_flow not flag1
  • Remove flags after a feature is fully rolled out
  • Use flags for rollouts and experiments, not permanent configuration
  • Combine with cache to store flag state in Redis for multi-instance sync