APM

>Agent Skill

@cloudflare/add-autogate

skilldevelopment

Step-by-step guide for adding a new autogate to workerd for gradual rollout of risky changes, including enum registration, string mapping, usage pattern, and testing.

cppdocumentation
apm::install
$apm install @cloudflare/add-autogate
apm::skill.md
---
name: add-autogate
description: Step-by-step guide for adding a new autogate to workerd for gradual rollout of risky changes, including enum registration, string mapping, usage pattern, and testing.
---

## 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`**:

```cpp
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**:

```cpp
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:

```cpp
#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:

```bash
just stream-test //src/workerd/api/tests:my-test@all-autogates
```

**B. Targeted C++ test setup**:

In a C++ test file, enable specific gates:

```cpp
#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

```bash
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:

1. Remove the `AutogateKey` enum value from `autogate.h`
2. Remove the `case` from `KJ_STRINGIFY` in `autogate.c++`
3. Remove all `Autogate::isEnabled()` checks, keeping only the new code path

### Checklist

- [ ] Enum value added to `AutogateKey` in `autogate.h` (before `NumOfKeys`)
- [ ] Comment describes what the gate controls
- [ ] String mapping added to `KJ_STRINGIFY` in `autogate.c++`
- [ ] Code guarded with `Autogate::isEnabled()`
- [ ] Old code path preserved (for rollback)
- [ ] `@all-autogates` test 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()` |