add-autogate
Adding an Autogate
Autogates enable gradual rollout of risky code changes independent of binary releases. Unlike compatibility flags (which are permanent, date-based behavioral changes), autogates are temporary gates that can be toggled on/off via internal tooling during rollout, then removed once the change is stable.
When to use an autogate vs a compat flag
| Use an autogate when... | Use a compat flag when... |
|---|---|
| Rolling out a risky internal change gradually | Changing user-visible behavior permanently |
| You need a kill switch during rollout | The change is tied to a compatibility date |
| The gate will be removed once stable | Users need to opt in or out explicitly |
Autogates and compat flags are separate mechanisms — an autogate does not become a compat flag.
Step 1: Add the enum value
Edit src/workerd/util/autogate.h. Add a new entry to the AutogateKey enum before NumOfKeys:
enum class AutogateKey {
TEST_WORKERD,
// ... existing gates ...
// Brief description of what this gate controls.
MY_NEW_FEATURE,
NumOfKeys // Reserved for iteration.
};
Naming convention: SCREAMING_SNAKE_CASE for the enum value.
Step 2: Add the string mapping
Edit src/workerd/util/autogate.c++. Add a case to the KJ_STRINGIFY switch before the NumOfKeys case:
kj::StringPtr KJ_STRINGIFY(AutogateKey key) {
switch (key) {
// ... existing cases ...
case AutogateKey::MY_NEW_FEATURE:
return "my-new-feature"_kj;
case AutogateKey::NumOfKeys:
KJ_FAIL_ASSERT("NumOfKeys should not be used in getName");
}
}
Naming convention: kebab-case for the string name. This string is what appears in runtime configuration (prefixed with workerd-autogate-). The enum name and string name should match to avoid confusion.
Step 3: Guard your code
Use Autogate::isEnabled() to conditionally execute the new code path:
#include <workerd/util/autogate.h>
// At the point where behavior should change:
if (util::Autogate::isEnabled(util::AutogateKey::MY_NEW_FEATURE)) {
// New code path
} else {
// Old code path (keep until gate is removed)
}
Step 4: Test
Three ways to test autogated code:
A. The @all-autogates test variant (automatic):
Every wd_test() and kj_test() generates a @all-autogates variant that enables all gates. If your feature is tested by existing tests, they'll automatically run with the gate enabled:
just stream-test //src/workerd/api/tests:my-test@all-autogates
B. Targeted C++ test setup:
In a C++ test file, enable specific gates:
#include <workerd/util/autogate.h>
// In test setup:
util::Autogate::initAutogateNamesForTest({"my-new-feature"_kj});
// In test teardown:
util::Autogate::deinitAutogate();
C. Environment variable:
Set WORKERD_ALL_AUTOGATES=1 to enable all gates when no explicit config is provided.
Step 5: Build and verify
just build
just stream-test //path/to:my-test@ # Old behavior (gate off)
just stream-test //path/to:my-test@all-autogates # New behavior (gate on)
Step 6: Remove the gate (after rollout)
Once the human user explicitly confirms that the feature is stable and fully rolled out:
- Remove the
AutogateKeyenum value fromautogate.h - Remove the
casefromKJ_STRINGIFYinautogate.c++ - Remove all
Autogate::isEnabled()checks, keeping only the new code path
Checklist
- Enum value added to
AutogateKeyinautogate.h(beforeNumOfKeys) - Comment describes what the gate controls
- String mapping added to
KJ_STRINGIFYinautogate.c++ - Code guarded with
Autogate::isEnabled() - Old code path preserved (for rollback)
-
@all-autogatestest variant passes - Tests cover both gated and ungated paths
Files touched
| File | What to do |
|---|---|
src/workerd/util/autogate.h |
Add enum value with comment |
src/workerd/util/autogate.c++ |
Add case to KJ_STRINGIFY |
| Your feature file(s) | Guard code with Autogate::isEnabled() |