Change management and rollback

How a change reaches production and how fast it can be undone: commit-anchored immutable deploys, per-resource event streams, the partial-success state to watch for, branch-based promotion, and a complete audit trail.

The Spikefrost Team30 Jul 20264 min read

Change is the most common cause of incidents on any platform, which makes change management part of an availability story rather than a separate topic.

A deploy, step by step

Every deploy is commit-anchored: it builds exactly one immutable snapshot of the tree, so the same commit always produces the same deploy.

One deploy. Migrations run before the worker is swapped, and the version pointer flips last — so a failure earlier in the sequence leaves the previous version serving.CacheEdgeD1BuildVersion controlYou / an agent~60s for every locationto serve the new versioncommit1sf deploy2prebuild + typecheck +platform checks3bundle worker4apply new migrations (perenvironment)5recorded in _migrations6upload worker + attach bindings7reconcile declared config<small>agents · routines · queues</small>8invalidate the app's edge cache9version pointer flipped10
One deploy. Migrations run before the worker is swapped, and the version pointer flips last — so a failure earlier in the sequence leaves the previous version serving.

Two ordering properties are worth knowing: migrations run before the worker is swapped, so schema is in place for the code that needs it, and the version pointer flips last, so a failure earlier in the sequence leaves the previous version serving rather than a half-deployed one.

Deployment states

Deployment outcomes. PARTIAL is the state that looks like success — the site is live, but some declared configuration did not apply.

everything applied

stopped at a named resource

a newer deploy took over

worker live,
config reconcile failed

fix and redeploy

fix the named file,
redeploy

IN_PROGRESS

COMPLETE

FAILED

SUPERSEDED

PARTIAL

Deployment outcomes. PARTIAL is the state that looks like success — the site is live, but some declared configuration did not apply.
sf deployments                          # newest first: status, environment, commit, trigger
sf deployments logs <deploymentId> --follow   # per-resource event stream

The event stream is per-resource — build, bundle, each migration, worker, secrets, and each agent or routine reconciliation — so a failure names the exact resource and reason. Read it before retrying; most failed deploys fail for a reason a retry won't change.

PARTIAL deserves an alert rather than a glance. Your app is live and serving the new code, and an agent, routine, or connector change silently did not apply. From a browser it is indistinguishable from success.

Promotion

Branches map to environments, and promotion is a merge:

Promotion is a merge into the branch mapped to the target environment — never a direct deploy of a side branch, which would leave production running code no branch describes.maindevbaselinefeature Afix Apromote → productionfeature B
Promotion is a merge into the branch mapped to the target environment — never a direct deploy of a side branch, which would leave production running code no branch describes.
sf vcs autodeploy main=production dev=dev   # attach branches to environments
sf vcs checkout main && sf vcs merge dev    # promote

With a mapped branch, every commit that advances it deploys that environment. Convenient and sharp — warn people, including agents, before committing untested work to a mapped branch.

One rule with a real consequence: never deploy a side branch directly to production. If you do, production runs code that isn't reachable from the branch describing it, and the next main deploy silently reverts it.

Rollback

What you want Command Effect
Stop the bleeding on one environment sf deploy --env production --commit @1 Pins that environment to an earlier commit. Branch untouched
Revert the code itself sf vcs rollback @3 --confirm Creates a new commit restoring the older tree — itself reversible
Undo a data change sf d1 restore --minutes-ago 30 --yes Point-in-time recovery, 30-day window

Pinning an environment is usually the right first move during an incident: it's fast, it's reversible, and it doesn't require deciding what the correct code is while under pressure.

There is no automatic rollback on a failed deploy. That's deliberate — an automatic revert during a partially applied migration can do more damage than the failure. Deploys are retry-forward.

The audit trail

Three independent records, none of which your team maintains by hand:

Record Contains Retention
Commits Every code change, attributed, including every AI turn Full history
Deployment records Every attempt: who, which commit, which environment, per-resource events, outcome Full history
File versions Every version of every file Full history
sf vcs log                     # who changed what, when
sf vcs show <commit>           # one commit's metadata and files
sf deployments --env production
sf files versions <path>

The property auditors care about: there is no path by which a change reaches a deployed app without a commit and a deployment record. An AI agent editing the app produces the same trail as a human — attributed, diffable, and revertible.

Practices worth adopting

  • Map a non-production branch and test there. Real infrastructure, real migrations, empty data — see Environments.
  • Deploy with a message. sf deploy -m "what changed" keeps history readable; deploying with uncommitted changes auto-commits under a mechanical message.
  • Check sf deployments after any automated deploy, especially for PARTIAL.
  • Don't diagnose inside the first minute. For up to ~60 s some locations still serve the previous version. Compare x-sf-version against the id the deploy printed before concluding anything.
  • Rehearse a rollback in staging so the command isn't new to you during an incident.

Next

Frequently asked questions

How long does a rollback take?

About a minute. Deploying an earlier commit to an environment is an ordinary deploy — no ticket, no escalation, and it doesn't touch the branch, so you can stop the bleeding before diagnosing.

Is every change auditable?

Yes. Every AI turn, editor save, and local commit lands as an attributed commit, and every deploy attempt produces a record with a per-resource event stream. There is no path by which code reaches production without both.

What is a PARTIAL deployment?

The worker deployed and your site is live, but reconciling declared configuration failed — so part of your intended change did not apply. It looks like success in a browser, which is exactly why it needs an alert rather than a glance.

How do changes get promoted between environments?

By merging into the branch mapped to the target environment. Deploying a side branch straight to production is possible and is a mistake, because production then runs code the branch doesn't describe.

Can a deploy be rolled back automatically on failure?

No — deploys are retry-forward by design. A failed deploy names the resource that failed; you fix it and deploy again, or pin the environment to an earlier commit deliberately.