A Feature-Flag Brief That Defines Defaults, Failures and Rollback

Drafted and reviewed using AI assistance.

On this page

A feature-flag brief should say what the user sees when evaluation succeeds, when it has not become ready and when it fails. It should also say who may expand the rollout, how to return to the old path and when to remove the flag. A Boolean name alone leaves those decisions unfinished.

Take a fictional export_preview flag. It selects a new export-preview screen or the existing export screen. It does not grant access to data, change export permissions or alter the file format. This narrow boundary makes the fallback understandable: the existing export path remains available.

Define four application states

For this example, use the following local policy:

Application state Condition User-visible path
ON Ready, no error, value is Boolean true New export-preview screen
OFF Ready, no error, value is Boolean false Existing export screen
NOT_READY No error, but the adapter is not ready Existing export screen
ERROR Evaluation error or invalid value type Existing export screen

These are application states chosen for this example, not a claim that every provider exposes these four enum values. An adapter must map the selected provider's readiness, evaluation result and error details into this contract.

The OpenFeature flag-evaluation specification defines typed evaluations with a supplied default and richer evaluation details. Its type definitions distinguish errors and other result metadata. That is a useful interface reference, but a concrete provider's startup and cache behavior still needs its own checks. A returned false alone may not tell your application whether the flag is intentionally off or evaluation fell back after an error.

Make the fallback explicit in code

This pure Python function illustrates the application's branch selection. It is not an SDK integration:

def choose_export(ready, value, error=None):
    if error is not None:
        return {"path": "legacy", "state": "ERROR"}
    if not ready:
        return {"path": "legacy", "state": "NOT_READY"}
    if type(value) is not bool:
        return {"path": "legacy", "state": "ERROR"}
    return {
        "path": "preview" if value else "legacy",
        "state": "ON" if value else "OFF",
    }

The order matters. A reported error takes precedence over readiness and a cached value. A string containing "false" is an invalid type; treating it as truthy would select the wrong screen.

Five fictional cases were executed locally with Python 3.12.14:

Ready Value Error Actual state and path
Yes true None ON, preview
Yes false None OFF, legacy
No null None NOT_READY, legacy
Yes true PROVIDER_ERROR ERROR, legacy
Yes string "false" None ERROR, legacy

Those results establish the helper's listed branches. They do not establish provider startup, network recovery, rollout targeting or real export behavior. Before connecting a provider, specify how stale values are identified and whether they are allowed. For this example, the adapter must not mark an unaccepted stale result ready; until its cache policy is defined and checked, the application uses the legacy path.

Separate a UI fallback from a reversal of side effects

Switching the flag off selects the old screen for the next evaluation point. It does not automatically close an already-open preview, recall a generated file, reverse a data migration or deploy older code.

For this fictional feature, set the evaluation point at opening the export flow. Keep the chosen path stable while that flow is open. If a serious defect requires interrupting open flows, specify a separate behavior and message; do not hide it inside a vague promise of instant rollback.

Both screen paths must continue to use the same existing permission checks. A feature flag can decide which interface is offered. It is not an authorization boundary, and a hidden button does not protect the underlying operation.

Write rollout decisions before changing exposure

Use a manual decision card. Fill the names and evidence fields for your project; the blanks below are intentional, not an implied approval.

Flag: export_preview
Change controlled: new preview screen only
Default and fallback: legacy screen
Evaluation point: opening a new export flow; stable within that flow

Decision owner: [named person or role]
Implementer: [named person or role]
Initial audience: [explicitly approved cohort]
Current deployment and provider configuration: [identifiers]

Before initial exposure:
- Confirm all four application states in the actual adapter.
- Confirm export permission checks apply on both paths.
- Confirm representative files match the agreed export contract.
- Confirm the legacy path is present and usable.

Before expanding the audience:
- Record which cohort and checks were observed, with actual results.
- Record unresolved errors and the decision owner's decision.
- Define the next cohort explicitly; do not infer approval from silence.

Return to legacy if:
- A preview chooses or exports the wrong records, or
- Required permission behavior differs between the two paths, or
- A blocking preview failure has no accepted workaround.

Rollback action: authorized owner disables the preview for new flows.
Verify: a newly opened flow selects legacy using the real adapter.
Open-flow handling: [separate decision if interruption is required]
Other recovery needed: [files, data or deployment actions, if applicable]

A project may add carefully defined operational thresholds, but they need a known measurement source and an owner. Do not invent error rates or enable new telemetry merely to fill a template. Record an unperformed observation as unperformed.

Give the flag an exit condition

“Remove later” tends to leave both branches in the code indefinitely. Define what must be true before removal: the new path has been accepted for the intended audience, required clients or deployments no longer need the old branch, the owner accepts losing the flag-based fallback, and the remaining references can be removed together.

Removal is a code change with its own checks. Deleting only the remote flag while clients still evaluate it may send them to their configured default. Name the expected behavior before deleting configuration.

The implementation brief

Add the fictional export_preview branch using the four-state table.
Keep the change limited to selecting preview vs legacy export UI.

Implement the pure selection policy and a separately identified adapter.
Use legacy when not ready, on evaluation error or for an invalid type.
Check errors first. Never coerce the string "false" to a Boolean.
Resolve readiness/error/cache mapping from the chosen provider's docs.
Do not accept stale data as ready without an explicit checked policy.

Evaluate on opening the flow; keep that choice stable inside the flow.
Preserve existing permission checks, export data rules and the legacy path.
Do not change live flag settings, target cohorts, deployments or telemetry.

Deliver the implementation, five branch cases, provider-specific unknowns,
and a filled decision card for review. Report helper tests, adapter tests
and real UI checks separately. Leave rollout and flag removal as explicit
owner decisions rather than taking those actions during implementation.

The useful deliverable is a controlled choice with understandable failure behavior. The flag is only the mechanism that selects it.