# GO Feature Flag GO Feature Flag is a lightweight, self-hosted, and open-source feature flag solution that provides a simple and complete feature flag implementation. It supports the OpenFeature standard, enabling multi-language support through a relay proxy API server. The solution allows teams to store configuration flags in various formats (YAML, JSON, TOML) and locations (GitHub, S3, HTTP endpoints, Kubernetes ConfigMaps, MongoDB, Redis, etc.), while providing complex targeting rules, percentage-based rollouts, progressive deployments, scheduled changes, and A/B testing experimentation. GO Feature Flag can be used in two main modes: as a GO module directly embedded in Go applications for local flag evaluation, or via the relay proxy which exposes a REST API compatible with OpenFeature SDKs in multiple languages including JavaScript/TypeScript, Java/Kotlin, Python, .NET, Ruby, PHP, Swift, and Android. The relay proxy supports both remote evaluation (API calls per evaluation) and in-process evaluation (local caching with periodic sync), making it suitable for various performance requirements and deployment scenarios. ## Feature Flag Configuration Feature flags are defined in YAML, JSON, or TOML files with variations, targeting rules, and default rules for determining which value to serve to users. ```yaml # flag-config.goff.yaml # Boolean flag with percentage-based rollout new-checkout-flow: variations: enabled: true disabled: false defaultRule: percentage: enabled: 20 disabled: 80 # Multi-variant string flag with targeting rules scream-level-feature: variations: low: "whisper" medium: "talk" high: "scream" targeting: - name: admin-users query: admin eq true variation: high - name: beta-testers query: beta eq true percentage: medium: 50 high: 50 defaultRule: variation: low # JSON object flag for configuration user-dashboard-config: variations: v1: layout: "classic" showAnalytics: false maxWidgets: 5 v2: layout: "modern" showAnalytics: true maxWidgets: 10 targeting: - query: plan eq "enterprise" variation: v2 defaultRule: variation: v1 metadata: description: "Dashboard configuration by plan type" owner: "platform-team" ``` ## Relay Proxy Configuration The relay proxy is configured via a YAML file that specifies where to retrieve flag configurations, how to export evaluation data, and server settings. ```yaml # goff-proxy.yaml server: mode: http port: 1031 host: 0.0.0.0 # Where to retrieve flag configurations retrievers: - kind: file path: /goff/flags.yaml - kind: s3 bucket: my-feature-flags-bucket item: flags/production.yaml # Polling interval in milliseconds pollingInterval: 60000 # Start even if retriever fails initially startWithRetrieverError: false # Export evaluation data exporters: - kind: s3 bucket: evaluation-data-bucket flushInterval: 10000 - kind: log # Notify on flag changes notifiers: - kind: slack webhookUrl: "https://hooks.slack.com/services/xxx/yyy/zzz" - kind: webhook endpointUrl: "https://my-app.com/webhook/flags-changed" # API key authentication authorizedKeys: evaluation: - "evaluation-api-key-1" - "evaluation-api-key-2" admin: - "admin-api-key" # Add context to all evaluations evaluationContextEnrichment: env: production version: "1.0.0" # Enable Swagger UI at /swagger/ swagger: enabled: true host: localhost:1031 # Logging configuration logLevel: INFO logFormat: json ``` ## Docker Deployment Deploy the relay proxy using Docker with mounted configuration files. ```bash # Basic deployment with local flag file docker run -d \ --name go-feature-flag \ -p 1031:1031 \ -v $(pwd)/flag-config.yaml:/goff/flags.yaml \ -v $(pwd)/goff-proxy.yaml:/goff/goff-proxy.yaml \ gofeatureflag/go-feature-flag:latest # With environment variable overrides docker run -d \ --name go-feature-flag \ -p 1031:1031 \ -e LOGLEVEL=debug \ -e POLLINGINTERVAL=30000 \ -e RETRIEVERS_0_KIND=github \ -e RETRIEVERS_0_REPOSITORYSLUG=my-org/feature-flags \ -e RETRIEVERS_0_PATH=flags/production.yaml \ -e RETRIEVERS_0_BRANCH=main \ -e RETRIEVERS_0_TOKEN=ghp_xxxxxxxxxxxx \ gofeatureflag/go-feature-flag:latest # Health check curl http://localhost:1031/health # Response: {"initialized":true} # Get proxy info curl http://localhost:1031/info # Response: {"cacheRefresh":"2024-01-15T10:30:00Z"} ``` ## GO Module Usage Use GO Feature Flag directly as a Go module for local flag evaluation without the relay proxy. ```go package main import ( "context" "fmt" "log" "time" ffclient "github.com/thomaspoignant/go-feature-flag" "github.com/thomaspoignant/go-feature-flag/ffcontext" "github.com/thomaspoignant/go-feature-flag/retriever/fileretriever" "github.com/thomaspoignant/go-feature-flag/retriever/httpretriever" ) func main() { // Initialize with local file retriever err := ffclient.Init(ffclient.Config{ PollingInterval: 60 * time.Second, Retriever: &fileretriever.Retriever{ Path: "flag-config.goff.yaml", }, // Or use HTTP retriever for remote files // Retriever: &httpretriever.Retriever{ // URL: "https://my-flags-server.com/flags.yaml", // Timeout: 10 * time.Second, // }, Environment: "production", }) if err != nil { log.Fatal(err) } defer ffclient.Close() // Create evaluation context with user attributes user := ffcontext.NewEvaluationContextBuilder("user-12345"). AddCustom("email", "john@example.com"). AddCustom("firstname", "John"). AddCustom("lastname", "Doe"). AddCustom("admin", true). AddCustom("plan", "enterprise"). AddCustom("anonymous", false). Build() // Boolean flag evaluation showNewFeature, _ := ffclient.BoolVariation("new-checkout-flow", user, false) if showNewFeature { fmt.Println("Showing new checkout flow") } // String flag evaluation screamLevel, _ := ffclient.StringVariation("scream-level-feature", user, "low") fmt.Printf("Scream level: %s\n", screamLevel) // JSON object flag evaluation dashboardConfig, _ := ffclient.JSONObjectVariation("user-dashboard-config", user, map[string]interface{}{}) fmt.Printf("Dashboard config: %+v\n", dashboardConfig) // Get detailed evaluation result details, _ := ffclient.BoolVariationDetails("new-checkout-flow", user, false) fmt.Printf("Flag value: %v, Reason: %s, Variant: %s\n", details.Value, details.Reason, details.Variant) } ``` ## Node.js/TypeScript SDK Use the OpenFeature SDK with the GO Feature Flag provider for server-side JavaScript/TypeScript applications. ```typescript import { OpenFeature, EvaluationContext } from "@openfeature/server-sdk"; import { GoFeatureFlagProvider, EvaluationType } from "@openfeature/go-feature-flag-provider"; // Initialize with remote evaluation (API call per evaluation) const remoteProvider = new GoFeatureFlagProvider({ endpoint: "http://localhost:1031/", evaluationType: EvaluationType.Remote, apiKey: "my-evaluation-api-key", timeout: 10000, }); // Or initialize with in-process evaluation (local caching) const inProcessProvider = new GoFeatureFlagProvider({ endpoint: "http://localhost:1031/", evaluationType: EvaluationType.InProcess, flagChangePollingIntervalMs: 30000, dataFlushInterval: 60000, maxPendingEvents: 10000, disableDataCollection: false, }); // Set the provider await OpenFeature.setProviderAndWait(remoteProvider); const client = OpenFeature.getClient("my-app"); // Create evaluation context (targetingKey is mandatory) const userContext: EvaluationContext = { targetingKey: "user-12345", email: "john@example.com", firstname: "John", lastname: "Doe", admin: true, plan: "enterprise", company_info: { name: "Acme Corp", size: 500 }, labels: ["beta", "premium"], }; // Boolean flag evaluation const showNewFeature = await client.getBooleanValue("new-checkout-flow", false, userContext); console.log(`Show new feature: ${showNewFeature}`); // String flag evaluation with details const screamDetails = await client.getStringDetails("scream-level-feature", "low", userContext); console.log(`Scream level: ${screamDetails.value}, reason: ${screamDetails.reason}`); // Number flag evaluation const discountPercentage = await client.getNumberValue("discount-percentage", 0, userContext); console.log(`Discount: ${discountPercentage}%`); // Object flag evaluation const dashboardConfig = await client.getObjectValue("user-dashboard-config", {}, userContext); console.log(`Dashboard config:`, dashboardConfig); // Track custom events client.track("purchase_completed", userContext, { amount: 99.99, currency: "USD", items: 3, }); ``` ## React SDK Use the OpenFeature React SDK with GO Feature Flag for client-side React applications. ```tsx import React from "react"; import { OpenFeature, OpenFeatureProvider, useFlag, useSuspenseFlag } from "@openfeature/react-sdk"; import { GoFeatureFlagWebProvider } from "@openfeature/go-feature-flag-web-provider"; // Initialize the provider const goFeatureFlagProvider = new GoFeatureFlagWebProvider({ endpoint: "http://localhost:1031", apiKey: "my-web-api-key", pollInterval: 30000, }); // Set initial evaluation context OpenFeature.setContext({ targetingKey: "user-12345", email: "john@example.com", admin: false, plan: "free", }); // Set the provider (no need to await - React SDK handles suspense) OpenFeature.setProvider(goFeatureFlagProvider); // Main App component with provider wrapper function App() { return ( }> ); } // Component using feature flags function Dashboard() { // Boolean flag with useFlag hook const { value: showNewDashboard, reason } = useFlag("new-dashboard", false); // String flag const { value: theme } = useFlag("ui-theme", "light"); // Object flag for configuration const { value: config } = useFlag("dashboard-config", { widgets: [] }); return (
{showNewDashboard ? ( ) : ( )}

