Skip to content
ErrorDock

Feature flags

Boolean flags evaluated locally in your own process, the exact evaluation order, and what happens to your application when ErrorDock is unreachable.

A feature flag lets you turn a feature on and off without deploying. ErrorDock evaluates flags inside your process, against an immutable snapshot in memory.

if (ErrorDockSdk.FeatureFlags.IsEnabled("new-checkout", userId, defaultValue: false))
{
    // New implementation
}
else
{
    // Existing implementation
}

That call reads one in-memory object. No network, no disk, no lock, no database. If ErrorDock is down, has been down for a week, or has never been reachable from this machine at all, the call still returns in about a quarter of a microsecond and your application keeps running.

Local evaluation is available in the .NET SDK. The browser and React SDKs report telemetry but do not yet carry a flag client.

Evaluation order

Written out in full, because "why is this user not seeing it" has to be answerable without a debugger.

# Question If no Reason
1 Is there any configuration? your defaultValue no_configuration
2 Is the flag in it? your defaultValue unknown_flag
3 Does it configure this environment? your defaultValue no_environment_config
4 Is it enabled here? the environment's default result flag_disabled
5 Is the identity on the off list? (if yes) false explicit_target_off
6 Is the identity on the on list? (if yes) true explicit_target_on
7 Is the rollout 100 %? (if yes) true percentage_rollout
8 Is there an identity to bucket? the environment's default result no_identity
9 Is the bucket inside the rollout? (if yes) true percentage_rollout
— Otherwise the environment's default result default

Four things in that order are decisions rather than accidents:

  • The kill switch (4) comes before the target lists (5, 6). Disabling a feature switches it off for everybody, including identities somebody pinned six weeks ago. An operator acting on an incident at two in the morning must not have to audit a list first.
  • Off (5) beats on (6). An identifier that somehow reaches both lists resolves to off.
  • Explicit targets beat the rollout. That is what targeting is for.
  • Steps 1–3 return your default; steps 4 and 8, and the final fallback, return the environment's. In the first three cases ErrorDock has nothing to say, so the honest answer is whatever your application did before the flag existed. In the others it does have something to say, and "the old behaviour" is not always the false branch.

Percentage rollout

Bucketing is a deterministic hash of the flag key and the identity, so a user stays in their cohort across processes, restarts and deploys. The algorithm is a compatibility contract rather than an implementation detail: changing it would move users between cohorts mid-session with no configuration change to explain it.

Rollouts are whole percents. Explicit targeting is an exact, case-sensitive match against the context's user id or its anonymous id — both are checked, so a support engineer who pinned an anonymous id does not lose it the moment the visitor signs in.

What happens if ErrorDock is down

ErrorDock down                 → the SDK evaluates from its in-memory snapshot
Your application restarts      → the SDK loads flags.json from disk before serving traffic
No snapshot has ever existed   → every call returns your defaultValue

Nothing throws in any of those cases.

Scenario What the SDK does
Unavailable for 10 seconds Evaluations continue from memory. One failed poll, then a retry. No user-facing effect.
Unavailable for hours The snapshot keeps working. Backoff walks out to a five-minute cap. Diagnostics report the disconnection and the configuration's age.
Restart during the outage flags.json is loaded synchronously at startup. Flags answer correctly from the first request.
First-ever start, unreachable Your defaultValue, with reason no_configuration.
Corrupt snapshot file Ignored in full, a diagnostic is raised, and the SDK behaves as on a first-ever start.

The consequence, stated plainly: a change cannot reach a client that cannot reach ErrorDock — including a kill switch. That is the trade this design makes, and it is the right way round for a flag system, but it is a real trade and not a footnote.

Feature Health

Because flags and errors are in the same platform, ErrorDock can compare the error rate of the cohort that got a feature against the cohort that did not.

new-checkout · production · 7 days

  Rollout            20 %
  Exposed users      12 481
  Evaluations        842 213

  Error rate     Got the feature      1.18 %   (147 of 12 481 users hit at least one error)
                 Did not get it       0.42 %   (43 of 10 233 users hit at least one error)

  The difference is statistically significant (p = 0.003).

The error rate is a share of exposed users, not of evaluations: errors per evaluation would make an application that checks a flag in a loop look healthier the harder it spins.

The claim is calculated or it is not made. Below a hundred exposed users in either cohort the screen says there is not enough data rather than showing a number. Above it, a two-proportion z-test runs and the p-value is reported so the claim can be checked rather than trusted.

This is not an experimentation engine. The cohorts come from a deterministic hash rather than from randomisation at assignment, so a significant difference means these two groups differ, not the feature caused it.

Telemetry and billing

The SDK keeps one counter per flag, identity, result and reason for the reporting window and sends the counter rather than a row per evaluation. A million evaluations by two hundred users becomes a few hundred rows an hour.

Flag evaluations are not metered. They are a by-product of a feature you enabled, they are already aggregated before they are sent, and they are not a billed dimension.

Known limitations

  1. A disconnected SDK cannot receive a change, including a kill switch.
  2. A flag is a boolean. There are no multivariate flags.
  3. No experimentation engine — see above.
  4. No automatic rollback. The telemetry is shaped for it; the behaviour is not built.
  5. No targeting beyond exact identity matches: no country, plan, app version, platform or custom attributes.
  6. No push. Polling with a conditional GET is what makes a thousand instances cost a thousand 304s.
  7. Local evaluation is .NET only today.
  8. The flag key is immutable. Renaming would turn every call site into an unknown flag.

Last updated October 20, 2026