How do you set the versioning policy for a Helm chart that many teams install?
answer
- Decide what a bump promises the consumer
- Two defensible models, different owners
- One number answers your risk or theirs
- Price a major in other teams' work
- Retirement is a published version too
basics
~20 sDecide what the chart version promises. For a chart many teams consume, version it on its values contract and rendered output, let appVersion float independently, batch breaking changes into rare majors, use prereleases as an opt-in channel, and retire the chart with the deprecated flag.
solid answer
~50 sStart from the question the version has to answer for a consumer: *can I take this upgrade without reading anything?* That points at versioning the chart on its **own** contract — major when the values interface or the rendered topology breaks, minor for new optional values, patch for template fixes — while `appVersion` tracks the application independently. The alternative, pinning chart version to application version one-to-one, is simpler and honest when one team owns both and they always ship together, but it leaves no number for a template-only fix and tells consumers nothing about their own risk. Then set the surrounding policy: majors are budgeted and batched because each one is a migration for every consuming team; release candidates give a canary channel that ordinary upgrades cannot pick up; and retirement is an announced `deprecated: true` on the final version, not an unpublish.
code
yaml · 7 linesapiVersion: v2
name: invoice-worker
description: Renders invoices from a work queue
type: application
version: 4.0.0
appVersion: "2.3.1"
deprecated: truego deeper
You are not expected to set policy, but know that consumers pin a chart version and that the number is how they judge an upgrade. Follow whatever bump rule your chart's README states.
Be able to argue which bump a given change deserves, and explain why changing a default value is not a patch even though the diff is one line.
Show you can run a migration: batch breaking changes, publish a candidate, wire a loud failure on removed keys, and know which teams are installed before you announce.
Own the model choice and its cost. Say what the version promises, price a major in consuming teams' work, budget how often one ships, and design the values interface so majors stay rare.
## What the number has to mean A chart version is the only communication channel you have with people who install your chart. Everything else — the README, the migration note, the chat announcement — is optional reading. So policy starts by deciding what a consumer may infer from a bump, and the useful inference is about *their* risk, not your release process: can they take this upgrade without editing their values file or expecting a restart? ## The two coherent models **Chart version tracks the application.** The chart is packaging for one service, the same team owns both, and every application release publishes a chart. Often chart `version` and `appVersion` are simply equal. This is genuinely good when the chart is deployed only by its own pipeline: one number, no ambiguity, and the chart version answers "which build is this?". It breaks down as soon as the chart is a product: a template-only fix has no application version to borrow, so you either invent one or break the equality you promised; and a consumer reading a jump from 1.14.3 to 1.15.0 learns something about your application and nothing about their values file. **Chart version tracks the chart's contract.** Major when the values interface or the rendered topology breaks, minor when you add optional values or new optional objects, patch when you fix a template without changing output for anyone. `appVersion` floats independently and simply records what is inside. This is the right default for a chart many teams install, because the number now answers the question the consumer actually has. Its cost is that two numbers must be read together, and that you have to be disciplined about what "changes output for anyone" means — changing a default is not a patch. The choosing factors are concrete: how many consumers, whether you can migrate them yourself, whether the same team owns the application and the chart, how often the application releases, and whether the chart is published to a shared repository at all. A chart living in the application's own repository and deployed only by its own pipeline barely needs a policy — the pipeline pins a commit and the version is decoration. The moment a second team installs it, everything above applies. ## Budgeting majors The expensive fact is that a major is not an event in your repository; it is a piece of work in every consumer's. With seventeen consuming teams, one values rename is seventeen pull requests, seventeen review queues and seventeen deploys, spread over however long the slowest team takes. That drives real policy. Batch breaking changes rather than trickling them: hold a `next-major` branch of intended changes and ship them together. Budget majors explicitly — at most one or two a year for a widely installed chart — and treat wanting a third as a design smell in the values interface. Announce before publishing, with a before-and-after values snippet. And keep a bridge where you can: accept the old and new key for one minor line, warn through `NOTES.txt`, then remove in the next major. A bridge with no removal date is just a permanent second interface. ## Channels and retirement Prerelease versions give you a canary lane inside the same repository. Publishing `3.0.0-rc.2` is invisible to ordinary installs and upgrades — prerelease versions are excluded from matching unless a consumer opts in — so one friendly team can validate a major before the rest of the world sees it. Promote by publishing `3.0.0` as a new version; never re-push a candidate with different content, and never re-push any published version, because a chart version that means two things destroys the only guarantee you sell. Retirement is a version too. `deprecated: true` in `Chart.yaml` marks a chart as deprecated, and if the *latest* published version carries it the chart as a whole is treated as deprecated: ```yaml apiVersion: v2 name: invoice-worker version: 4.0.0 appVersion: "2.3.1" deprecated: true ``` What that flag does not do is matter to anyone already running the chart: existing releases keep running, and the chart can still be installed. So the flag is the announcement mechanism, not the migration; you still owe consumers a replacement and a path to it, and you still need to know who is on it — which is what `helm list` across your clusters tells you, since the CHART column carries the chart name and version of every live release. ## What a principal answer sounds like It does not recite version-number rules. It says what the number promises, admits the tradeoff between the two models rather than declaring one correct, prices a major in other teams' time, and names the mechanisms Helm actually gives you: a version consumers resolve, an appVersion that is metadata, prerelease exclusion as a channel, a deprecation flag as an announcement, and nothing else. Everything more — knowing who is installed, chasing migrations — you have to build.
- When is pinning chart version to application version one-to-one the right call?When the chart exists to ship one application, the same team owns both, and the chart is deployed by that application's own pipeline rather than installed by others. Then a single number is simpler and the chart version doubles as a build identifier. Reconsider the moment a second team installs it, or the first time a template-only fix needs a version and the application has not moved.
- What does deprecated: true actually change for a chart consumers already run?Nothing operationally. Existing releases keep running and the chart remains installable; the flag is metadata that marks the chart, and when the latest published version carries it the chart as a whole is considered deprecated. It is the announcement, not the migration — you still owe a replacement, a path to it, and an inventory of who is still installed.
- How do you find out who would be affected before you publish a major?Helm gives you the live side: `helm list` across the clusters you can reach reports each release's chart name and version, so you can see who sits on which line. It does not tell you who has pinned what in their own repositories — that comes from searching your organisation's configuration repositories for the chart reference. Knowing both before announcing is what turns a major from a surprise into a schedule.
- A team asks for a breaking values change that only they need. How do you respond?Look for a non-breaking shape first: a new optional key whose default reproduces today's output usually gets them what they want at the cost of one more key. If it genuinely cannot be additive, it queues for the next major rather than shipping alone, because the cost is paid by every other consumer. A chart accumulating one-team escape hatches is a signal the chart is too broad, not that the policy is too strict.
saying these in an interview costs you the question
- Insisting chart version must equal application version
- Shipping breaking values changes as they arrive
- Treating a major as free because publishing is easy
- Re-publishing an existing chart version with fixes
- Assuming deprecated: true blocks new installs
- Keeping compatibility bridges with no removal date