Skip to main content
workerd uses a compatibility date system to ensure that updating the runtime never breaks existing workers. Every worker must specify a compatibility date, and workerd will emulate the API as it existed on that date.

Why compatibility dates?

Runtime environments typically face a dilemma when fixing bugs or changing behavior:
  • Don’t fix the bug: Users depend on broken behavior, so changing it could break their applications
  • Fix the bug: New applications work correctly, but existing applications break
Compatibility dates solve this by allowing both:
  • New workers get the fixed behavior
  • Old workers continue using the old behavior
  • Each worker can opt in to fixes on their own schedule

How compatibility dates work

Every worker specifies a date

In your worker configuration, specify a compatibilityDate:
const myWorker :Workerd.Worker = (
  serviceWorkerScript = embed "worker.js",
  compatibilityDate = "2024-01-15"
);
This tells workerd: “Make this worker behave as if it’s running on the version of workerd that was current on January 15, 2024.”

Breaking changes are dated

When workerd needs to change behavior, the change is associated with a date:
formDataParserSupportsFiles @0 :Bool
    $compatEnableFlag("formdata_parser_supports_files")
    $compatEnableDate("2021-11-03")
    $compatDisableFlag("formdata_parser_converts_files_to_strings");
This means:
  • Workers with compatibilityDate >= "2021-11-03": Files in FormData work correctly
  • Workers with compatibilityDate < "2021-11-03": Files are converted to strings (old behavior)

workerd versions are dates

workerd’s version number is simply a date, like 20240115. This corresponds to the maximum compatibility date that version supports. You can run a worker with compatibilityDate = "2023-01-01" on workerd 20240115, and it will emulate the 2023 behavior.

Compatibility flags

Compatibility dates work by controlling a set of compatibility flags defined in src/workerd/io/compatibility-date.capnp.

Automatic flags

Flags are automatically enabled based on your compatibility date:
specCompliantUrl @10 :Bool
    $compatEnableFlag("url_standard")
    $compatEnableDate("2022-10-31")
    $compatDisableFlag("url_original");
With compatibilityDate = "2023-01-01":
  • This flag is enabled (date is after 2022-10-31)
  • Your worker gets spec-compliant URL parsing
With compatibilityDate = "2022-06-01":
  • This flag is disabled (date is before 2022-10-31)
  • Your worker gets the original URL parsing behavior

Manual flags

You can explicitly enable or disable flags:
const myWorker :Workerd.Worker = (
  serviceWorkerScript = embed "worker.js",
  compatibilityDate = "2022-06-01",
  compatibilityFlags = [
    "url_standard"  # Opt in to new URL behavior early
  ]
);

Flag naming

Each flag has:
  • Enable flag: Name to enable the new behavior (e.g., "url_standard")
  • Disable flag: Name to keep old behavior (e.g., "url_original")
  • Enable date: Date when the flag becomes default
The disable flag allows opting out:
compatibilityDate = "2023-01-01",
compatibilityFlags = [
  "url_original"  # Keep old behavior despite new date
]

Example flags from workerd

FormData file handling

formDataParserSupportsFiles @0 :Bool
    $compatEnableFlag("formdata_parser_supports_files")
    $compatEnableDate("2021-11-03")
    $compatDisableFlag("formdata_parser_converts_files_to_strings");
The original implementation incorrectly turned files into strings. This flag fixes it for workers with dates >= 2021-11-03.

URL parsing

specCompliantUrl @10 :Bool
    $compatEnableFlag("url_standard")
    $compatEnableDate("2022-10-31")
    $compatDisableFlag("url_original");
The original URL implementation wasn’t spec-compliant. This flag enables the standard-compliant implementation.

WebSocket compression

webSocketCompression @20 :Bool
    $compatEnableFlag("web_socket_compression")
    $compatEnableDate("2023-08-15")
    $compatDisableFlag("no_web_socket_compression");
WebSocket compression was added. Workers with dates before 2023-08-15 don’t have compression, ensuring they behave identically.

Node.js compatibility

nodeJsCompat @21 :Bool
    $compatEnableFlag("nodejs_compat")
    $compatDisableFlag("no_nodejs_compat");
Some flags don’t have enable dates - they must be explicitly opted into. Node.js compatibility is opt-in because it adds significant APIs and changes behavior.

Async API exception handling

