FundamentalsMay 2, 20266 min readSignaKit

Feature Flags vs Environment Variables: When to Use Each

Both tools control runtime behavior — but they operate at different layers and solve completely different problems. Here's the clear rule for choosing between them, with a side-by-side comparison and real examples.

Two diverging roads representing the choice between feature flags and environment variables

Quick verdict

Use environment variables for infrastructure config that varies by deployment environment — API keys, base URLs, connection strings, service timeouts. Use feature flags for behavioral changes you want to control at runtime without a redeploy, especially across user segments, A/B experiments, or kill switches.

Every engineering team hits this decision eventually. You need to turn something on for staging but off for production, or ship a feature to 10% of users without deploying new code. The first instinct for many developers is to reach for an environment variable. Sometimes that's right. Often it isn't.

Feature flags and environment variables are both runtime configuration tools, but they operate at different layers. Conflating them produces code that's hard to test, inflexible in production, and difficult to reason about at 2 a.m. when something breaks.

What Are Environment Variables?

Environment variables are key-value pairs injected into a process before it starts. They're the standard mechanism for passing infrastructure config that differs between environments — API keys, database connection strings, service base URLs, and anything else that varies between local, staging, and production.

They have been a cornerstone of web engineering since Adam Wiggins published The Twelve-Factor App in 2011. Environment variables are:

  • Set at deploy time, not at request time or runtime
  • Consistent for all users within a given environment — every request gets the same value
  • A redeploy away from changing in most setups
  • The right home for secrets — API keys, credentials, tokens
# .env.production
DATABASE_URL=postgres://prod-host/myapp
STRIPE_SECRET_KEY=sk_live_...
API_BASE_URL=https://api.example.com
CACHE_TTL_SECONDS=300
Server rack in a data center representing infrastructure configuration managed by environment variables

What Are Feature Flags?

Feature flags (also called feature toggles or feature switches) are named values evaluated at runtime, typically retrieved from a centralized management service. Unlike environment variables, feature flags can be:

  • Changed without redeploying — flip a flag in the dashboard; users see the effect in seconds
  • Targeted at specific users or segments — 10% rollout, beta users only, a single company
  • Used in A/B tests and experiments — expose different users to different variants and measure the difference
  • Evaluated locally — good SDKs cache rules in-process; evaluation costs under 1 ms with no network call per request
// Feature flag evaluation — SignaKit SDK
const { enabled } = signakit.decide('exp_checkout_v2', {
  userId: user.id,
  plan: user.plan,
})

if (enabled) {
  return <CheckoutV2 />
}
return <CheckoutV1 />

The flag value isn't set in a config file — it's retrieved from a service that can change it for any subset of users without touching or redeploying the application. You can learn more about how feature flags work and what you can build with them.

What Are the Key Differences Between Feature Flags and Environment Variables?

FactorEnvironment VariableFeature Flag
Change without redeploy❌ No✅ Yes
Per-user targeting❌ No✅ Yes
A/B testing / experiments❌ No✅ Yes
Suitable for secrets✅ Yes❌ No
Kill switch in seconds❌ Needs redeploy✅ Yes
Audit trail⚠️ Depends on CI/CD✅ Built-in
Works offline / without network✅ Always⚠️ Depends on SDK cache
Available on free plan✅ Yes (OS feature)✅ Yes (SignaKit Free)

When Should You Use Environment Variables?

Environment variables are the right choice when:

The value is infrastructure config.

Database URLs, API keys, OAuth client IDs, service base URLs, region identifiers. These change between local, staging, and production— but they don't change within an environment for different users or requests.

The value is a secret.

API keys and credentials should never live in a feature flag service, which is optimized for values that may be read by many clients. Secrets belong in environment variables or a dedicated secrets manager such as AWS Secrets Manager or HashiCorp Vault.

A deploy is always required to change it anyway.

If the value can only change with a code release, there's no benefit to routing it through a flag service. Keep it in environment config where it belongs.

# ✅ Right for environment variables
DATABASE_URL=postgres://...
STRIPE_SECRET_KEY=sk_live_...
NODE_ENV=production
REGION=us-east-1

