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.
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.
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
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:
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 deploymentsafter any automated deploy, especially forPARTIAL. - Don't diagnose inside the first minute. For up to ~60 s some locations still serve the previous version. Compare
x-sf-versionagainst 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
- Observability and audit — noticing a bad change quickly.
- Failure domains and resilience — the fastest mitigations.
- Build and deploy — the hands-on guide.
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.