Skip to content

Hotfix Flow

martyy-code edited this page Aug 5, 2026 · 5 revisions

Last synced from docs/engineering/process/hotfix.md on 2026-08-05. The repo file is the source of truth. If the two diverge, follow the repo.

This page describes the hotfix flow for @deessejs/fp: when to use it, how it differs from the regular release pipeline, and what every contributor and release engineer needs to do.

A hotfix is the path used to ship an urgent production fix without dragging unfinished work from staging. It is the one exception to the rule that every PR targets staging first.

Outline

  1. When to use the hotfix flow
  2. The flow at a glance
  3. Cut the hotfix branch
  4. Open the hotfix PR against main
  5. Ship the hotfix
  6. Back-merge to staging and dev
  7. Open the follow-up PR on staging
  8. Why the rule above all: every main merge is a release
  9. Related documents

1. When to use the hotfix flow

Use the hotfix flow when all of the following are true:

  • There is an urgent production bug that cannot wait for the next regular release.
  • staging contains work that is not ready to ship to production.
  • Merging staging into main to release the fix would drag the unfinished work along with it.

If the unfinished work in staging is ready to ship, the regular pipeline is faster end-to-end. The hotfix flow only earns its keep when the urgency justifies bypassing staging.

2. The flow at a glance

main                                  staging                            dev
────                                  ───────                            ───
hotfix/issue-123 ──┐
                   │  PR target: main
                   │  (the one exception)
                   ▼
                   │  hotfix.yml runs
                   │  build → test → smoke → publish →
                   │  tag vX.Y.Z → GitHub Release
                   │
                   │  auto-backmerge main → staging
                   │  (carries the fix forward)
                   ▼
              staging is in sync with main.
                   │
                   │  follow-up PR target: staging
                   │  carries a Changeset (patch)
                   ▼
              staging has the audit entry for the fix.
                   │
                   │  auto-backmerge main → dev
                   ▼
              dev is in sync. Ready for the next feature.

The flow is six steps; the rest of this page walks through each.

3. Cut the hotfix branch

Cut the branch from main, never from staging or dev. The branch should be the production code plus nothing else, so what you test is exactly what production will get.

git checkout main
git pull
git checkout -b hotfix/issue-123

If the fix already exists as a commit on staging, cherry-pick it onto the hotfix branch rather than rebuilding it:

git cherry-pick <commit-hash>

If the fix does not exist yet, write it directly on the hotfix branch.

4. Open the hotfix PR against main

The hotfix PR targets main directly. This is the one exception to the rule that every PR targets staging (see the Release Process page for the normal pipeline).

A few rules apply:

  • The PR does not need a Changeset file. The per-PR Changeset rule (Authoring Changesets § 8) applies to PRs targeting staging; hotfix PRs are explicitly out of its scope. The follow-up PR on staging carries the Changeset, see § 7.
  • The PR title should make the urgency obvious, for example [HOTFIX] Fix panic in Result.fold on circular references (#123).
  • The PR description should link the incident or issue, summarize the root cause, and list the tests run.
  • Branch protection on main requires the hotfix PR to be reviewed before merge, but the reviewer pool may be smaller (the hotfix GitHub environment, documented in the Release Process page).

5. Ship the hotfix

When the hotfix PR merges into main, a dedicated hotfix.yml workflow fires (not the regular publish.yml). The workflow:

  • Runs pnpm build and pnpm test against the merge commit.
  • Runs the smoke test on dist/.
  • Runs pnpm changeset publish --tag latest.
  • Tags the merge commit as vX.Y.Z.
  • Creates a GitHub Release page with auto-generated notes.

The smoke test, the anti-republish guard, and the OIDC publish are identical to the regular release path. What differs is the trigger: a push event on main from a hotfix/* branch, instead of a pull_request: closed on a regular PR.

6. Back-merge to staging and dev

After the hotfix is published, main is ahead of staging and dev. Back-merge is mandatory. Skipping it is the usual cause of the next merge conflict.

The back-merge is automated by a workflow that fires on every push to main:

auto-backmerge: main → staging
  • open a PR with title "Backmerge main → staging"
  • skip if there is nothing to backmerge
  • skip if a backmerge PR is already open
  • do not trigger another backmerge attempt on its own merge (anti-recursion)
  • auto-merge on green CI

The same workflow runs for main → dev on a separate trigger.

If the auto-backmerge workflow is not enabled on the repository, the maintainer runs the merge manually:

git checkout staging
git merge main
git push

git checkout dev
git merge main
git push

7. Open the follow-up PR on staging

The hotfix PR shipped to npm without a Changeset, which means the CHANGELOG.md for that release is empty (or only contains unrelated changes). To restore the audit trail, open a follow-up PR on staging that:

  • Targets staging.
  • Adds a Changeset file under .changeset/ with patch level and a one-line summary describing the hotfix (e.g. Fixed panic in Result.fold on circular references (#123).).
  • Has no code changes — it is a documentation PR.

This follow-up PR follows the normal per-PR Changeset rule and lands in the next regular release, where the user-visible changelog entry finally appears.

Why a separate PR? Because the hotfix itself cannot wait for a Changeset review, and the follow-up PR is too trivial to justify bypassing staging. Splitting them keeps the hotfix fast and the changelog honest.

8. Why the rule above all: every main merge is a release

The Release Process page § 1.1 establishes that every merge to main triggers a release. The hotfix is the only path where that rule produces a release without a corresponding changelog entry on the merge commit itself.

This is intentional. The trade-off is:

  • Hotfix path: minimal time to production. Trade-off: the changelog entry is deferred to the next release via the follow-up PR.
  • Regular path: the changelog entry is on the same commit as the release. Trade-off: the fix waits for staging to be shippable.

Both paths produce a complete, audited release at the end. The hotfix path just defers the visible part of the audit trail by one release.

9. Related documents

Clone this wiki locally