add-compat-flag
$
npx mdskill add cloudflare/workerd/add-compat-flagGuide developers through implementing new behavioral changes using compatibility flags in workerd.
- Manages the phased rollout of breaking changes by defining opt-in/opt-out flags.
- Requires interaction with `capnp` schema files, C++ code, and testing procedures.
- Provides a structured, multi-step workflow for integrating new feature flags safely.
- Delivers a detailed, sequential guide covering schema updates, implementation, and documentation.
SKILL.md
.github/skills/add-compat-flagView on GitHub ↗
---
name: add-compat-flag
description: Step-by-step guide for adding a new compatibility flag to workerd, including capnp schema, C++ usage, testing, and documentation requirements.
---
## Adding a Compatibility Flag
Compatibility flags control behavioral changes in workerd. They allow breaking changes to be rolled out gradually using compatibility dates. Follow these steps in order.
### Step 1: Choose flag names
Every flag needs:
- **Enable flag**: Opts in to the new behavior (e.g., `text_decoder_replace_surrogates`)
- **Disable flag**: Opts out after it becomes default (e.g., `disable_text_decoder_replace_surrogates`). Only needed if the flag will eventually become default for all workers.
Naming conventions:
- Use `snake_case`
- Enable flag describes the new behavior positively
- Disable flag uses a `no_` or `disable_` prefix, or describes the old behavior
### Step 2: Add to `compatibility-date.capnp`
Edit `src/workerd/io/compatibility-date.capnp`. Add a new field at the end of the `CompatibilityFlags` struct.
```capnp
myNewBehavior @<NEXT_ORDINAL> :Bool
$compatEnableFlag("my_new_behavior")
$compatDisableFlag("no_my_new_behavior")
$compatEnableDate("2026-03-15");
# Description of what this flag changes and why.
# Include context about the old behavior and what the new behavior fixes.
```
Replace `<NEXT_ORDINAL>` with the value returned by the `next-capnp-ordinal` tool.
Key points:
- The field number must be the next sequential ordinal. **Use the `next-capnp-ordinal` tool** to find it: call it with `file: "src/workerd/io/compatibility-date.capnp"` and `struct: "CompatibilityFlags"`. Do NOT guess or hardcode the number.
- The field name is `camelCase` and becomes the C++ getter name (e.g., `getMyNewBehavior()`).
- `$compatEnableDate` is the date after which new workers get this behavior by default. Set this to a future date. If the flag is not yet ready for a default date, omit `$compatEnableDate` — the flag will only activate when explicitly listed in `compatibilityFlags`.
- Add `$experimental` annotation if the feature is experimental and should require `--experimental` to use.
- The comment block is required and serves as internal documentation.
Available annotations:
| Annotation | Purpose |
|---|---|
| `$compatEnableFlag("name")` | Flag name to enable the behavior |
| `$compatDisableFlag("name")` | Flag name to disable after it's default |
| `$compatEnableDate("YYYY-MM-DD")` | Date after which behavior is default |
| `$compatEnableAllDates` | Force-enable for all dates (rare, breaks back-compat) |
| `$experimental` | Requires `--experimental` flag to use |
| `$neededByFl` | Must be propagated to Cloudflare's FL proxy layer |
| `$impliedByAfterDate(name = "otherFlag", date = "YYYY-MM-DD")` | Implied by another flag after a date |
### Step 3: Use the flag in C++ code
Access the flag via the auto-generated getter:
```cpp
// In code that has access to jsg::Lock:
if (FeatureFlags::get(js).getMyNewBehavior()) {
// New behavior
} else {
// Old behavior
}
```
The `FeatureFlags` class is defined in `src/workerd/io/features.h`. The getter name is derived from the capnp field name with a `get` prefix and the first letter capitalized.
For JSG API classes, you can also access flags in `JSG_RESOURCE_TYPE`:
```cpp
JSG_RESOURCE_TYPE(MyApi, workerd::CompatibilityFlags::Reader flags) {
if (flags.getMyNewBehavior()) {
JSG_METHOD(newMethod);
}
}
```
### Step 4: Add tests
Test both the old and new behavior. The test variant system helps:
- **`test-name@`** runs with the oldest compat date (2000-01-01) — tests old behavior
- **`test-name@all-compat-flags`** runs with the newest compat date (2999-12-31) — tests new behavior
In your `.wd-test` file, you can explicitly set the flag:
```capnp
const unitTests :Workerd.Config = (
services = [(
name = "my-test",
worker = (
modules = [(name = "worker", esModule = embed "my-test.js")],
compatibilityFlags = ["my_new_behavior"],
),
)],
);
```
For tests, the `compatibilityDate` field should not be included.
Write test cases that verify both behaviors. Consider edge cases where the flag changes observable behavior.
### Step 5: Document the flag
**This is required before the enable date.**
1. Create a PR in the [cloudflare-docs](https://github.com/cloudflare/cloudflare-docs) repository.
2. Add a markdown file under `src/content/compatibility-flags/` describing:
- What the flag does
- When it becomes default
- How to opt in or opt out
- Migration guidance if applicable
See `docs/api-updates.md` for more details on the documentation process.
### Step 6: Build and verify
```bash
# Build to verify the capnp schema compiles
just build
# Run the specific test
just stream-test //src/workerd/api/tests:my-test@
# Run with all compat flags to test the new behavior
just stream-test //src/workerd/api/tests:my-test@all-compat-flags
# Run the compatibility-date test to verify flag registration
just stream-test //src/workerd/io:compatibility-date-test@
```
### Checklist
- [ ] Flag added to `compatibility-date.capnp` with correct sequential field number
- [ ] Enable and disable flag names follow naming conventions
- [ ] Comment block describes old behavior, new behavior, and rationale
- [ ] Enable date is set (or intentionally omitted for experimental/unreleased flags)
- [ ] C++ code uses `FeatureFlags::get(js).getMyNewBehavior()` to branch on the flag
- [ ] Tests cover both old and new behavior
- [ ] Documentation PR created in cloudflare-docs (required before enable date)
- [ ] `compatibility-date-test` passes
More from cloudflare/workerd
- add-autogateStep-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.
- bazel-test-hygieneMandatory rules for running bazel tests during development. Load this skill before running any bazel test command, especially when validating fixes or verifying regression tests. Prevents false confidence from cached results, filter flags that silently match nothing, and partial test runs that miss breakage.
- commit-categoriesCommit categorization rules for changelogs and "what's new" summaries. MUST be loaded before categorizing commits in changelog or whats-new commands. Provides the canonical path-based category table used to group commits by area.
- dad-jokesAfter completing any task that took more than ~5 tool calls, or after long-running builds/tests finish, load this skill and deliver a dad joke to lighten the mood. Also load before any user-requested joke, pun, or limerick. Never improvise jokes without loading this skill first.
- find-and-run-testsHow to find, build, and run tests in workerd. Covers wd-test, kj_test target naming, bazel query patterns, and common flags. Also covers parent project integration tests if workerd is used as a submodule. Load this skill when you need to locate or run a test and aren't sure of the exact target name or invocation.
- identify-reviewerIdentifies the local user's GitHub account and git identity before performing code reviews. Load this skill at the start of any PR review, code review, or commit log analysis so findings can be framed relative to the user's own prior comments, commits, and approval status.
- investigation-notesStructured scratch tracking document for investigation state during bug hunts - prevents re-reading code, losing context, and rabbit holes; maintains external memory so you don't re-derive conclusions
- kj-styleKJ/workerd C++ style guidelines for code review. Covers naming, type usage, memory management, error handling, inheritance, constness, and formatting conventions. Load this skill when reviewing or writing C++ code in the workerd codebase.
- markdown-draftsUse markdown formatting when drafting content intended for external systems (GitHub issues/PRs, Jira tickets, wiki pages, design docs, etc.) so formatting is preserved when the user copies it. Load this skill before producing any draft the user will paste elsewhere.
- module-registryLoad when working with the module registry in workerd — reading, modifying, debugging, or reviewing module resolution, compilation, evaluation, or registration code. Provides pointers to three reference documents covering the legacy registry, V8 module internals, and the new registry design.