The junior pastes a terminal. npx wrangler deploy exits 0. Chat is quiet. They file an all-cutover: “Durable Object code is live.”
I stop the run there. A green wrangler deploy is not an immediate Durable Object code cutover. The public Workers SDK notes that named this field are blunt: Durable Objects now have code update strategies on wrangler deploy, wrangler versions deploy, and wrangler rollback. When the strategy is unset, it defaults to deferred with a 5-minute maximum delay. Delays cannot exceed 24 hours. That is a Worker bundle that uploaded. It is not every live object already running the new class. [Source: https://github.com/cloudflare/workers-sdk/releases/tag/wrangler%404.141.0] [Source: https://releasebot.io/updates/cloudflare/workers-sdk]
I already refused to treat a /readyz 200 through a Postgres blip as a healthy store in A /readyz 200 Through a Postgres Blip Is Not a Healthy Store. I already refused to treat a timed-out job as a killed worker in A Timed-Out Job Is Not a Killed Worker. I already refused to treat every-turn HTTP 400 as a dead gateway in Every-Turn HTTP 400 on ANTHROPIC_BASE_URL Is Not a Dead Gateway. This post is the same desk rule for a green deploy. Print deploy. Print the strategy. Name who owns the Workers cutover.
The question is not whether the CLI looks green. The question is whether the named owner can still tell “Worker version uploaded” from “Durable Object instances left the old class.”

The ticket that looks like all-cutover
Juniors treat wrangler deploy the way they treat a green LED on a rack. The command returned. The dashboard shows a new version. They page nobody. They tell chat every Durable Object is on the new code.
Two jobs collide on that command.
- Upload the Worker. Official Cloudflare docs: you deploy a Worker version with Wrangler. A successful deploy means the new Worker version exists. It does not mean every globally unique Durable Object already restarted onto that version. [Source: https://developers.cloudflare.com/workers/versions-and-deployments/gradual-deployments/]
- Move Durable Object code. Official Durable Objects known-issues docs: only one version of each Durable Object can run at a time. Code changes for Workers and Durable Objects are released globally in an eventually consistent manner. A request can arrive at the latest Worker and still call a unique Durable Object running the previous class for a short period, typically seconds to minutes. [Source: https://developers.cloudflare.com/durable-objects/platform/known-issues/]
If you only screenshot “Uploaded” you will file all-cutover. You will not file the objects that are still awake on the old class.
I do not invent a fake overnight outage. I use the public contract. The update-strategy field is the ticket, not a version pin in the title.
What wrangler deploy actually is
Official gradual-deployment docs: you upload a version, then you create a deployment that may split traffic. wrangler versions upload creates a version that is not automatically deployed. wrangler versions deploy creates a deployment. wrangler deploy is the older one-shot path many desks still run in CI. All three are Worker version jobs. [Source: https://developers.cloudflare.com/workers/versions-and-deployments/gradual-deployments/]
Durable Objects sit on a different clock. Official known-issues page, under Global uniqueness: uniqueness is enforced when a new event starts and when storage is accessed. A Durable Object may be replaced on a software update, including an update of the Durable Object class code. Official Code updates section: because each Durable Object is globally unique, the latest Worker can call a previous-class object for seconds to minutes. Best practice is forward and backward compatible APIs across that window. [Source: https://developers.cloudflare.com/durable-objects/platform/known-issues/]
The 25 September 2026 Workers SDK release named the missing knob. GitHub tag [email protected] (published_at 2026-09-25T19:33:53Z, prerelease false) adds Durable Objects code update strategies to Worker deployments. Use --durable-objects-code-update-mode immediate on wrangler deploy, wrangler versions deploy, and wrangler rollback to update code without waiting for active instances to hibernate. Use --durable-objects-code-update-mode deferred 30s to set a maximum delay, or configure durable_objects.code_update_strategy with mode and max_delay. When unset, the strategy defaults to deferred with a 5-minute maximum delay. Delays cannot exceed 24 hours and must use millisecond precision. [Source: https://github.com/cloudflare/workers-sdk/releases/tag/wrangler%404.141.0] [Source: https://releasebot.io/updates/cloudflare/workers-sdk]
That paragraph is evidence. It is not a how-to. This post does not tell you to pass immediate. This post tells you to print which mode you actually ran.
Pins are evidence, not the hook
[email protected] on 25 September 2026. The npm tarball [email protected] exists at https://registry.npmjs.org/wrangler/4.141.0. This morning’s https://registry.npmjs.org/wrangler/latest still answered version: 4.140.0. Print GitHub tag, npm tarball, and dist-tags.latest as three columns. Do not treat npx wrangler@latest as the GitHub tag. Do not put those numbers in the title. This post is the deploy field versus the Durable Object field, not a patch table. [Source: https://github.com/cloudflare/workers-sdk/releases/tag/wrangler%404.141.0] [Source: https://registry.npmjs.org/wrangler/latest] [Source: https://registry.npmjs.org/wrangler/4.141.0]
Three columns on the ticket
I keep one table on the ticket.
| What you saw | What it is | What it is not |
|---|---|---|
wrangler deploy exit 0 | Worker version uploaded | Proof every Durable Object left the old class |
Unset code_update_strategy | Public default: deferred, 5-minute maximum delay | Immediate cutover |
--durable-objects-code-update-mode deferred 30s | A named maximum delay | Proof instances already restarted |
--durable-objects-code-update-mode immediate | A named owner decision to skip the wait for hibernation | A coding-agent default |
| Latest Worker calling an old-class object | The documented seconds-to-minutes window | A broken binding |
| Gradual 20 / 80 split | Each object pinned to one version for that deployment | Permission to mix class APIs |
[Source: https://github.com/cloudflare/workers-sdk/releases/tag/wrangler%404.141.0] [Source: https://developers.cloudflare.com/durable-objects/platform/known-issues/] [Source: https://developers.cloudflare.com/workers/versions-and-deployments/gradual-deployments/with-durable-objects/]
The junior screenshot is the first row. They want the third column. Print the second column first.
Name the owner. I use WORKERS_CUTOVER_OWNER the same way I use a migration owner. The person who owns Wrangler on this desk also owns “did we deploy deferred, immediate, or unset.” A coding agent does not get to file all-cutover because the CLI was green.
Do not heading-clone the compound-glob post. That post is one matching glob versus the rest of the line. This post is one deploy versus one object clock. Do not heading-clone a wrangler-never-returns post. That post is a hung CLI. This post is a finished CLI that still left objects on a delay.
Probe the two clocks, do not write a force-immediate recipe
I keep a probe in the repo the coding agent uses, not in a gist on a laptop. It does not pass --durable-objects-code-update-mode immediate. It does not edit wrangler.jsonc for you. It classifies the config that already exists.
1#!/usr/bin/env python3
2"""Classify wrangler Durable Object code-update strategy. Do not flip the mode."""
3from __future__ import annotations
4
5import json
6import os
7import re
8import sys
9from pathlib import Path
10
11OWNER = os.environ.get("WORKERS_CUTOVER_OWNER", "").strip()
12CONFIG = Path(os.environ.get("WRANGLER_CONFIG", "wrangler.jsonc"))
13OUT = Path("do-cutover-ticket.json")
14
15DEFAULT_MODE = "deferred"
16DEFAULT_MAX_DELAY = "5m"
17MAX_ALLOWED = "24h"
18
19
20def strip_jsonc(text: str) -> str:
21 text = re.sub(r"/\*.*?\*/", "", text, flags=re.S)
22 text = re.sub(r"^\s*//.*$", "", text, flags=re.M)
23 return text
24
25
26def load_config(path: Path) -> dict:
27 if not path.is_file():
28 return {}
29 raw = strip_jsonc(path.read_text(encoding="utf-8"))
30 return json.loads(raw)
31
32
33def classify(cfg: dict) -> dict:
34 do = cfg.get("durable_objects") or {}
35 strategy = do.get("code_update_strategy")
36 if not isinstance(strategy, dict) or not strategy:
37 return {
38 "mode": DEFAULT_MODE,
39 "max_delay": DEFAULT_MAX_DELAY,
40 "source": "unset_default",
41 "verdict": "GREEN_DEPLOY_IS_NOT_IMMEDIATE_DO_CUTOVER",
42 }
43 mode = str(strategy.get("mode") or DEFAULT_MODE)
44 max_delay = str(strategy.get("max_delay") or DEFAULT_MAX_DELAY)
45 if mode == "immediate":
46 verdict = "IMMEDIATE_NAMED_ON_TICKET"
47 else:
48 verdict = "GREEN_DEPLOY_IS_NOT_IMMEDIATE_DO_CUTOVER"
49 return {
50 "mode": mode,
51 "max_delay": max_delay,
52 "source": "config",
53 "verdict": verdict,
54 "max_delay_cannot_exceed": MAX_ALLOWED,
55 }
56
57
58def main() -> int:
59 if not OWNER:
60 print("MISSING_WORKERS_CUTOVER_OWNER", file=sys.stderr)
61 return 2
62 result = classify(load_config(CONFIG))
63 result["owner"] = OWNER
64 result["config"] = str(CONFIG)
65 OUT.write_text(json.dumps(result, indent=2) + "\n", encoding="utf-8")
66 print(result["verdict"])
67 return 0
68
69
70if __name__ == "__main__":
71 raise SystemExit(main())
The script does not call Wrangler. It does not call the Cloudflare API. VERDICT=GREEN_DEPLOY_IS_NOT_IMMEDIATE_DO_CUTOVER means you do not have an all-cutover. You have a Worker upload whose Durable Object code still sits on the public deferred default, or on an explicit delay. Then open the Workers ticket. Put those four lines on it: owner, deploy result, mode, max delay.
A second probe is the config field the release named. Official release text: configure durable_objects.code_update_strategy with mode and max_delay. Copy the shape as evidence of the field, not as a hide-the-wait recipe.
1{
2 "name": "session-worker",
3 "main": "src/index.ts",
4 "compatibility_date": "2026-09-25",
5 "durable_objects": {
6 "bindings": [
7 {
8 "name": "SESSION",
9 "class_name": "Session"
10 }
11 ],
12 "code_update_strategy": {
13 "mode": "deferred",
14 "max_delay": "5m"
15 }
16 }
17}
[Source: https://github.com/cloudflare/workers-sdk/releases/tag/wrangler%404.141.0]
That file names the delay. It does not name an instance that already restarted. If the junior pastes it and files “Durable Object code is live,” they mixed the two jobs the same way they mix /readyz with the store.
Print the ticket from the probe file. Empty owner is a fail, not a skip.
1export WORKERS_CUTOVER_OWNER="${WORKERS_CUTOVER_OWNER:-}"
2export WRANGLER_CONFIG="${WRANGLER_CONFIG:-wrangler.jsonc}"
3python3 scripts/probe_do_cutover.py
4python3 -c "import json,pathlib; print(json.loads(pathlib.Path('do-cutover-ticket.json').read_text())['verdict'])"

Gradual deployments are not this ticket either
Official docs: gradual deployments work differently for Durable Objects because only one version of each Durable Object can run at a time. When you create a new gradual deployment, each Durable Object is assigned a Worker version based on the percentages you configured. That version does not change until you create a new deployment. For a given deployment, requests to each Durable Object always use the same Worker version. The object is reset when it is assigned a different version. [Source: https://developers.cloudflare.com/workers/versions-and-deployments/gradual-deployments/with-durable-objects/]
A 20 percent / 80 percent split is not “all objects on new code.” It is “some objects pinned to B, some still pinned to A, until you create another deployment.” Official docs also: Durable Object class lifecycle changes (create, delete, rename, transfer) cannot be uploaded as gradual versions. Those changes are atomic and go through npx wrangler deploy. Lifecycle is a third ticket. Do not mix it with a code-update delay. [Source: https://developers.cloudflare.com/workers/versions-and-deployments/gradual-deployments/with-durable-objects/]
If chat was quiet while 20 percent of objects sat on B, file “partial assignment.” Do not file all-cutover.
Compatibility across the window is the real work
Official known-issues, Code updates: it is best practice to ensure that API changes between your Workers and Durable Objects are forward and backward compatible across code updates. The reason is the window itself. The latest Worker can call a previous-class object for seconds to minutes even without a gradual split. [Source: https://developers.cloudflare.com/durable-objects/platform/known-issues/]
That is the Monday job. Keep the old RPC method until the named owner prints that objects left the old class. Add the new method beside it. Do not delete the old argument shape in the same deploy that also changes storage layout. Lifecycle migrations stay on their own wrangler deploy because official docs say they are atomic and cannot roll back to a version before the migration. [Source: https://developers.cloudflare.com/workers/versions-and-deployments/gradual-deployments/with-durable-objects/]
A coding agent that “just bumps the class” in the same PR as a deleted method is writing the outage. The green CLI will still be green.
What you must not do
Forbidden:
- File a “Durable Object code is live” ticket without printing the deploy result, the code-update mode or unset default, the max delay, and one human name on the Workers cutover.
- Put a Wrangler version in the title or the first line. The pin is evidence after the decision.
- Mix this field with a
/readyzstore ticket, a timed-out job ticket, a compound-glob ticket, or a wrangler-never-returns ticket. Those are other posts. - Write a force-immediate how-to, a recommended
max_delay, or a “pass this flag in CI” runbook. The public notes already state the default is deferred with a 5-minute maximum. You do not need a live skip-the-wait recipe. [Source: https://github.com/cloudflare/workers-sdk/releases/tag/wrangler%404.141.0] - Treat
npx wrangler@latestas GitHub tag[email protected]. This morningregistry.npmjs.org/wrangler/lateststill answered 4.140.0 while the 4.141.0 tarball existed. Print both. [Source: https://registry.npmjs.org/wrangler/latest] [Source: https://registry.npmjs.org/wrangler/4.141.0] - Recommend buying Workers, a plan, or a seat because one deploy stayed green.
- Treat a gradual 20 percent assignment as all objects on the new class. Official docs: each object is assigned one version for that deployment. [Source: https://developers.cloudflare.com/workers/versions-and-deployments/gradual-deployments/with-durable-objects/]
- Treat “Worker latest” as “Durable Object latest.” Official docs: the latest Worker can call a previous-class object for seconds to minutes. [Source: https://developers.cloudflare.com/durable-objects/platform/known-issues/]
- Mix class lifecycle (create, delete, rename, transfer) with a code-update delay. Official docs: lifecycle changes are atomic and are not gradual. [Source: https://developers.cloudflare.com/workers/versions-and-deployments/gradual-deployments/with-durable-objects/]
- Paste a ready-check, permission-NUL, or queue-timeout fix because those families shipped this week. Those are different fields.
Allowed:
- Print
wrangler deploy(orversions deploy) exit code from the job that actually ran. - Print
durable_objects.code_update_strategyor writeunset_defaultwith the public 5-minute deferred maximum. - Name one human as
WORKERS_CUTOVER_OWNER. - Keep Worker and Durable Object APIs compatible across the documented window.
- Print
npx wrangler --versionandnpm view wrangler dist-tags.latestafter the decision, in a details block, not in the title. - File “Worker uploaded, Durable Object still deferred” as the ticket title when those two columns disagree.
GSC this week still has no striking-distance query on the /readyz post, the timed-out-job post, or the HTTP 400 post. I am not refreshing those URLs. This is a new field, not a synonym of Saturday’s ready bit or Thursday’s queue clock.
If you need the broader habit, start at /ai-agent-operations/. Tooling notes live under /developer-tools/. Laravel plus Vue notes live under /laravel-vue-saas/. A first-week map is at /start-here/.
A changelog bullet about a deferred Durable Object delay is not permission to skip the instance clock.
What you should do Monday morning
- Open the repo that actually ships. Export
WORKERS_CUTOVER_OWNERto a human name. Runprobe_do_cutover.pyagainst the Wrangler config CI uses. Write the verdict on the ticket next to that name. - Print the last CI deploy log. Write exit code, command (
deployvsversions deployvsrollback), and whether anyone passed--durable-objects-code-update-mode. If the flag is absent, writeunset_default. [Source: https://github.com/cloudflare/workers-sdk/releases/tag/wrangler%404.141.0] - If chat was quiet after a green deploy, file “Worker uploaded.” Decide whether Durable Object instances were still inside the deferred window. Official language: the latest Worker can call a previous-class object for seconds to minutes. [Source: https://developers.cloudflare.com/durable-objects/platform/known-issues/]
- If someone pastes only a green CLI, reject the review until the named owner can show four lines: owner, deploy result, mode, max delay.
- Print
npx wrangler --versionandnpm view wrangler version dist-tags --json. If the laptop is behind the 25 September 2026 GitHub tag that named the strategy field, do not treat the local pin as those fields. Compare GitHub tag, npm tarball, andlatest, then decide an upgrade as change control, not as a social post. [Source: https://github.com/cloudflare/workers-sdk/releases/tag/wrangler%404.141.0] [Source: https://registry.npmjs.org/wrangler/latest] - Confirm coding-agent instructions on this desk name the same owner and forbid “Durable Object code is live” without the four lines: owner, deploy result, mode, max delay.
The question is not whether a green CLI demos well in a gist. The question is whether the named owner can still tell a Worker upload from Durable Object instances that left the old class after handoff.
Further reading
Source GitHub — [email protected] release notes
Source Cloudflare docs — Durable Objects known issues (global uniqueness and code updates)
Source Cloudflare docs — gradual deployments with Durable Objects
