WEBDEVMASTERS
Release without ambiguity

Deploy one identifiable artifact

Tie the promoted commit to one immutable artifact, switch atomically, verify runtime identity, and keep rollback independent from guesswork.

A deployment is complete only when the running release can prove it is the artifact that was promoted.

Make release identity a first-class invariant

A release manifest identifies the promoted commit, build time, runtime version, and hashes for important assets. The release process uploads one immutable artifact, validates its structure, and switches the active directory atomically. A smoke test then compares the reported release identity with the promoted commit. If the identifiers differ, the deployment remains incomplete even when the health endpoint returns HTTP 200.

This invariant prevents a common operational ambiguity: source control says one commit, the pipeline says another job succeeded, and production serves files from an earlier manual upload. A status endpoint should return a nonsecret release identifier generated during the build, not inferred from the current checkout after deployment.

{
  "commit": "4f8d2a1c",
  "builtAt": "2026-07-23T05:40:00Z",
  "runtime": "node-24",
  "assets": {
    "app.css": "sha256-7b2e…",
    "app.js": "sha256-a91c…"
  }
}

Build once and promote the same bytes

An artifact is the deployable output plus the files required to run and identify it. Build it in a controlled environment, run tests against it, calculate its digest, and store it under a release identifier. Staging and production should receive that same artifact; rebuilding after approval weakens the evidence because dependency resolution, tool versions, or environment inputs may change the bytes.

EntityAttributeRequired valueVerification
CommitIdentityFull source SHAManifest and pipeline input agree
ArtifactImmutabilityContent-addressed or locked releaseDigest unchanged through promotion
ReleaseConfigurationEnvironment-specific, external to bytesRequired keys and safe defaults validated
RuntimeReported identityPromoted commit and release IDHealth response matches manifest
RollbackTargetLast verified compatible artifactSwitch rehearsal or documented command

Do not place secrets in the artifact or manifest. Validate expected directories, entry points, file permissions, and migration metadata before promotion. A malformed artifact should fail before it can become active.

Switch the active release atomically

Copying files directly into the live directory can create a mixed release: new HTML references JavaScript that has not arrived, or a worker restarts while only half the files are present. Instead, unpack into a versioned release directory, validate it, then update one symlink or platform release pointer atomically. Keep the prior directory intact until verification completes.

releases/
  20260723-4f8d2a1c/
  20260722-91be7740/
current -> releases/20260723-4f8d2a1c/

# Promotion changes only the current pointer after validation.
ln -sfn "$next_release" current.next
mv -Tf current.next current

The exact command varies by platform. The invariant does not: visitors should see either the old complete release or the new complete release, never a partially copied combination. Long-running processes must restart or reload against the new pointer in a controlled order.

Separate code rollback from data compatibility

Database changes can make application rollback unsafe. Prefer expand-and-contract migrations: first add compatible schema, deploy code that can use old and new forms, backfill or verify data, switch reads and writes, then remove the old form in a later release. A destructive migration should have a backup, tested recovery method, and explicit stop conditions.

Run migrations once under a lock, not independently in every application replica. Record the migration version in release evidence. If the new code fails after a backward-compatible expansion, the previous artifact can often run safely. If compatibility was broken, “roll back the code” may compound the incident.

Verify internals and the public path

A successful process start is not a complete smoke test. Check the public URL through the same proxy and caching layers visitors use. Confirm the expected status, title or content marker, critical assets, and the release identity. Exercise one read path and one safe critical interaction when applicable. Compare asset URLs to the manifest and ensure they return the expected content type.

expected_commit="4f8d2a1c"
reported_commit="$(curl -fsS https://example.com/health | jq -r .commit)"
test "$reported_commit" = "$expected_commit"

curl -fsS https://example.com/ | grep -F "Expected release marker"
curl -fsSI https://example.com/assets/app.a91c.js | grep -i "200"

The caching guide explains why versioned asset URLs and revalidated HTML avoid old-new mixtures. Observe error rate, latency, and business-critical signals during a bounded post-release window.

Keep rollback boring and preselected

Go/no-go framework

  1. Before: identify the artifact, migration risk, previous compatible release, and stop thresholds.
  2. Promote: switch atomically and record the release event.
  3. Verify: compare runtime identity, public content, assets, and critical behavior.
  4. Observe: watch error, latency, and task success against the baseline.
  5. Decide: continue only if evidence stays inside thresholds; otherwise switch to the preselected release and investigate offline.

Rollback should not rebuild. It should reactivate a known artifact with compatible configuration and data. After recovery, preserve logs and the failed release for diagnosis. The goal of reliable deployment is not zero failure; it is a short, observable path from change to proof—and from bad evidence back to a known state.

Sources and further reading

These references support the standards and factual claims in this guide. The decision frameworks and worked examples are original editorial synthesis.

  1. GitHub Docs: Managing deployment environments
  2. GitHub Docs: Artifact attestations
  3. MDN: HTTP caching
  4. The Twelve-Factor App: Build, release, run

Questions practitioners ask

Is HTTP 200 enough for a health check?

No. It proves that an endpoint answered. A completion check should also confirm critical dependencies as appropriate and expose a release identity that matches the promoted commit.

Should production build source code again?

Prefer promoting the same tested immutable artifact. Rebuilding in production can create different bytes from the artifact tested in staging.

Can every database migration be rolled back automatically?

No. Destructive data changes may be irreversible or unsafe to reverse. Use expand-and-contract changes, backups, compatibility windows, and an explicit recovery plan.