Lập trình · 19/09/2026

Feature Flags in Practice: Release Safely Without Redeploying

Feature flags separate deploying code from releasing behavior. A team can ship disabled code, expose it internally, increase the audience gradually, and turn it off during an incident without another build. Without lifecycle discipline, however, flags become permanent configuration and make every code path harder to understand.

Feature flag thực chiến: Phát hành tính năng an toàn không cần deploy lại

Feature flags separate deploying code from releasing behavior. A team can ship disabled code, expose it internally, increase the audience gradually, and turn it off during an incident without another build. Without lifecycle discipline, however, flags become permanent configuration and make every code path harder to understand.

Deployment is not release

Deployment moves an artifact into an environment. Release decides who receives new behavior:

const enabled = flags.getBooleanValue(
  'checkout-v2', false,
  { targetingKey: user.id, plan: user.plan }
);
return enabled ? checkoutV2(order) : checkoutV1(order);

The default is a safety decision. An unproven feature normally falls back to false. A protective kill switch needs unambiguous naming and semantics so operators cannot invert it under pressure.

Four common flag types

TypePurposeLifetime
ReleaseHide unfinished code and roll out graduallyShort
ExperimentCompare variants against a hypothesisUntil enough evidence
Ops / kill switchShed load or disable a failing integrationPotentially long
EntitlementGrant behavior by plan or contractLong; manage as policy

Do not treat a release flag as authorization. Access control belongs on the server with auditability and deny-by-default behavior.

Percentage rollout must be stable

Do not choose randomly on every request. Hash a stable subject:

bucket = hash(flagKey + ':' + targetingKey) % 10000
enabled = bucket < rolloutBasisPoints

Choose user, account, or tenant according to the required consistency boundary. B2B workflows often need tenant-level allocation so colleagues see the same behavior.

Evaluation context without unnecessary PII

OpenFeature standardizes evaluation APIs, providers, context, and hooks. Context can include application, region, tenant, plan, and targeting key. Include only attributes required by rules; prefer an opaque identifier over names or email addresses. Order rules clearly: emergency override, internal allowlist, compatibility constraints, percentage rollout, then default.

Client-side or server-side?

  • Server-side: business logic, algorithms, integrations, and data control.
  • Client-side: presentation changes, while assuming downloaded flags are observable and modifiable.

Never use a client flag to protect secrets or authorization. When both frontend and backend participate, the backend remains authoritative for data-changing behavior.

When the flag system fails

SDKs should cache configuration locally; evaluation should not make a synchronous network call per request. Define each default, acceptable stale-cache duration, provider-not-ready behavior, type mismatch handling, and alerts that distinguish an error fallback from a rule result.

There is no universal fail-open choice. Cosmetic behavior may default on, a new payment path should return to the old flow, and authorization must fail closed.

Observe by variant

Aggregate dashboards can hide a regression affecting a 5% cohort. Split request count, errors, latency, conversion, and business invariants by variant. Record key, variant, reason, provider, and ruleset version while minimizing sensitive context. Set rollback guardrails before rollout and test the kill switch before an incident.

A rollout sequence

  1. Deploy disabled and verify the old path.
  2. Enable for developers or employees.
  3. Canary at 1% for a representative load cycle.
  4. Advance through 5%, 25%, 50%, and 100% checkpoints.
  5. Stop or disable when a guardrail fails.
  6. After stabilization, delete the old path and flag.

Observation time must match the business cycle; five minutes cannot validate an end-of-day job or monthly billing.

Testing without combinatorial explosion

  • Unit-test enabled and disabled paths at key decisions.
  • Contract-test types, defaults, and required context.
  • Integration-test provider failures, timeout, and stale cache.
  • Use E2E tests for critical journeys, not every flag combination.
  • Run production smoke tests on an internal cohort.

Reducing simultaneous flags and nested branches is more effective than attempting every possible combination.

Cleanup belongs in the Definition of Done

Every flag needs an owner, purpose, creation date, expiry date, default, and removal ticket. At stable 100% rollout, remove the old branch, temporary rules and dashboards, and the control-plane flag. Linting and expiry reports can highlight stale flags.

Production checklist

  • Stable key and behavior-oriented name.
  • Safe default tested without a provider.
  • Consistent hashing by user, account, or tenant.
  • No client flag used as authorization.
  • Variant telemetry without leaking PII.
  • RBAC, audit logs, and an emergency process for rule changes.
  • An owner and removal date for every release flag.

Conclusion

Feature flags control release risk; they are not permanent storage for unfinished branches. Safe defaults, deterministic rollout, variant telemetry, a tested kill switch, and disciplined cleanup let teams release in smaller steps, learn sooner, and recover without waiting for another deployment.

References

Discussion

Comments 0

Sign in to comment

You need an account to join the discussion and reply to other readers.

Sign inRegister

No comments yet. Be the first to share your thoughts.