how-to

Feature Flags in Go, Done Right - A 2026 Tutorial With Real SDK Code

A practical guide to implementing feature flags in Go, from a hand-rolled toggle to four production SDKs. Real code, honest trade-offs, and how to pick a platform.

Published:

Feature flags in Go are easy to start and easy to get wrong. The naive version - a global boolean you flip with a redeploy - works until the day you need to turn something off at 2am without shipping code. This guide walks the whole path: a hand-rolled toggle you can write in five minutes, then four production SDKs, with real code and honest trade-offs on each.

The core idea is simple: separate “is this code deployed” from “is this feature on.” Once those are two different switches, you can deploy dark, roll out gradually, and kill a bad feature instantly. Go’s concurrency model makes this clean, because a well-built SDK holds flag rules in memory and evaluates them locally with no network hop on the request path.

Step 1: Roll your own for a single flag

Before you reach for a platform, know what you are replacing. For one or two flags that rarely change, this is genuinely enough:

package flags

import "os"

type Flags struct {
	NewCheckout bool
	FastSearch  bool
}

func Load() Flags {
	return Flags{
		NewCheckout: os.Getenv("FLAG_NEW_CHECKOUT") == "true",
		FastSearch:  os.Getenv("FLAG_FAST_SEARCH") == "true",
	}
}

Then in a handler:

if f.NewCheckout {
	renderNewCheckout(w, r)
} else {
	renderLegacyCheckout(w, r)
}

What you get: zero dependencies, instant reads, full control. What you lose: you cannot change a flag without a restart, there is no per-user targeting, no percentage rollout, no audit log, and no UI for a non-engineer to flip. The moment you want any of those, a platform pays for itself. Our feature flag management guide covers when that line gets crossed.

Step 2: Pick a platform, and know why

Four hosted or self-hosted options all ship real Go SDKs. They differ less on Go support and more on pricing model and whether you can self-host.

ToolGo SDKModelSelf-hostBest for
LaunchDarklyYes$10/connection + $8.33/1k MAUNoDeepest tooling
UnleashYes$0 self-hosted (AGPL-3.0)YesOwn-infra Go teams
FlagsmithYes$0 / $40/mo annualYes (BSD-3)Cheap open source
ConfigCatYesFlat, no per-MAUNoPredictable pricing

The pattern across all four is the same: initialize a client once at startup, cache rules in memory, evaluate locally per request. Never create a client per request - that defeats the local-evaluation design and hammers the network.

Step 3: The SDK integration pattern

Here is the shape every one of these follows in Go. Initialize once, usually in main, keep the client on a struct or package variable, and pass a user context to each evaluation. Illustrative pattern:

// main.go - initialize once at startup
client := ff.NewClient("your-sdk-key")
defer client.Close()

// per request - evaluate locally, no network hop
user := ff.NewUser("user-42")
if client.BoolVariation("new-checkout", user, false) {
	renderNewCheckout(w, r)
}

The third argument is the default. Always pass a sane default so that if the SDK cannot reach the service or the flag does not exist, your service degrades gracefully instead of panicking. Treat the flag service as a dependency that can fail, and default to the safe path.

Step 4: Choose by the reason, not the ranking

If you want the deepest tooling and can forecast cost: LaunchDarkly. It has a Go SDK and around 38 SDKs total, the broadest coverage here, plus guarded releases that auto-roll-back on a bad metric. The gotcha is pricing - it bills $10 per service connection per month plus $8.33 per 1,000 client-side MAU on your highest-volume context kind, and Vendr’s contract data puts the median around $72,000 a year. For a Go backend service where you target users, not devices, model your context volume carefully first. See how to reduce LaunchDarkly costs if the estimate alarms you.

If you run your own infra: Unleash. It is genuinely open source under AGPL-3.0 with a Go SDK, and you can self-host it including air-gapped. The open-source edition is free and real - flag management, gradual rollouts, kill switches, canary. The catch is the OSS-versus-Enterprise line: RBAC, SSO/SAML and SCIM are all gated to paid tiers, and there is no read-only seat. For a Go team that just wants flags in its own cluster and does not yet need enterprise SSO, it is the strongest self-host pick.

