Your latest release is 2.1.0, and a critical bug is reported by customers still running 1.9.3. How do you ship the fix, and what version does it carry?
answer
- patch the line the users are on
- published versions never change
- smallest possible delta under pressure
- mind what the default pointer resolves to
- forward-port or it comes back
basics
~20 sRelease it as 1.9.4 from a maintenance line cut at the v1.9.3 tag, containing only the fix. Publish it on a separate release channel so 1.x users receive it while 2.x users are not pulled backwards, and land the same fix on the mainline.
solid answer
~50 sThree decisions, in order. First the version: it is a PATCH on the line the affected users are on, so `1.9.4` — never `2.1.1`, which those customers cannot install, and never a re-cut `1.9.3`, because published versions are immutable. Second the path: a maintenance line branched from the `v1.9.3` tag carrying only the fix, so customers get a minimal, reviewable delta rather than eighteen months of unrelated 2.x work. Fix the mainline first if you can, then port to the maintenance line — do it the other way and the bug reappears in 2.2. Third the distribution: publish `1.9.4` on its own channel or distribution tag rather than the default one, so a consumer resolving "give me the current release" still gets 2.x while consumers pinned to `1.x` pick the fix up. Then say out loud, in a support policy, how long the 1.x line stays open.
go deeper
Be ready to say that the fix ships as 1.9.4 — a patch on the line those users are on — and that you never change what an already-published version contains.
Explain the mechanics: branch from the released tag so the delta is minimal, run the same pipeline, and forward-port the fix to the mainline so it does not regress in the next release.
Show that you would also control distribution — publishing on a non-default channel so nobody resolving 'latest' is silently downgraded — and that you would verify the default pointer afterwards.
Own the support policy that makes this routine: how many lines stay open and for how long, what each open line costs in pipelines and preserved toolchains, and how the commitment is communicated to customers before an incident forces the question.
## Why this question is asked It is the question that separates people who have only ever shipped from a single moving branch from people who have supported released software. Every part of the answer — the number, the path, the channel, the policy — is a decision with a way to get it wrong. ## The version number `1.9.4`. The reasoning is mechanical once you accept what SemVer's fields mean: the change is a backward-compatible bug fix, so it is a PATCH, and it is a patch *on the line the affected users are on*. Three wrong answers show up regularly: - **`2.1.1`** — the affected customers are on 1.x precisely because they have not done the 2.0 migration. A fix they cannot install is not a fix. - **Re-publishing `1.9.3` with the fix inside** — published versions are immutable. Silently changing the contents of an existing version breaks every consumer that has recorded its checksum and destroys the ability to reason about what is deployed anywhere. - **`1.9.3-hotfix1`** — a pre-release identifier sorts *below* `1.9.3`, so this version is older than the thing it fixes as far as any resolver is concerned. ## The code path The fix ships from a maintenance line based on the `v1.9.3` tag, not from the mainline. That is the whole point: customers on 1.9.3 get the smallest possible delta — one fix — rather than everything that has landed in 2.x. A minimal delta is reviewable, testable and low-risk, which matters because a hotfix usually ships under time pressure with a compressed test cycle. Direction matters. Fix the mainline first, then port the fix to the maintenance line. Fixing only the maintenance line is the classic regression: 2.2 ships with the bug back, because the mainline never received the change. If urgency forces the maintenance fix first, the port back to the mainline is part of the same piece of work, not a follow-up ticket. Critically, the maintenance line is not a one-off scratch branch. Once you have supported customers on an older line, that line is a long-lived release target with its own pipeline runs and its own artifacts. ## The distribution channel This is the part people miss. Most registries and package managers have a concept of a default pointer — the version a consumer gets when they ask for "the current one" with no constraint. If you publish `1.9.4` to that default pointer, every consumer who asks for the latest silently downgrades from 2.1.0 to 1.9.4. That is a self-inflicted outage. So maintenance releases go on a separate channel: a non-default distribution tag, a distinct repository or feed, or whatever the ecosystem's equivalent is. Consumers who pinned to the `1.x` range resolve the new patch normally, because range resolution reads version numbers rather than the default pointer. Release tooling models this directly. semantic-release, for example, is configured with a `branches` list in which a maintenance branch such as `1.x` publishes on its own release channel rather than the default one, and pre-release branches (`next`, `beta`) do the same for the other direction — versions like `2.2.0-beta.1` that early adopters opt into and that no default resolution ever reaches. ## Pre-release channels are the same mechanism pointed forwards The machinery that keeps `1.9.4` away from 2.x consumers is the machinery that lets you publish `3.0.0-rc.1` without disturbing anyone on 2.x. Both rely on the two rules from SemVer: pre-releases sort below their release, and a range never matches a pre-release unless the consumer asks for one. A team that has set up channels for one direction has already paid for the other. ## The policy question underneath Having answered the mechanics, the follow-up is always: *how many lines do you support, and for how long?* Every open maintenance line is a permanent tax — a pipeline that must keep working, a build environment that must keep existing (old toolchains rot), test infrastructure for a platform matrix you would rather retire, and a decision to make on every security advisory. Teams that never write the policy down end up quietly supporting five lines because no customer ever agreed to stop asking. A typical written policy: the current major receives fixes; the previous major receives security and critical fixes for N months after the successor ships; anything older receives nothing. The value of writing it down is that the hotfix conversation stops being a negotiation and becomes a lookup. ## What good looks like operationally - The maintenance line runs the *same* pipeline as the mainline. A hotfix is not a reason to hand-build and hand-upload an artifact; that is exactly when you most need the process that produces reproducible, signed, tested output. - The fix is on the mainline before, or in the same work as, the maintenance release. - The release notes for `1.9.4` say what it fixes and explicitly say it is a maintenance release for the 1.x line. - Someone checks afterwards that the default pointer still resolves to 2.1.0.
- What actually goes wrong if you publish 1.9.4 to the registry's default pointer?Every consumer resolving without a constraint — a fresh install, a CI job that asks for the current version, an unpinned Dockerfile — silently moves from 2.1.0 to 1.9.4. That is a downgrade across an incompatible major boundary, which typically surfaces as missing APIs, failed migrations, or a service that starts and then behaves like last year's build. The fix is to publish maintenance releases on a non-default channel.
- How many maintenance lines should a team keep open?As few as the support commitment allows, and the number should be written down rather than emergent. Each open line costs a working pipeline, a preserved toolchain, test capacity, and a triage decision on every advisory. A common policy is: current major gets everything, previous major gets security and critical fixes for a fixed window after its successor ships, older gets nothing.
- Why is 1.9.3-hotfix1 a bad version for this fix?Because SemVer gives pre-release versions lower precedence than the release they hang off. `1.9.3-hotfix1` sorts *below* `1.9.3`, so any resolver treats it as older than the version it is meant to repair, and consumers on 1.9.3 will never be offered it. Patch releases move the number up: 1.9.4.
- When is the right answer to this scenario "tell the customer to upgrade to 2.x"?When it is a decision, not a reflex. It can be right if 1.x is past its published support window, if the fix is structurally impossible to backport, or if the migration is genuinely cheap. It is wrong as an instinctive answer to a critical bug in a line you are still supporting — that turns a patch release into a forced migration under incident pressure.
saying these in an interview costs you the question
- Shipping the fix as 2.1.1 to everyone
- Re-publishing 1.9.3 with new content
- Publishing the hotfix as the default latest
- Fixing only the branch, so 2.2 regresses
- Hand-building the hotfix artifact outside the pipeline