-
Notifications
You must be signed in to change notification settings - Fork 0
Hotfix Flow
Last synced from
docs/engineering/process/hotfix.mdon 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.
A hotfix cuts a hotfix/* branch from main, opens a PR against main directly
(no Changeset on that PR), ships via a dedicated hotfix.yml workflow, back-merges
main to staging, and finally opens a follow-up PR on staging that carries a
Changeset describing the fix. The follow-up PR lands in the next regular release,
where the changelog entry finally appears.
- When to use the hotfix flow
- The flow at a glance
- Cut the hotfix branch
- Open the hotfix PR against
main - Ship the hotfix
- Back-merge to
staging - Open the follow-up PR on
staging - Why the rule above all: every
mainmerge is a release - Related documents
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.
-
stagingcontains work that is not ready to ship to production. - Merging
stagingintomainto 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.
main staging
──── ───────
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.
The flow is five steps; the rest of this page walks through each.
Note on the branching model.
CLAUDE.mddocuments the model asmain <- staging <- dev, butdevis a conceptual name for the collective per-feature work-in-progress, not a long-lived branch. The modelfeature/*andfix/*branches targetstagingdirectly; there is nodevbranch to back-merge into. The back-merge target after a hotfix is thereforestagingonly. Activefeature/*branches pick up the fix on their next rebase.
Cut the branch from main, never from staging. 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-123If 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.
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 onstagingcarries 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
mainrequires the hotfix PR to be reviewed before merge, but the reviewer pool may be smaller (thehotfixGitHub environment, documented in the Release Process page).
When the hotfix PR merges into main, a dedicated hotfix.yml workflow fires (not the regular publish.yml). The workflow:
- Runs
pnpm buildandpnpm testagainst 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.
After the hotfix is published, main is ahead of staging. 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
If the auto-backmerge workflow is not enabled on the repository, the maintainer runs the merge manually:
git checkout staging
git merge main
git pushActive feature/* and fix/* branches pick up the fix on their next rebase from staging. There is no dev branch acting as an intermediate.
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/withpatchlevel 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.
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
stagingto 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.
- Release Process: the regular release pipeline, and the three trigger paths.
- Authoring Changesets: how to write the Changeset file that the follow-up PR carries.
- Wiki Operations with
ghCLI: how to read and write this wiki. - Internal:
docs/engineering/process/hotfix.md: operational details, including the exact workflow files and CI configuration. - Internal:
docs/engineering/plans/release-pipeline.md§ 8.3: the senior plan's hotfix architecture. - Internal:
docs/engineering/reports/changesets-hotfix-research.md: research notes that informed this page.
This wiki is maintained alongside the @deessejs/fp package. Pages here mirror internal documentation at docs/engineering/ in the repository; the repo file is the source of truth.
- Repository: source code, issues, releases.
- npm package: install, changelog, version history.
- Issue tracker: bug reports and feature requests.
- Discussions: questions and design conversations.