When Should You Use Feature Flags?

Feature flags are the right choice when:

You need to change behavior without deploying.

A kill switch that disables a broken payment flow in 30 seconds is only possible with a feature flag. An environment variable change requires a full deployment cycle — minutes to hours depending on your pipeline.

You need per-user or per-segment targeting.

“Enable for beta users only” or “roll out to 5% of US traffic” cannot be expressed with an environment variable. Every request in that environment gets the same env var value. Feature flags were built for exactly this use case.

You're running an A/B test or experiment.

Experimentation requires exposing different users to different variants and measuring outcomes. Feature flags make this first-class, with built-in variant assignment and experiment result tracking. Learn more about A/B testing and experimentation with SignaKit.

You want to ship code before it's visible.

Dark launches — deploying code behind a flag that's off for all users — let you test in production, run load tests, and validate integrations with zero user impact.

// ✅ Right for feature flags
const { enabled } = signakit.decide('exp_new_checkout_flow', { userId })
const { variant } = signakit.decide('exp_pricing_cta_test', { userId, plan })
const { enabled: killSwitch } = signakit.decide('ops_disable_recommendations', {})
A messy terminal showing a cluttered config file full of ENABLE_ environment variable flags

The Anti-Pattern: Env Vars as Feature Flags

# ❌ Anti-pattern
ENABLE_NEW_CHECKOUT=true
ENABLE_BETA_DASHBOARD=false
ENABLE_PAYMENT_REDESIGN=true

This pattern looks harmless with 2 variables. At 20, you have a sprawling .env file full of ENABLE_*booleans. Each one requires a deploy to change, affects all users equally, has no audit trail, and can't be tested per-user. The improvised feature flag system built on environment variables gets rewritten eventually — usually after a production incident that required a deploy to fix something that should have taken 10 seconds.

The practical rule:if you'd ever want to change the value without a redeploy, or differently for different users — it's a feature flag, not an environment variable.

How Do You Use Feature Flags and Environment Variables Together?

The best setups use both — at different layers. Environment variables provide the feature flag service connection details; feature flags control the behavioral switches inside the application:

# .env — infrastructure config
SIGNAKIT_API_KEY=sk_prod_...
SIGNAKIT_ENV=production
// Application code — behavioral control via flags
import { signakit } from '@/lib/signakit'

const { enabled } = signakit.decide('rel_new_onboarding', { userId })
const { variant } = signakit.decide('exp_checkout_cta', { userId, plan })

The environment variable holds the secret needed to authenticate with the flag service. The flag service handles everything that needs runtime control — and provides a full audit trail of who changed what, when.

Frequently Asked Questions

Can I implement feature flags with environment variables?

Technically yes, but it's an anti-pattern at any real scale. Env-var-based flags can't be changed without a redeploy, can't target individual users, have no audit trail, and create a cluttered config surface. Use a purpose-built feature flag service for anything that benefits from runtime control.

Do feature flags add latency to my application?

Good SDKs evaluate flags against a locally-cached ruleset — typically in under 1 ms, with no network call per request. Ruleset updates are fetched asynchronously in the background. The latency impact is negligible and doesn't block the request path.

Are feature flags secure?

Feature flags should not hold secrets (API keys, connection strings). They're designed to be evaluated client-side in some setups and are not secret by nature. Infrastructure secrets belong in environment variables or a dedicated secrets manager. Feature flags are for behavioral control, not credential storage.

What happens if the feature flag service goes down?

SDKs like SignaKit's evaluate flags against a locally-cached ruleset. If the service is temporarily unreachable, the SDK uses the last-known rules or configured default values. Flag evaluation doesn't fail open or block requests — your application degrades gracefully.

Conclusion

Environment variables and feature flags are both configuration tools, but they live at different layers. Environment variables configure infrastructure — secrets, URLs, environment identities. Feature flags control behavior — who sees what, when, and in what variant.

The deciding question: would you ever want to change this without a redeploy, or differently for different users? If yes — it's a feature flag. If no — it's an environment variable.

If you're choosing a feature flag platform, see how SignaKit compares to LaunchDarkly and Statsig — or browse the full comparison hub.

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.