If you want cheap open source without the DevOps burden of Unleash’s paid tier: Flagsmith. BSD-3-Clause, a Go SDK, and the cheapest paid cloud entry here at $40/mo annual. It bills on requests, so there is no MAU trap. Self-host the OSS build for free, but budget the DevOps - it runs a Django and Postgres stack that is real work to operate in production.

If you want flat, predictable pricing: ConfigCat. It has a Go SDK and charges no per-seat and no per-MAU fees - MAUs, contexts and flag reads are unlimited on every tier including Free. You pay by config-download volume instead. The one thing to watch in Go: a chatty SDK polling too often drives your download volume and can push you across a tier. Cache aggressively and use the SDK proxy.

Step 5: Wire it in safely

Whichever you pick, the same rules apply in a Go service:

  1. Initialize the client once, at startup, and reuse it. Store it on a struct you inject into handlers.
  2. Always pass a default value to every evaluation so a service outage degrades gracefully.
  3. Use a stable user or context key so percentage rollouts are deterministic - the same user stays in the same bucket across requests.
  4. Close the client on shutdown with defer client.Close() so the background refresh goroutine stops cleanly.
  5. Clean up dead flags. A flag that shipped to 100 percent three months ago is tech debt. Our how to clean up feature flags guide has the routine.

So which one?

  • Just one or two flags that rarely change - roll your own with an env var. Do not add a dependency you do not need.
  • You want the deepest platform and can absorb usage pricing - LaunchDarkly.
  • You want to self-host in your own Go infra - Unleash, or Flagsmith if you want cheaper cloud and a request-based meter.
  • You want flat pricing with no per-MAU surprise - ConfigCat, and cache your polling.

The Go SDK is rarely the deciding factor, because all four are solid. The decision is pricing model and self-host, same as it is in any language. Doing feature flags in Java next? The Java version of this guide covers the Spring-heavy side.


Tool facts and pricing verified against each vendor’s site on 26 July 2026. Code samples are illustrative Go patterns - check each SDK’s docs for exact method signatures. Contract figures are from Vendr’s third-party buyer data and attributed as such.

Frequently Asked Questions

What is the best feature flag library for Go?

It depends on what you need. For a hosted platform with the deepest tooling, LaunchDarkly has a Go SDK and around 38 SDKs total. If you want open source you can self-host, Unleash (AGPL-3.0) and Flagsmith (BSD-3-Clause) both ship Go SDKs. If you want flat pricing with no per-MAU fee, ConfigCat has a Go SDK too. For a single toggle with no vendor, a struct and an environment variable is enough - you do not need a library.

Can I implement feature flags in Go without a third-party service?

Yes. For a handful of flags, a map or struct read from an environment variable or a config file works fine, and this guide shows that pattern first. You lose runtime updates, targeting, gradual rollouts and an audit trail. Once you need to flip a flag without a redeploy, or roll out to 5 percent of users, a platform earns its keep.

How do feature flag SDKs work in Go?

Most Go SDKs initialize a client once at startup with an SDK key, then cache flag definitions in memory and evaluate them locally per request. That keeps flag checks fast - no network hop on the hot path. The client polls or streams updates in the background. You pass a user or context key to the evaluation call so targeting and percentage rollouts can be deterministic per user.

Do feature flags slow down a Go service?

Not if the SDK evaluates locally, which the major ones do. The client holds flag rules in memory and decides in microseconds, so a flag check is a map lookup, not an API call. The cost is at startup (initial fetch) and in a background goroutine that refreshes rules. Always initialize the client once and reuse it - do not create a client per request.

Explore More

Free Newsletter

Get the Feature Flags Newsletter

Platform benchmarks, real pricing data and progressive delivery practice. No spam.

Free. Unsubscribe any time. See our privacy policy.

Related Articles