Code updates
When you deploy, new requests to your Worker run the new code within seconds. A Durable Object that is already running is different: it keeps running the code it started with until it is safe to switch. Knowing when that happens tells you how to write code that survives a deploy and tests that see what you just shipped.
A Worker starts fresh on every deploy
Section titled “A Worker starts fresh on every deploy”Each deploy runs in new isolates. The Construction phase runs again, and anything the old isolate held in memory is gone:
export default Cloudflare.Worker( "Worker", { main: import.meta.url }, Effect.gen(function* () { // Runs once per isolate, so again after every deploy const counters = yield* Counter;
return { fetch: Effect.gen(function* () { return HttpServerResponse.text("Hello from my Worker!"); }), }; }),);The new version can take a few seconds to reach every location, so retry the first request after a deploy. See Retry the first request.
A Durable Object keeps the code it started with
Section titled “A Durable Object keeps the code it started with”Each object runs one copy of your code, the inner Effect, from the moment it starts:
export default class Counter extends Cloudflare.DurableObject<Counter>()( "Counter", Effect.gen(function* () { const state = yield* Cloudflare.DurableObjectState;
return Effect.gen(function* () { // Runs each time the object starts let count = (yield* state.storage.get<number>("count")) ?? 0;
return { increment: () => Effect.gen(function* () { count += 1; yield* state.storage.put("count", count); return count; }), }; }); }),) {}An object stops when it goes idle. It hibernates after about 10
seconds without work, or is evicted after 70 to 140 seconds if
something keeps it from hibernating, such as a pending setTimeout
or an open outbound connection. Its next request starts it again on
the latest code. count is read back from storage, so it survives.
Anything kept only in memory does not.
Deploys wait for objects to go idle
Section titled “Deploys wait for objects to go idle”By default an object that is busy during a deploy keeps running its current code for up to 5 minutes while it waits to hibernate. Then it resets onto the new code:
{ main: import.meta.url, // the default codeUpdateStrategy: { mode: "deferred", maxDelay: "5 minutes" },}Requests in progress get time to finish, and hibernatable WebSockets stay connected when the object picks up the new code after hibernating.
Choose how long to wait
Section titled “Choose how long to wait”Set maxDelay to anything from 0 to 24 hours:
{ main: import.meta.url, codeUpdateStrategy: { mode: "deferred", maxDelay: "1 minute" },}Waiting is best effort. An object can still reset sooner, for example when Cloudflare updates the Workers runtime.
Switch objects to new code right away
Section titled “Switch objects to new code right away”Use immediate when every object must run the new code as soon as
the deploy finishes, such as when you ship a fix during an incident:
{ main: import.meta.url, codeUpdateStrategy: { mode: "immediate" },}Every running object resets. A request it was handling fails with
Durable Object reset because its code was updated. Data in
state.storage is kept; in-memory values are lost.
The strategy applies to each deploy
Section titled “The strategy applies to each deploy”The strategy is not a setting stored on the Worker. It is sent with
each deploy. Changing only codeUpdateStrategy does not deploy
anything, because a redeploy would reset the objects it is meant to
protect. Your next deploy uses the new value.
Old and new code run side by side
Section titled “Old and new code run side by side”While an object waits, a Worker running the new code can call an object still running the old code. Calling a method the old code doesn’t have fails:
const counter = counters.getByName(name);const next = yield* counter.increment();const next = yield* counter.incrementBy(5); // missing on objects still running the old codeShip the new method in one deploy and start calling it in the next,
once maxDelay has passed. Keep method arguments and return values
readable by both versions in the meantime.
Write state as you go
Section titled “Write state as you go”An object can reset at any time: on deploy, when maxDelay runs out,
or during a runtime update. Save each change to storage when it
happens, as increment does, rather than when the object stops. Make
operations safe to retry, so a caller can repeat a request that
failed during a reset.
Gradual deployments assign objects to versions
Section titled “Gradual deployments assign objects to versions”During a gradual deployment,
Cloudflare assigns each object to one version. Raising the new
version’s traffic moves more objects onto it. Only objects that move
wait for maxDelay; the rest keep running.
Tests can see the old code
Section titled “Tests can see the old code”A test that deploys and then calls a Durable Object can reach an object an earlier deploy started. If that object stayed busy, it can answer with the old code for up to 5 minutes.
Deploy your test stages with immediate, so every object runs the
code the test just deployed:
import * as Cloudflare from "@alchemy.run/cloudflare";import { Stack } from "alchemy/Stack";import * as Effect from "effect/Effect";import * as HttpServerResponse from "effect/http/HttpServerResponse";import Counter from "./counter.ts";
export default Cloudflare.Worker( "Worker", Stack.useSync(({ stage }) => ({ main: import.meta.url, codeUpdateStrategy: stage === "prod" ? undefined : { mode: "immediate" }, })), Effect.gen(function* () { const counters = yield* Counter;
return { fetch: Effect.gen(function* () { return HttpServerResponse.text("Hello from my Worker!"); }), }; }),);Or give each test its own object. A new object always starts on the latest code:
test( "increment starts at 1", Effect.gen(function* () { const { url } = yield* stack; const name = crypto.randomUUID();
const response = yield* HttpClient.post(`${url}/counter/${name}`); expect(yield* response.text).toBe("1"); }),);Where next
Section titled “Where next”- Durable Objects — define an object, persist its state, and call it from a Worker.
- Accept WebSockets — connections that stay open through hibernation and code updates.
- Gradual deployments — roll a deploy out to a share of traffic.
- Testing a Stack — deploy once per suite and drive the live URL.
- Cloudflare’s code updates docs
— the platform feature, including Wrangler’s
--durable-objects-code-update-modeflag. - Worker API reference —
every prop, including
codeUpdateStrategy.