Team Brilliant

Feature flags

The two kinds of flag, what each is for, and the naming and lifecycle rules that stop them accumulating.

01 layer

capabilities

the workflows it can run on demand

03 playbooks

1

teams on autopilot

A feature flag is a switch around a code path, checked at runtime or at build time, that decides whether that path executes. It is the mechanism that lets you deploy code without releasing it, and the mechanism that lets you turn something off at 2am without a deploy.

Both uses matter, and they need different rules, so it is worth separating them.

Runtime and build time

Runtime flags turn things on and off in a running application. These are the useful ones. Six weeks of work can go live for a handful of test accounts, then a segment, then everyone, on whatever schedule the rollout plan says.

Build-time flags include or exclude code from the artifact itself. Use these when the code should not ship at all: a payment provider you are not licensed for in a given market, a platform-specific integration that would only be dead weight in the bundle.

Launch flags and risk flags

Separating flags by what they are for is what keeps the set from growing without bound.

Launch flags are temporary. You create one before the feature exists, wrap the entry point, and from that moment the work ships to production disabled. It goes on for test users, then by segment, then for everyone. Then you delete it. A launch flag that outlives its launch is a dead branch in your code that someone will eventually take.

Risk flags are permanent by design. They exist so you can switch something off when it misbehaves:

  • Wrap each third-party integration separately. On an e-commerce platform carrying several payment providers, one flag per provider means a degraded provider gets taken out of rotation instead of taking checkout down with it. The same applies to a warehouse integration when the warehouse software is having a day.
  • Wrap expensive internal features so they can be shed under load.

Rules worth setting before you have many

Flags multiply, and undisciplined flags produce a system whose behaviour nobody can predict from the code. A few conventions prevent most of it:

  • Name flags for what they enable, never what they disable. enable_paypal is unambiguous. disable_paypal forces every reader to invert it, and eventually someone gets it wrong in a hurry.
  • Every flag exists in every environment, starting with local development. A flag defined in production and missing in staging means staging is testing different code.
  • Provision flags automatically. Manual creation is how environments drift apart.
  • Give every flag a sane default, so a lookup that fails does something reasonable rather than something surprising.
  • Do not let flags depend on each other. Two flags that only make sense in certain combinations is a state machine nobody documented.

The alternative to all of this is timing releases to feature launches, which means the first time the code runs in production is also the first time customers see it. That is the stressful version, and it is the one flags exist to replace.

Feature flags in a continuous delivery pipeline shows the whole thing implemented, including provisioning, typing and archiving.