Flag reason: {reason}

); } // Update context when user changes function UserProfile({ user }) { React.useEffect(() => { OpenFeature.setContext({ targetingKey: user.id, email: user.email, admin: user.isAdmin, plan: user.subscription, }); }, [user]); return ; } function LoadingSpinner() { return
Loading feature flags...
; } export default App; ``` ## Java/Kotlin SDK Use the OpenFeature Java SDK with GO Feature Flag provider for JVM applications. ```java import dev.openfeature.sdk.*; import dev.openfeature.contrib.providers.gofeatureflag.*; import java.util.Map; public class FeatureFlagExample { public static void main(String[] args) { // Initialize provider with in-process evaluation GoFeatureFlagProviderOptions options = GoFeatureFlagProviderOptions.builder() .endpoint("https://relay-proxy.example.com") .evaluationType(EvaluationType.IN_PROCESS) .apiKey("my-api-key") .timeout(10000) .flagChangePollingIntervalMs(60000) .flushIntervalMs(5000) .maxPendingEvents(10000) .disableDataCollection(false) .build(); FeatureProvider provider = new GoFeatureFlagProvider(options); OpenFeatureAPI.getInstance().setProviderAndWait(provider); Client client = OpenFeatureAPI.getInstance().getClient("my-app"); // Create evaluation context with user attributes EvaluationContext userContext = new MutableContext("user-12345") .add("email", "john@example.com") .add("firstname", "John") .add("lastname", "Doe") .add("admin", true) .add("plan", "enterprise") .add("age", 30) .add("anonymous", false); // Boolean flag evaluation Boolean showNewFeature = client.getBooleanValue("new-checkout-flow", false, userContext); if (showNewFeature) { System.out.println("Showing new checkout flow"); } // String flag with details FlagEvaluationDetails screamDetails = client.getStringDetails( "scream-level-feature", "low", userContext); System.out.printf("Scream level: %s, reason: %s, variant: %s%n", screamDetails.getValue(), screamDetails.getReason(), screamDetails.getVariant()); // Integer flag Integer maxItems = client.getIntegerValue("max-cart-items", 10, userContext); System.out.printf("Max cart items: %d%n", maxItems); // Object flag Value configValue = client.getObjectValue("dashboard-config", Value.objectToValue(Map.of()), userContext); System.out.printf("Dashboard config: %s%n", configValue.asStructure()); // Cleanup OpenFeatureAPI.getInstance().shutdown(); } } ``` ## Python SDK Use the OpenFeature Python SDK with GO Feature Flag provider. ```python from gofeatureflag_python_provider.provider import GoFeatureFlagProvider from gofeatureflag_python_provider.options import GoFeatureFlagOptions from openfeature import api from openfeature.evaluation_context import EvaluationContext from openfeature.flag_evaluation import FlagEvaluationDetails # Initialize the provider goff_provider = GoFeatureFlagProvider( options=GoFeatureFlagOptions( endpoint="http://localhost:1031/", api_key="my-api-key", timeout=10000, ) ) # Set the provider globally api.set_provider(goff_provider) # Get a client client = api.get_client(domain="my-python-app") # Create evaluation context with user attributes user_context = EvaluationContext( targeting_key="user-12345", attributes={ "email": "john@example.com", "firstname": "John", "lastname": "Doe", "admin": True, "plan": "enterprise", "age": 30, "anonymous": False, "company_info": {"name": "Acme Corp", "size": 500}, "labels": ["beta", "premium"], }, ) # Boolean flag evaluation show_new_feature = client.get_boolean_value( flag_key="new-checkout-flow", default_value=False, evaluation_context=user_context, ) if show_new_feature: print("Showing new checkout flow") # String flag with details scream_details: FlagEvaluationDetails = client.get_string_details( flag_key="scream-level-feature", default_value="low", evaluation_context=user_context, ) print(f"Scream level: {scream_details.value}, reason: {scream_details.reason}") # Number flag discount = client.get_float_value( flag_key="discount-percentage", default_value=0.0, evaluation_context=user_context, ) print(f"Discount: {discount}%") # Object flag dashboard_config = client.get_object_value( flag_key="dashboard-config", default_value={}, evaluation_context=user_context, ) print(f"Dashboard config: {dashboard_config}") ``` ## REST API - Flag Evaluation The relay proxy exposes REST endpoints for direct flag evaluation without SDKs. ```bash # Evaluate a single flag curl -X POST http://localhost:1031/v1/feature/new-checkout-flow/eval \ -H "Content-Type: application/json" \ -H "X-API-Key: my-evaluation-api-key" \ -d '{ "evaluationContext": { "key": "user-12345", "custom": { "email": "john@example.com", "admin": true, "plan": "enterprise" } }, "defaultValue": false }' # Response: # { # "trackEvents": true, # "variationType": "enabled", # "failed": false, # "version": "1.0.0", # "reason": "TARGETING_MATCH", # "errorCode": "", # "value": true, # "cacheable": true # } # Evaluate all flags for a user curl -X POST http://localhost:1031/v1/allflags \ -H "Content-Type: application/json" \ -H "X-API-Key: my-evaluation-api-key" \ -d '{ "evaluationContext": { "key": "user-12345", "custom": { "email": "john@example.com", "admin": true, "plan": "enterprise" } } }' # Response: # { # "flags": { # "new-checkout-flow": { # "value": true, # "variationType": "enabled", # "reason": "TARGETING_MATCH" # }, # "scream-level-feature": { # "value": "high", # "variationType": "high", # "reason": "TARGETING_MATCH" # } # }, # "valid": true # } # OFREP single flag evaluation (OpenFeature Remote Evaluation Protocol) curl -X POST http://localhost:1031/ofrep/v1/evaluate/flags/new-checkout-flow \ -H "Content-Type: application/json" \ -H "Authorization: Bearer my-evaluation-api-key" \ -d '{ "context": { "targetingKey": "user-12345", "email": "john@example.com", "admin": true } }' # OFREP bulk evaluation curl -X POST http://localhost:1031/ofrep/v1/evaluate/flags \ -H "Content-Type: application/json" \ -H "Authorization: Bearer my-evaluation-api-key" \ -d '{ "context": { "targetingKey": "user-12345", "email": "john@example.com" } }' ``` ## REST API - Management and Monitoring Monitor and manage the relay proxy using admin and monitoring endpoints. ```bash # Health check endpoint curl http://localhost:1031/health # Response: {"initialized":true} # Get proxy info curl http://localhost:1031/info # Response: {"cacheRefresh":"2024-01-15T10:30:00Z"} # Prometheus metrics endpoint curl http://localhost:1031/metrics # Returns Prometheus-formatted metrics # Force refresh flag configuration (requires admin API key) curl -X POST http://localhost:1031/admin/v1/retriever/refresh \ -H "X-API-Key: admin-api-key" # Response: {"refreshed":true} # Check for flag configuration changes (polling endpoint) curl http://localhost:1031/v1/flag/change \ -H "X-API-Key: my-evaluation-api-key" # Response: {"hash":1234567890,"flags":{"new-checkout-flow":1,"scream-level":2}} # Get flag configurations for in-process evaluation curl -X POST http://localhost:1031/v1/flag/configuration \ -H "Content-Type: application/json" \ -H "X-API-Key: my-evaluation-api-key" \ -d '{"flags":["new-checkout-flow","scream-level-feature"]}' ``` ## Targeting Rules with nikunjy/rules Format Create complex targeting rules using the nikunjy/rules query format. ```yaml # flag-config.goff.yaml premium-features: variations: full: true limited: false targeting: # Target specific user by email - name: specific-user query: email eq "vip@example.com" variation: full # Target admin users - name: admin-users query: admin eq true variation: full # Target enterprise plan users - name: enterprise-users query: plan eq "enterprise" variation: full # Target users with specific domain - name: company-domain query: email ew "@bigcorp.com" variation: full # Complex multi-condition rule - name: qualified-beta-users query: (beta eq true) and (age ge 18) and (country in ["US", "CA", "UK"]) percentage: full: 50 limited: 50 # Target non-anonymous users with minimum age - name: verified-adults query: (anonymous ne true) and (age gt 21) variation: full # String operations - name: test-accounts query: email sw "test" or email co "+test" variation: full disable: false # Rule is active defaultRule: variation: limited # Available operators: # eq, == : equals # ne, != : not equals # lt, < : less than # gt, > : greater than # le, <= : less than or equal # ge, >= : greater than or equal # co : contains # sw : starts with # ew : ends with # in : in list # pr : present (field exists) # not : logical not # and : logical and # or : logical or ``` ## Targeting Rules with JsonLogic Format Create targeting rules using JsonLogic for more complex logic. ```yaml # flag-config.goff.yaml advanced-features: variations: enabled: true disabled: false targeting: # Simple equality check - name: specific-user query: '{"==": [{"var": "email"}, "vip@example.com"]}' variation: enabled # Check if user is admin - name: admin-users query: '{"==": [{"var": "admin"}, true]}' variation: enabled # Check if value is in array - name: premium-plans query: '{"in": [{"var": "plan"}, ["enterprise", "business"]]}' variation: enabled # Complex AND condition - name: qualified-users query: '{"and": [{">=": [{"var": "age"}, 18]}, {"==": [{"var": "verified"}, true]}, {"in": [{"var": "country"}, ["US", "CA"]]}]}' variation: enabled # OR condition with nested checks - name: special-access query: '{"or": [{"==": [{"var": "admin"}, true]}, {"and": [{"==": [{"var": "beta"}, true]}, {">=": [{"var": "tenure_months"}, 12]}]}]}' variation: enabled # String operations - name: internal-users query: '{"endsWith": [{"var": "email"}, "@company.com"]}' variation: enabled # Numeric comparison - name: high-value-users query: '{">=": [{"var": "lifetime_value"}, 1000]}' percentage: enabled: 80 disabled: 20 defaultRule: variation: disabled ``` ## Progressive Rollout Gradually release features to users over time with automatic percentage increases. ```yaml # flag-config.goff.yaml new-payment-system: variations: new: true legacy: false defaultRule: progressiveRollout: initial: variation: legacy percentage: 0 date: 2024-01-15T00:00:00Z end: variation: new percentage: 100 date: 2024-01-22T00:00:00Z # Timeline: # Before Jan 15: 100% legacy (0% new) # Jan 15: 0% new, starts progressive rollout # Jan 16: ~14% new, 86% legacy # Jan 17: ~28% new, 72% legacy # Jan 18: ~42% new, 58% legacy # Jan 19: ~57% new, 43% legacy # Jan 20: ~71% new, 29% legacy # Jan 21: ~85% new, 15% legacy # Jan 22+: 100% new (0% legacy) # Partial rollout (keep some users on legacy) partial-migration: variations: new: true legacy: false defaultRule: progressiveRollout: initial: variation: legacy percentage: 20 # Start at 20% new, 80% legacy date: 2024-02-01T00:00:00Z end: variation: new percentage: 80 # End at 80% new, 20% legacy date: 2024-02-15T00:00:00Z # Progressive rollout on specific targeting rule targeted-rollout: variations: new: true legacy: false targeting: - name: enterprise-progressive query: plan eq "enterprise" progressiveRollout: initial: variation: legacy date: 2024-01-10T00:00:00Z end: variation: new date: 2024-01-20T00:00:00Z defaultRule: variation: legacy ``` ## Scheduled Rollout Schedule flag configuration changes for specific dates and times. ```yaml # flag-config.goff.yaml black-friday-sale: variations: sale: true normal: false defaultRule: variation: normal scheduledRollout: # Enable sale on Black Friday - date: 2024-11-29T00:00:00-05:00 defaultRule: variation: sale # Disable sale after Cyber Monday - date: 2024-12-03T00:00:00-05:00 defaultRule: variation: normal # Multi-stage feature rollout staged-release: variations: off: false beta: true ga: true targeting: [] defaultRule: variation: off scheduledRollout: # Stage 1: Enable for internal testing - date: 2024-03-01T09:00:00Z targeting: - name: internal-testers query: email ew "@company.com" variation: beta # Stage 2: Expand to beta users - date: 2024-03-08T09:00:00Z targeting: - name: internal-testers query: email ew "@company.com" variation: beta - name: beta-testers query: beta eq true variation: beta # Stage 3: 50% rollout to all users - date: 2024-03-15T09:00:00Z defaultRule: percentage: off: 50 ga: 50 # Stage 4: Full GA release - date: 2024-03-22T09:00:00Z targeting: [] defaultRule: variation: ga # Update targeting rule by name dynamic-targeting: variations: enabled: true disabled: false targeting: - name: percentage-rule query: targetingKey pr true percentage: enabled: 10 disabled: 90 defaultRule: variation: disabled scheduledRollout: # Increase percentage over time - date: 2024-04-01T00:00:00Z targeting: - name: percentage-rule percentage: enabled: 25 disabled: 75 - date: 2024-04-08T00:00:00Z targeting: - name: percentage-rule percentage: enabled: 50 disabled: 50 - date: 2024-04-15T00:00:00Z targeting: - name: percentage-rule percentage: enabled: 100 disabled: 0 ``` ## Experimentation (A/B Testing) Run time-limited experiments with automatic start and end dates. ```yaml # flag-config.goff.yaml checkout-button-experiment: variations: control: "Buy Now" variant_a: "Add to Cart" variant_b: "Purchase" defaultRule: percentage: control: 34 variant_a: 33 variant_b: 33 experimentation: start: 2024-02-01T00:00:00Z end: 2024-02-28T23:59:59Z # Outside experiment dates: returns default value from SDK # During experiment: splits traffic according to percentages # Experimentation with targeting premium-pricing-test: variations: current_pricing: monthly: 9.99 yearly: 99.99 new_pricing: monthly: 12.99 yearly: 119.99 targeting: - name: new-users-only query: created_at gt "2024-01-01" percentage: current_pricing: 50 new_pricing: 50 defaultRule: variation: current_pricing experimentation: start: 2024-03-01T00:00:00Z end: 2024-03-31T23:59:59Z trackEvents: true # Ensure events are exported for analysis metadata: experiment_id: "pricing-test-q1-2024" hypothesis: "Higher prices will not significantly impact conversion" ``` ## Multi-Team Flag Sets Configure separate flag sets for different teams with isolated configurations. ```yaml # goff-proxy.yaml server: mode: http port: 1031 logLevel: INFO flagSets: # Frontend team configuration - name: frontend apiKeys: - "frontend-api-key-prod" - "frontend-api-key-staging" retrievers: - kind: github repositorySlug: my-org/frontend-flags path: flags/production.yaml branch: main token: ${GITHUB_TOKEN} pollingInterval: 30000 exporters: - kind: s3 bucket: frontend-feature-flag-events notifiers: - kind: slack webhookUrl: "https://hooks.slack.com/services/frontend-channel" # Backend team configuration - name: backend apiKeys: - "backend-api-key-prod" retrievers: - kind: s3 bucket: backend-feature-flags item: flags/production.yaml pollingInterval: 60000 exporters: - kind: kafka kafka: addresses: - "kafka-1.example.com:9092" - "kafka-2.example.com:9092" topic: "backend-flag-events" notifiers: - kind: webhook endpointUrl: "https://backend-service.example.com/webhooks/flag-changes" # Mobile team configuration - name: mobile apiKeys: - "mobile-ios-api-key" - "mobile-android-api-key" retrievers: - kind: http url: "https://config-server.example.com/mobile/flags.yaml" headers: Authorization: "Bearer ${CONFIG_SERVER_TOKEN}" pollingInterval: 120000 evaluationContextEnrichment: platform: mobile exporters: - kind: googlecloudstorage bucket: mobile-analytics format: parquet ``` ## CLI Linting and Evaluation Use the GO Feature Flag CLI to lint configurations and evaluate flags locally. ```bash # Install the CLI go install github.com/thomaspoignant/go-feature-flag/cmd/cli@latest # Lint flag configuration files go-feature-flag-cli lint --input-file=flag-config.yaml # Output: ✅ flag-config.yaml is valid # Lint with JSON output go-feature-flag-cli lint --input-file=flag-config.yaml --format=json # Output: {"valid":true,"errors":[]} # Lint multiple files go-feature-flag-cli lint --input-file=flags/*.yaml # Evaluate a flag locally go-feature-flag-cli evaluate \ --config=flag-config.yaml \ --flag=new-checkout-flow \ --ctx='{"targetingKey":"user-123","admin":true}' # Output: {"value":true,"variationType":"enabled","reason":"TARGETING_MATCH"} # Evaluate all flags for a context go-feature-flag-cli evaluate \ --config=flag-config.yaml \ --ctx='{"targetingKey":"user-123","plan":"enterprise"}' # Output: All flag evaluations as JSON # Generate flag manifest for type-safe SDKs go-feature-flag-cli generate manifest \ --config=flag-config.yaml \ --output=flag-manifest.json # Use in CI/CD pipeline go-feature-flag-cli lint --input-file=flag-config.yaml || exit 1 ``` ## WebSocket Real-time Updates Subscribe to real-time flag change notifications via WebSocket. ```javascript // JavaScript WebSocket client for flag change notifications const ws = new WebSocket("ws://localhost:1031/ws/v1/flag/change?apiKey=my-api-key"); ws.onopen = () => { console.log("Connected to GO Feature Flag WebSocket"); }; ws.onmessage = (event) => { const changes = JSON.parse(event.data); console.log("Flag changes received:", changes); // changes structure: // { // "deleted": {"old-flag": {...}}, // "added": {"new-flag": {...}}, // "updated": { // "updated-flag": { // "before": {...}, // "after": {...} // } // } // } // Trigger local cache refresh or UI update if (changes.updated["my-critical-flag"]) { refreshFeatureFlags(); } }; ws.onerror = (error) => { console.error("WebSocket error:", error); }; ws.onclose = (event) => { console.log("WebSocket closed:", event.code, event.reason); // Implement reconnection logic setTimeout(() => reconnectWebSocket(), 5000); }; ``` ## Custom Bucketing Use custom keys for consistent flag evaluation across related entities. ```yaml # flag-config.goff.yaml # Bucket by team ID so all team members see same variation team-feature: bucketingKey: teamId variations: enabled: true disabled: false defaultRule: percentage: enabled: 50 disabled: 50 # Bucket by organization for B2B features enterprise-feature: bucketingKey: organizationId variations: basic: maxUsers: 10 features: ["core"] advanced: maxUsers: 100 features: ["core", "analytics", "export"] targeting: - query: plan eq "enterprise" variation: advanced defaultRule: variation: basic # Bucket by session for consistent anonymous experience anonymous-experiment: bucketingKey: sessionId variations: control: { layout: "grid" } variant: { layout: "list" } defaultRule: percentage: control: 50 variant: 50 ``` GO Feature Flag provides a comprehensive solution for feature flag management that scales from simple boolean toggles to complex multi-variant experiments with sophisticated targeting. The combination of file-based configuration, the OpenFeature standard, and multi-language SDK support makes it suitable for organizations of all sizes seeking a self-hosted, vendor-neutral feature flagging solution. The relay proxy architecture enables centralized flag management while supporting both remote and in-process evaluation modes to meet different latency and reliability requirements. With built-in support for progressive rollouts, scheduled changes, experimentation, and real-time notifications, teams can safely deploy features, run A/B tests, and quickly respond to issues by toggling flags without code deployments.