Skip to content

Go SDK Reference

TL;DR: Run go get github.com/canopy-labs/featureflip-go, call client.BoolVariation("flag-key", ctx, false), and you’re evaluating flags from any Go service.

The featureflip Go package provides a goroutine-safe client for evaluating feature flags. It requires Go 1.25+, supports real-time updates via SSE streaming, and uses the standard library’s net/http for all network communication with no external dependencies.

View source on GitHub

  • Go 1.25+
Terminal window
go get github.com/canopy-labs/featureflip-go

Import in your code:

import "github.com/canopy-labs/featureflip-go"

All public types and functions are in the featureflip package.

The client uses the functional options pattern. All options have sensible defaults.

OptionDefaultDescription
WithBaseURL(url)"https://eval.featureflip.io"Base URL of the Evaluation API
WithStreaming(bool)trueEnable SSE for real-time flag updates
WithPollInterval(d)30sPolling interval (used when streaming is disabled)
WithFlushInterval(d)30sInterval between event flush batches
WithFlushBatchSize(n)100Maximum number of events per flush batch
WithInitTimeout(d)10sMaximum time to wait for initial flag fetch
WithConnectTimeout(d)5sTimeout for establishing HTTP connections
WithReadTimeout(d)10sTimeout for reading HTTP responses
client, err := featureflip.Get("sdk-dev-abc123",
featureflip.WithBaseURL("https://eval.featureflip.io"),
featureflip.WithStreaming(true),
featureflip.WithPollInterval(60 * time.Second),
featureflip.WithFlushInterval(15 * time.Second),
featureflip.WithInitTimeout(30 * time.Second),
)

Create a client by calling Get with an SDK key and optional configuration:

client, err := featureflip.Get("sdk-dev-abc123")
if err != nil {
log.Fatalf("Failed to initialize: %v", err)
}
defer client.Close()

Get blocks until the initial flag configuration is fetched (up to initTimeout). It returns an error if:

  • The SDK key is empty and FEATUREFLIP_SDK_KEY is not set
  • The initial flag fetch fails
  • The initialization times out

If the sdkKey parameter is empty, the client falls back to the FEATUREFLIP_SDK_KEY environment variable.

// SDK key read from FEATUREFLIP_SDK_KEY environment variable
client, err := featureflip.Get("")
ready := client.Initialized()

Returns true once the client has successfully loaded flag data. Since Get blocks until initialization, this always returns true for a successfully created client.

All evaluation methods accept a flag key, an evaluation context, and a default value. If the flag is not found or an error occurs, the default value is returned.

ctx := featureflip.EvaluationContext{UserID: "u-123"}
enabled := client.BoolVariation("dark-mode", ctx, false)

Signature:

func (c *Client) BoolVariation(key string, ctx EvaluationContext, defaultValue bool) bool
plan := client.StringVariation("pricing-tier", ctx, "free")

Signature:

func (c *Client) StringVariation(key string, ctx EvaluationContext, defaultValue string) string
limit := client.Float64Variation("rate-limit", ctx, 100.0)

Signature:

func (c *Client) Float64Variation(key string, ctx EvaluationContext, defaultValue float64) float64

Note: JSON numbers are always deserialized as float64 in Go. Use Float64Variation for all numeric flags.

Returns the raw deserialized value for JSON flags:

config := client.JSONVariation("banner-config", ctx, map[string]any{
"text": "Welcome",
"color": "#000",
})

Signature:

func (c *Client) JSONVariation(key string, ctx EvaluationContext, defaultValue any) any

The returned value is the Go representation of the JSON value (maps, slices, strings, float64, bool, nil).

Returns an EvaluationDetail struct with the evaluated value and metadata about the decision.

detail := client.VariationDetail("dark-mode", ctx, false)
fmt.Println(detail.Value) // true
fmt.Println(detail.Reason) // "RuleMatch"
fmt.Println(detail.Variation) // "true-variation"
fmt.Println(detail.RuleID) // "rule-abc"

Signature:

func (c *Client) VariationDetail(key string, ctx EvaluationContext, defaultValue any) EvaluationDetail
type EvaluationDetail struct {
Value any
Variation string
Reason EvaluationReason
RuleID string
}
FieldTypeDescription
ValueanyThe evaluated flag value
VariationstringThe key of the matched variation
ReasonEvaluationReasonWhy this value was returned
RuleIDstringThe ID of the matched targeting rule (empty if not applicable)

The EvaluationReason type is a string with the following constants:

ConstantValueDescription
ReasonRuleMatch"RuleMatch"A targeting rule matched the context
ReasonFallthrough"Fallthrough"No rules matched; the fallthrough variation was served
ReasonFlagDisabled"FlagDisabled"The flag is disabled; the off variation was served
ReasonFlagNotFound"FlagNotFound"The flag key does not exist
ReasonError"Error"An error occurred during evaluation

The EvaluationContext struct provides user attributes for flag evaluation. Custom attributes feed into targeting rules, and UserID anchors percentage rollouts:

ctx := featureflip.EvaluationContext{
UserID: "u-123",
Attributes: map[string]any{
"email": "[email protected]",
"country": "US",
"plan": "pro",
},
}
FieldTypeDescription
UserIDstringUnique user identifier, used for percentage rollouts
Attributesmap[string]anyCustom attributes for targeting rules

Records a custom analytics event.

metadata := map[string]any{
"plan": "pro",
"amount": 29.99,
}
client.Track("purchase-completed", ctx, metadata)

Signature:

func (c *Client) Track(eventKey string, ctx EvaluationContext, metadata map[string]any)

Events are batched and flushed automatically based on WithFlushInterval and WithFlushBatchSize.

Records an identify event for user association and analytics.

client.Identify(featureflip.EvaluationContext{
UserID: "u-123",
})

Signature:

func (c *Client) Identify(ctx EvaluationContext)

Forces all buffered events to be sent to the server immediately.

client.Flush()

Shuts down the client, stops background goroutines (streaming or polling), and flushes remaining events. Returns nil (the error return is reserved for future use). Safe to call multiple times.

err := client.Close()

Signature:

func (c *Client) Close() error

Always defer Close() after creating a client:

client, err := featureflip.Get("sdk-key")
if err != nil {
log.Fatal(err)
}
defer client.Close()

Creates a client pre-populated with flag overrides. No network calls or background goroutines are started.

client := featureflip.ForTesting(map[string]any{
"dark-mode": true,
"pricing-tier": "enterprise",
"rate-limit": 500.0,
})
defer client.Close()
ctx := featureflip.EvaluationContext{UserID: "test-user"}
client.BoolVariation("dark-mode", ctx, false) // true
client.StringVariation("pricing-tier", ctx, "free") // "enterprise"

Signature:

func ForTesting(overrides map[string]any) *Client

Supported value types: bool, string, float64/int, and any JSON-serializable type. The client is fully initialized immediately and returns override values regardless of the evaluation context.

TypeDescription
ClientMain client for flag evaluation and event tracking
OptionFunctional option for configuring the client (func(*config))
EvaluationContextUser attributes for flag evaluation
EvaluationDetailDetailed result of a flag evaluation
EvaluationReasonString type describing why a value was returned