captureThrowsAsRejections @12 :Bool
    $compatEnableFlag("capture_async_api_throws")
    $compatEnableDate("2022-10-31")
    $compatDisableFlag("do_not_capture_async_api_throws");
Worker APIs that return promises should never throw synchronously. This flag fixes them to return rejections instead, matching Web Platform specs.

Choosing a compatibility date

For new workers

Use a recent date, typically the current date:
compatibilityDate = "2024-01-15"
This ensures you get the latest bug fixes and behavior improvements.

For existing workers

When updating a worker’s compatibility date:
  1. Read the changelog: Review changes between your old date and the new date
  2. Test thoroughly: Test your worker with the new date in development
  3. Update incrementally: Consider updating in steps rather than jumping to the latest date
  4. Use flags if needed: If a change breaks something, use a disable flag temporarily

Compatibility date requirements

workerd versions only support compatibility dates within a certain range. Very old dates may eventually stop being supported as maintaining compatibility becomes infeasible.
A workerd version like 20240115 supports:
  • Maximum date: 2024-01-15 (the version date)
  • Minimum date: Typically several years back (check workerd documentation)

How to update compatibility dates

Step 1: Check the changelog

Review changes between your current date and the target date. On Cloudflare Workers, this is at: https://developers.cloudflare.com/workers/platform/compatibility-dates/ For workerd, check the compatibility-date.capnp file.

Step 2: Test locally

Update your config and test:
const myWorker :Workerd.Worker = (
  serviceWorkerScript = embed "worker.js",
  compatibilityDate = "2024-01-15"  # Updated from "2023-01-01"
);
Run your tests:
workerd serve config.capnp

Step 3: Test in staging

Deploy to a staging environment:
workerd serve config.capnp --socket-addr http=staging:8080
Run integration tests and manual verification.

Step 4: Deploy to production

Once confident, deploy the updated configuration to production.

Rollback if needed

If issues occur, simply revert the configuration change:
compatibilityDate = "2023-01-01"  # Reverted
The old behavior is instantly restored.

Compatibility flags for gradual migration

If a breaking change affects your worker, use flags to migrate gradually:

Option 1: Keep old behavior temporarily

compatibilityDate = "2024-01-15",
compatibilityFlags = [
  "url_original"  # Keep old URL behavior while fixing code
]
Fix your code, then remove the flag:
compatibilityDate = "2024-01-15"
# Flag removed - now using new behavior

Option 2: Opt in to new behavior early

compatibilityDate = "2022-06-01",
compatibilityFlags = [
  "url_standard"  # Opt in before the enable date
]
Test the new behavior before updating your date.

Experimental flags

Some flags are marked experimental:
workerdExperimental @24 :Bool
    $compatEnableFlag("experimental")
    $experimental;
Experimental flags:
  • Are subject to change or removal
  • Are not covered by compatibility guarantees
  • Require the --experimental CLI flag
  • Should not be used in production
Experimental features may change or be removed without warning. Use them only for testing and development.

Best practices

Keep compatibility dates current

Regularly update your compatibility dates to get bug fixes and improvements:
# Review and update quarterly
compatibilityDate = "2024-01-15"

Document your compatibility date

Include a comment explaining why you chose this date:
const myWorker :Workerd.Worker = (
  serviceWorkerScript = embed "worker.js",
  # Set to 2024-01-15 during initial development
  # Last reviewed: 2024-01-15
  compatibilityDate = "2024-01-15"
);

Test before updating

Always test compatibility date changes before deploying to production:
  1. Update date in development
  2. Run automated tests
  3. Perform manual testing
  4. Deploy to staging
  5. Monitor staging carefully
  6. Deploy to production

Use feature detection

When possible, use feature detection rather than depending on specific dates:
export default {
  async fetch(request) {
    // Good: Feature detection
    if ('getSetCookie' in request.headers) {
      const cookies = request.headers.getSetCookie();
    }
    
    // Avoid: Assuming feature based on date
    const cookies = request.headers.getSetCookie();  // Might not exist
  }
}

Compatibility dates and deployments

Different deployments can use different compatibility dates:
# Development: use latest features
const devWorker :Workerd.Worker = (
  inherit = "baseWorker",
  compatibilityDate = "2024-01-15"
);

# Production: use stable date
const prodWorker :Workerd.Worker = (
  inherit = "baseWorker",
  compatibilityDate = "2023-06-01"
);
This allows testing new behavior in development before enabling it in production.

Build docs developers (and LLMs) love