TL;DR
- A feature flag is a named boolean or multivariate value evaluated at runtime — no redeploy required to change it
- The SDK fetches flag rules from the cloud, then evaluates them locally in under 1 ms
- Consistent bucketing via MurmurHash3 ensures users always see the same variant
- 4 types: release flags, experiment flags, operational flags, permission flags
- Exposure events are tracked automatically; use trackEvent() for conversion data
- The free SignaKit plan includes 1 million events per month — flag evaluations are always free
Feature flag (noun): A named configuration value evaluated at runtime that controls whether a feature, behavior, or code path is active for a given user. Also called a feature toggle, feature switch, or feature gate. Feature flags allow teams to change application behavior without modifying or redeploying code.
Every engineering team eventually ships a feature to production before it's ready for all users. Maybe you need to test it with 5% of traffic, or show it only to beta customers, or have a kill switch ready in case something goes wrong. Feature flags are the infrastructure that makes all of this possible — without branching your codebase or gating deploys.
Martin Fowler described feature toggles as “a powerful technique, allowing teams to modify system behavior without changing code.” Since his canonical 2010 article, feature flags have become a core practice at engineering organizations of every size — from two-person startups to teams running hundreds of experiments in parallel.
How Do Feature Flags Work?
A feature flag system has three moving parts: the flag configuration, the SDK, and the evaluation logic.
1. Config Fetching
When your application starts, the SDK fetches a JSON configuration file from a CDN (in SignaKit's case, served from CloudFront/S3). This file contains all flag definitions: which rules apply, which user attributes to target, what the traffic split percentages are, and which variation each bucket maps to.
2. Local Evaluation
All flag evaluation happens in-process — no network call per request. When you call decide() or decideAll(), the SDK matches the user context against the cached rules and returns the decision in under 1 ms. This means feature flags add effectively zero latency to your application.
3. Consistent Bucketing
SignaKit uses MurmurHash3 to deterministically assign users to variations. Given the same user ID and the same flag configuration, the hash always produces the same bucket — so a user who sees the “treatment” variant today will see it tomorrow, even across different sessions or devices.
// lib/signakit/getFlags.ts
import { createInstance } from '@signakit/flags-node'
const client = createInstance({ sdkKey: process.env.SIGNAKIT_SDK_KEY! })
// Wait for initial config fetch
const { success } = await client.onReady()
// Create user context with targeting attributes
const userContext = client.createUserContext('user-abc123', {
plan: 'growth',
country: 'US',
$userAgent: request.headers.get('user-agent') ?? undefined,
})
// Evaluate all flags at once — local, <1ms
const decisions = userContext.decideAll()
// decisions['new-checkout'] => { variationKey: 'treatment', enabled: true, ... }4. Automatic Exposure Tracking
Every call to decide() or decideAll() automatically fires an $exposureevent in the background (fire-and-forget). This event records which user received which variation, enabling experiment analysis without any manual instrumentation. The backend deduplicates exposure events within a 15-second window per user/flag/page combination, so rapid re-renders don't inflate your event counts.

What Are the 4 Types of Feature Flags?
Not all flags are the same. The lifecycle, audience, and purpose differ significantly across these four categories:
| Type | Prefix | Purpose | Expected lifetime |
|---|---|---|---|
| Release flag | rel_ | Gradual rollout of a finished feature | Days to weeks — delete after full rollout |
| Experiment flag | exp_ | A/B or multivariate test to measure impact | Duration of experiment — delete after winner called |
| Operational flag | ops_ | Kill switch or circuit breaker for live systems | Indefinite — kept as a permanent safety net |
| Permission flag | perm_ | Entitlement or plan-based feature access | Permanent — evolves with your pricing |
Encoding the type in the flag name makes lifetime and cleanup decisions obvious. A flag prefixed exp_ is always temporary; a flag prefixed ops_ is always permanent. For a full treatment of naming, see our guide on feature flag naming conventions.
What Are Common Use Cases for Feature Flags?
Gradual Rollouts
Ship a new feature to 1% of users, watch your metrics, expand to 10%, 50%, then 100% — all without touching the deployment pipeline. If something looks wrong, roll back by flipping the flag off in the dashboard. The change takes effect for new requests immediately.
A/B Testing and Experimentation
Assign users deterministically to control or treatment variants, measure conversion differences, and call a winner at statistical significance. Because bucketing is consistent and exposure events are automatic, you get clean experiment data without complex manual instrumentation. Learn more in our complete A/B testing guide.
Kill Switches
Wrap risky new code paths — payment flows, third-party integrations, infrastructure migrations — in an ops_ flag. If a production incident points to the new code, disable it in 30 seconds without a deploy. This is one of the highest-leverage use cases for feature flags at any team size.
Dark Launches
Deploy new code with the flag off for all users. Run integration tests, measure performance, and validate the implementation in production before exposing it to anyone. Once confident, enable the flag for your team, then beta users, then everyone.
Permissions and Entitlements
Gate features by plan, role, or organization with a perm_ flag. When a customer upgrades, they see new features immediately — no deployment required. This pattern works particularly well for tiered SaaS products where Starter vs Growth vs Enterprise functionality differs significantly.
Feature Flags vs Configuration
Two tools are commonly confused with feature flags:
Environment variablesset infrastructure config at deploy time — database URLs, API keys, region identifiers. They're identical for all users in a given environment and require a redeploy to change. Feature flags change at runtime, per user, without a deploy. Full breakdown: Feature Flags vs Environment Variables.
Hard-coded constants (like const FEATURE_ENABLED = true) need a code change and redeploy to modify. They can't target user segments, can't be changed without engineering involvement, and have no audit trail.
What Are the Key Benefits of Feature Flags?
- Decouple deploy from release. Shipping code and releasing features to users are two separate decisions. Feature flags let you ship continuously and release on your own schedule.
- Instant incident response. A kill switch is worth more than any monitoring tool when something breaks in production. Disable the feature in seconds, not the minutes or hours a rollback deployment takes.
- Data-driven decisions. A/B test features before committing to them. Multi-armed bandit optimization (available on all SignaKit plans) automatically shifts traffic toward better-performing variants.
- Trunk-based development.Merge feature work to main behind a flag that's off. No long-lived feature branches, no merge conflicts, no integration risk.
- Safer migrations. Migrate databases, APIs, and infrastructure incrementally with flags as circuit breakers at every step.
Getting Started with SignaKit
Step 1: Install the SDK
npm install @signakit/flags-nodeStep 2: Set Your SDK Key
# .env
SIGNAKIT_SDK_KEY=sk_dev_abc123xyz_1234_abcdef123456Step 3: Initialize and Evaluate Flags
import { createInstance } from '@signakit/flags-node'
const client = createInstance({
sdkKey: process.env.SIGNAKIT_SDK_KEY!,
})
const { success } = await client.onReady()
const userContext = client.createUserContext(userId, {
plan: user.plan,
$userAgent: request.headers.get('user-agent') ?? undefined,
})
const decisions = userContext.decideAll()
// decisions['rel_checkout_v2'] => { variationKey: 'on', enabled: true }
// decisions['exp_pricing_cta'] => { variationKey: 'treatment', enabled: true }Step 4: Track Conversion Events
// Track a simple conversion
await userContext.trackEvent('signup')
// Track a conversion with value (e.g., revenue)
await userContext.trackEvent('purchase', { value: 99.99 })
// Track with metadata
await userContext.trackEvent('form_submit', {
metadata: { formId: 'checkout' },
})SignaKit's free plan includes 1 million events per month. Flag evaluations (decide() and decideAll()) are always free — only automatic $exposure events and trackEvent() calls count toward your limit.
Frequently Asked Questions
What's the difference between a feature flag and a feature toggle?
They're the same thing. “Feature toggle” was the original term popularized by Martin Fowler; “feature flag” became more common in North American engineering communities. Both describe the same concept: a runtime switch that controls feature visibility. See the full comparison: Feature Toggle vs Feature Flag.
Do feature flags add latency to my app?
No meaningfully. The SDK fetches flag rules once at startup, then evaluates all flags in-process. decide() and decideAll() make no network calls — evaluation is a local hash + rule lookup that completes in under 1 ms. Background updates fetch new rules asynchronously, never on the critical request path.
How are users consistently assigned to variants?
SignaKit uses MurmurHash3 on the user ID + flag key combination. This produces a deterministic bucket (0–100) for each user. If a user falls in the 0–10 range for a 10% rollout flag, they always see the treatment — across sessions, devices, and restarts — as long as the flag configuration doesn't change.
What happens if the feature flag service goes down?
The SDK continues evaluating flags against its locally-cached ruleset. If the service is temporarily unreachable, your application uses the last successfully fetched configuration. If the SDK hasn't fetched any configuration yet (e.g., very first startup and the service is down), decide() returns null — your code should always handle the null case and render the default experience.
Are feature flags secure?
Flag configurations (rules, targeting attributes, variation names) are not secret — treat them as public configuration, not credentials. Never put API keys, passwords, or sensitive values inside a feature flag. The SDK key (SIGNAKIT_SDK_KEY) is a server-side secret; keep it in environment variables or a secrets manager, not in client-side code.
Continue reading
Get started free
Add feature flags to your app in minutes
SDKs for React, Next.js, Node, PHP, and Laravel. Free up to 1 million events per month — no credit card required.
Related reading
Feature Toggle vs Feature Flag: What's the Difference? →
Same concept, two terms — and a full breakdown of all the related names.
A/B Testing with Feature Flags: The Complete Guide →
Step-by-step: set up, track conversions, and call a winner at significance.
Feature Flags vs Environment Variables →
The clear rule for choosing the right tool, with a comparison table.
