Local evaluation: why flag checks shouldn't call the network

Local evaluation means your app decides whether a flag is on from rules it already holds in memory, so a flag check is a function call, not a network request. Flagger.gg's Go and Node SDKs load the ruleset once, keep it current over a stream, and save it to disk so a restart during an outage still serves the last rules. An unknown flag, or one checked before any rules load, is off.

Updated

Why not call the network per check

A request handler may check several flags. If each check asked a remote service, every page would wait on that service, and an outage there would become an outage in your app. Evaluating locally removes both problems:

  • Speed. A check reads memory and never blocks.
  • Resilience. If the flag service is unreachable, checks keep answering from the last rules.
  • Privacy. The user you're checking stays in your app. With Flagger.gg, your users' ids, emails and workspaces never reach Flagger.gg; it stores only the targeting lists people type in.

How Flagger.gg's SDKs stay current

  1. At start, the SDK loads the last-known-good ruleset from its cache file on disk.
  2. It fetches the current rules, waiting up to 3 seconds the first time by default, and registers the flags declared in your code.
  3. It opens a server-sent events stream. A change reaches the SDK within a second.
  4. If the stream drops, it polls every 5 seconds and reconnects with backoff, from 1 second doubling to 30 seconds.
  5. Every new ruleset is written to the cache file atomically, and replaces the loaded one only if it's newer.

Checks don't wait for any of this. Until a ruleset has loaded, from disk or the network, every flag is off. Ready() (Go) and ready() (Node) tell you whether one has.

In code

// Go: flagger.gg/go
client := flagger.New(flagger.Options{SDKKey: os.Getenv("FLAGGER_SDK_KEY"), Defs: defs})
client.Start(ctx) // cache, then fetch (up to 3 s), register, then stream in the background
on := client.Enabled("new_search", &flagger.Subject{UserID: user.ID})
// Node / TypeScript: @flaggergg/node
const flagger = createFlagger({ sdkKey: process.env.FLAGGER_SDK_KEY, defs });
await flagger.start(); // never rejects for network errors
const on = flagger.flag("new_search", { userId: user.id });

Both SDKs share the same evaluation rules and run the same shared test vectors, so a percentage rollout puts a user in the same bucket in Go, in Node and in the service's eval. A nil Go client is valid, with every flag off.

In development Public installs of both SDKs (npm and go get) are in development.

The trade-offs

  • The ruleset lives on your server. Your server holds every flag's rules for its environment, including targeted user ids and emails. That's why local evaluation belongs in server code, with an SDK key that can only read rules and register flags.
  • Changes aren't instant everywhere. A change arrives within a second over the stream, or within about 5 seconds while polling.
  • Browsers. In development Flagger.gg has no browser SDK yet. Your server sends the browser a map of the flags that are on for that user (evaluatedFlags in Node, For in Go), which includes only flags registered in code.

Other tools do this too

Local evaluation in server SDKs is common. Unleash's docs say its "backend SDKs perform the flag evaluation locally" and, by default, refresh every 15 seconds (Unleash SDK overview). LaunchDarkly's server-side SDKs "evaluate feature flags using their cached ruleset" and receive changes over a streaming connection (LaunchDarkly: choosing an SDK type). Both cover far more languages than Flagger.gg's Go and Node, and both offer edge or proxy options: Unleash Edge, and LaunchDarkly's Relay Proxy and edge SDKs. Facts checked October 31, 2026.

FAQ

Does checking a feature flag make a network call?

Not with Flagger.gg's SDKs. Each check reads the ruleset already in memory; only fetching and streaming rule changes use the network, in the background.

What happens to my flags if Flagger.gg is down?

Checks keep answering from the rules in memory. If your app restarts during an outage, the SDK loads the last-known-good ruleset from disk, so flags don't all turn off.

What does a flag return before the rules load?

Off. An unknown flag, one checked before any ruleset loads, or any error means off.

Does Flagger.gg see my users?

No. Users are evaluated inside your app, so their ids and emails never reach Flagger.gg.

How fast does a flag change reach my servers?

Within a second over the stream, or within about 5 seconds if the SDK has fallen back to polling.

Ship your next change behind a flag.

Sign up, add an SDK key to your app and give your agent a token.

Sign up