The answer. A production Worker deploy needs two guards, each costing about one API call: know how to get back before you change anything, and check that what you shipped actually answers. Read the version id currently serving traffic with wrangler deployments list, and refuse to continue if you can't. Deploy. Poll /health a few times with a short delay — a new version takes a moment to route. If it never comes back ok, run wrangler rollback <previous-id> from the same script and probe again. Everything else is optional; these two are not.
The pattern.
// scripts/deploy-prod.mjs — wrangler via execFile
const NAME = "my-api", HEALTH = "https://api.example.com/health";
const list = await wrangler(["deployments", "list", "--name", NAME]);
// printed oldest → newest; the last 100% version is the one serving traffic
const live = [...list.matchAll(/\(100%\)\s*([0-9a-f-]{36})/g)].at(-1)?.[1];
if (!live) throw new Error("cannot read live version id; refusing to deploy without a rollback target");
const out = await wrangler(["deploy", "--env", "prod"]); // never --var here
const next = /Current Version ID:\s*([0-9a-f-]{36})/i.exec(out)?.[1];
if (next === live) throw new Error("same version id as before; the upload did not take effect");
if (await probe(HEALTH, { attempts: 6, delayMs: 5000 })) {
console.log(`ok — undo with: wrangler rollback ${live} --name ${NAME} -y`);
} else {
await wrangler(["rollback", live, "--name", NAME, "-y", "-m", "auto-rollback"]);
if (await probe(HEALTH)) throw new Error("rolled back; prod is healthy on the previous version");
throw new Error("rollback did not restore health — manual attention now");
}
The receipt. In our API Worker, 2026-07-27: the previous production path was a ~10,000-line release apparatus that could not deploy to production at all — its promotion lane had only ever been implemented for a staging Worker and threw by name when aimed at prod. It was replaced by a ~220-line script doing only the two checks above; the probe is 6 attempts × 5 s. It never passes --var: the prod vars map holds about 40 runtime settings (kill switches, thresholds, origins), and a replace-not-merge would silently wipe them, so success is verified by version id instead. One guard was added on 2026-08-25, after a deploy from a stale pre-launch branch shipped a platform-status map that reverted two launched integrations: a source-file check that runs before the rollback-target read.
Watch out.
wrangler deployments listprints oldest first; take the last 100% version.- Scripted requests to a Worker can get bot-challenged (error 1010). Send a browser User-Agent from the probe.
- A dirty worktree should warn, never block. A build that would revert a launched feature should block, before any mutation.
Related: production-canary-version-pinned-cutover · cron-gate-outage-kill-switch-returned-early