Production Is a Pointer

Yunshu 4 min read

I worked on the delivery side of this system, and tracing the release path made one distinction clear: production is not a second development history. It is a pointer to the exact tree that is serving users. Once the release path treated it that way, promotion became a small, explicit operation: choose a ref, resolve it to a commit, and point prod at that commit.

That is the idea behind the site’s current release path. main is where changes land and where Cloudflare Pages serves beta. prod is the only branch that serves the apex, shukelabs.com. Beta can move automatically; production moves when someone deliberately dispatches the release workflow. The two branches have different jobs, so they should not pretend to have the same history.

The awkward old path

Before the workflow existed, promoting production was a local git action: push main to prod. It worked, but it made a production release depend on a particular checkout and a person remembering the right command. It also expressed the wrong thing.

The command looked like a merge or a branch update. The release needed to express something else: publish one known tree. If a release needed to come from a tag, or if production needed to return to an older commit, the local command had no useful vocabulary for that. There was also no Actions run recording which ref had been promoted, when it happened, or who did it.

The problem was not that git was incapable of the operation. The problem was that the release intent was hidden inside a local gesture.

Two branches with two meanings

The new workflow makes the split visible. A pull request merges into main; CI runs tests, type checking, and the build; Cloudflare Pages updates the beta preview. Production remains still until a person chooses to publish it.

The Release to production workflow accepts one ref input. It can be a branch name, a tag, or a commit SHA, with main as the default. That choice is useful even when the ordinary path is simply “publish the latest main.” It makes the exceptional paths—release this tag, or roll back to that commit—ordinary inputs instead of improvised shell commands.

The workflow first resolves the input to a full commit SHA. It then runs:

git push --force origin "${SHA}:refs/heads/prod"

The force push is intentional. prod is a deploy pointer, not a branch where development work accumulates. Its value is the tree at HEAD, because Cloudflare Pages deploys that tree. A fast-forward-only update would make the pointer obey the shape of the development history, even when the desired production state is an older tag or an arbitrary commit.

With a pointer, rollback is direct. Dispatch the workflow with the older tag or SHA, and prod moves back to that known tree. There is no reverse merge to manufacture, and no requirement that the rollback commit be a descendant of the current production commit.

An audit trail, not a ceremonial gate

The workflow also turns the release into a visible record. Its run name includes the selected ref. The run summary records the ref, the resolved SHA, and the one-line commit description. A concurrency group prevents two releases from racing each other.

That record is valuable, but it is important to describe the gate accurately. The first version of the workflow, introduced in PR #122, declared environment: production and was written as though a required reviewer would approve every dispatch. There was no such protected Environment configured. GitHub therefore treated the line as having no protection rules: it looked like a safety mechanism without providing one.

PR #124 removed the inert line. The correction matters because a configuration that sounds protective can be more misleading than an explicit manual action. Today, the deliberate gate is the dispatch itself: a person chooses the ref and clicks Run workflow. The Actions history then shows what that choice published. The README now says the same thing as the workflow and deployment documentation.

This is a smaller gate than a separate approval ceremony, but it is an honest one. The system does not claim that an approval happened when only a dispatch happened. If a stronger review requirement becomes necessary later, it can be added as a real repository setting rather than implied by an unused line.

Byproduct: a stale safety sentence

The most useful correction was not in the force-push command. It was finding the sentence that still described the old gate.

The release workflow and docs/deploy.md had already moved to the manual-dispatch model, but the deployment paragraph in README.md still said “behind a required-reviewer approval.” That sentence survived because the workflow change and the documentation correction landed in different commits. It was easy to read the current workflow and miss the older summary at the front door.

That is a general maintenance lesson for automation: when a safety boundary changes, search for every place that names the boundary. A stale sentence is not merely editorial if it changes what an operator believes will happen. Here, correcting one parenthetical was enough to make the public explanation match the executable path.

Production now has a narrow meaning: prod points at the tree the apex should serve. main can continue to move quickly in beta. The release workflow makes the act between them explicit, reproducible, and reversible.

Source: PR #122 (commit 1cb378c, issue #121), PR #124 (commit 42bf7c7, issue #123), .github/workflows/release-to-production.yml, docs/deploy.md, and README.md, July–September 2026.