Using Flagonic from Go

Flagonic has no Go SDK of its own — by design. You use the official OpenFeature Go SDK with its official OFREP provider. Nothing in your code imports anything Flagonic-specific, so swapping flag vendors later is a config change, not a refactor.

Prerequisites

  • A running Flagonic server with a flag defined — see the overview & quickstart.
  • An SDK key (fgn_sdk_…) for your project + environment, minted in the dashboard or via the admin API.
  • Go 1.21 or newer.

Install

go get github.com/open-feature/go-sdk/openfeature
go get github.com/open-feature/go-sdk-contrib/providers/ofrep

Both modules are maintained by the OpenFeature project (a CNCF project), not by Flagonic.

Initialize once at startup

Point the OFREP provider at your Flagonic base URL, authenticating with the SDK key. Set the provider once, early in main:

package main

import (
    "context"
    "log"
    "os"

    "github.com/open-feature/go-sdk-contrib/providers/ofrep"
    "github.com/open-feature/go-sdk/openfeature"
)

func main() {
    provider := ofrep.NewProvider(
        os.Getenv("FLAGONIC_URL"),     // e.g. https://flags.example.com
        ofrep.WithBearerToken(os.Getenv("FLAGONIC_SDK_KEY")),
    )
    if err := openfeature.SetProviderAndWait(provider); err != nil {
        log.Fatalf("set flag provider: %v", err)
    }

    client := openfeature.NewClient("checkout-service")
    _ = client // use it everywhere flags are needed
}
Keep keys out of code. The SDK key selects your project and environment. Inject it (and the base URL) through configuration so the same build runs against staging and production.

Build an evaluation context

The evaluation context is what Flagonic's targeting rules and percentage rollouts match against. The targeting key should be a stable identifier for the subject (user ID, session ID, tenant ID) — it is what keeps a user inside a percentage rollout as you ramp it up.

evalCtx := openfeature.NewEvaluationContext(
    "user-42", // targetingKey — stable per subject
    map[string]interface{}{
        "plan":  "pro",
        "email": "jo@example.com",
        "build": 4217,
    },
)

Every attribute you send is available to rule conditions (eq, neq, in, not_in, contains, starts_with, ends_with, gt, gte, lt, lte, exists).

Evaluate flags

Each evaluation takes a default value, returned if the flag is missing or the server is unreachable — your code keeps working through outages:

ctx := context.Background()

enabled, _ := client.BooleanValue(ctx, "new-checkout", false, evalCtx)
if enabled {
    // new code path
}

theme, _   := client.StringValue(ctx, "banner-theme", "default", evalCtx)
limit, _   := client.IntValue(ctx, "rate-limit", 100, evalCtx)
ratio, _   := client.FloatValue(ctx, "sampling-ratio", 0.1, evalCtx)
config, _  := client.ObjectValue(ctx, "checkout-config", map[string]interface{}{}, evalCtx)

The five methods map to Flagonic's flag types: boolean, string, number (int or float), and object.

Inspect why a value was served

The …ValueDetails variants return the variant and the OpenFeature reason — useful for logging and debugging targeting:

details, _ := client.BooleanValueDetails(ctx, "new-checkout", false, evalCtx)
log.Printf("value=%v variant=%s reason=%s",
    details.Value, details.Variant, details.Reason)
TARGETING_MATCHA rule's conditions matched the context.
SPLITThe subject fell inside a percentage rollout bucket.
DEFAULTRules exist but none matched; the default variant was served.
STATICThe flag has no rules; the default variant was served.
DISABLEDThe flag is toggled off in this environment; the off variant was served.
ERROREvaluation failed (flag missing, server unreachable); your default argument was returned.

Worked example: gating an HTTP handler

func checkoutHandler(client *openfeature.Client) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        user := userFrom(r) // however you authenticate

        evalCtx := openfeature.NewEvaluationContext(user.ID,
            map[string]interface{}{"plan": user.Plan})

        if on, _ := client.BooleanValue(r.Context(), "new-checkout", false, evalCtx); on {
            newCheckout(w, r)
            return
        }
        legacyCheckout(w, r)
    }
}

Production notes

  • Set the provider once. openfeature.SetProviderAndWait at startup; create clients freely after that — they are cheap and safe for concurrent use.
  • Defaults are your safety net. Choose the value you would want during a Flagonic outage; evaluation errors return it rather than failing your request.
  • Stable targeting keys. Percentage rollouts hash the targeting key per flag. Anonymous users need a sticky identifier (e.g. a session cookie) to get a consistent experience.
  • One SDK key per service per environment makes revocation surgical — revoke a key in the dashboard and only that deployment loses access.

Switching vendors (yes, really)

Because the provider above speaks standard OFREP, pointing your service at any other OFREP-compatible backend is exactly this diff — no import changes, no code changes:

- FLAGONIC_URL=https://flags.example.com
+ FLAGONIC_URL=https://other-vendor.example.com

That door swings both ways: migrating to Flagonic from another OFREP backend is the same one-line change.