with one click
add-compat-flag
// Step-by-step guide for adding a new compatibility flag to workerd, including capnp schema, C++ usage, testing, and documentation requirements.
// Step-by-step guide for adding a new compatibility flag to workerd, including capnp schema, C++ usage, testing, and documentation requirements.
[HINT] Download the complete skill directory including SKILL.md and all related files
| 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. |
Compatibility flags control behavioral changes in workerd. They allow breaking changes to be rolled out gradually using compatibility dates. Follow these steps in order.
Every flag needs:
text_decoder_replace_surrogates)disable_text_decoder_replace_surrogates). Only needed if the flag will eventually become default for all workers.Naming conventions:
snake_caseno_ or disable_ prefix, or describes the old behaviorcompatibility-date.capnpEdit src/workerd/io/compatibility-date.capnp. Add a new field at the end of the CompatibilityFlags struct.
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:
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.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.$experimental annotation if the feature is experimental and should require --experimental to use.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 |
Access the flag via the auto-generated getter:
// 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:
JSG_RESOURCE_TYPE(MyApi, workerd::CompatibilityFlags::Reader flags) {
if (flags.getMyNewBehavior()) {
JSG_METHOD(newMethod);
}
}
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 behaviortest-name@all-compat-flags runs with the newest compat date (2999-12-31) — tests new behaviorIn your .wd-test file, you can explicitly set the flag:
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.
This is required before the enable date.
src/content/compatibility-flags/ describing:
See docs/api-updates.md for more details on the documentation process.
# 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@
compatibility-date.capnp with correct sequential field numberFeatureFlags::get(js).getMyNewBehavior() to branch on the flagcompatibility-date-test passes