# Below 1.0, the minor number is the breaking one

Foxstash 0.9 removed a public storage mode. Anyone whose code named it gets a compile error on
upgrade. That's a breaking change, so the release is 0.9.0, not 0.8.1.

That sounds obvious. It wasn't written down anywhere until this week, and the reason it matters below
1.0 is not the reason most people think.

## What the caret actually does

When you add a dependency in Rust, `cargo add` writes something like `foxstash-core = "0.8.0"`,
which Cargo reads as `^0.8.0`. npm writes `"^0.8.0"` outright. Both mean "this version, plus anything compatible with it". And both decide
compatibility the same way:

| You wrote | Accepts | Refuses |
|---|---|---|
| `^1.3.4` | `1.3.5`, `1.4.0`, `1.99.0` | `2.0.0` |
| `^0.3.4` | `0.3.5`, `0.3.99` | `0.4.0` |

Below 1.0, the caret treats the second number as the one that breaks things. So a dependent pinned
to `^0.3.4` picks up every `0.3.y` the next time anyone runs an update or builds without a
lockfile, and refuses `0.4.0` until a person chooses it.

That makes the version number a promise. Ship a breaking change as `0.3.5` and it lands in every
dependent's next build, unasked, and breaks it. Ship it as `0.4.0` and it waits until they opt in.
The number is the only signal they get.

## The rule

Semantic versioning, shifted one place while the major version is zero:

| Change | Before 1.0.0 (`0.x.y`) | From 1.0.0 (`x.y.z`) |
|---|---|---|
| Breaking (`!` or `BREAKING CHANGE:`) | x: `0.3.4` to `0.4.0` | x: `1.3.4` to `2.0.0` |
| New feature (`feat`) | y: `0.3.4` to `0.3.5` | y: `1.3.4` to `1.4.0` |
| Fix or other released change (`fix`, `perf`) | y: `0.3.4` to `0.3.5` | z: `1.3.4` to `1.3.5` |

Below 1.0 there's no separate slot for features, so features and fixes share the last number. That
loses a little information. It's the price of keeping the one signal that matters, "this will break
you", in the position the tools actually read.

Two more lines go with the table.

**Going to 1.0 is a decision, not a counter.** It means the public API is stable. It shouldn't
happen because a run of features rolled the number over.

**Tell your release tool.** By default, release-please treats a breaking change below 1.0 as a
jump straight to 1.0.0, and bumps the minor on every feature, which under this rule marks every
feature as breaking. Set `bump-minor-pre-major` and `bump-patch-for-minor-pre-major` to true and
it follows the table.

## Where it lives now

My company's software lifecycle procedure now has this table in its release stage, and a release
isn't done until the version bump matches the type of change. The yearly internal audit checks
it: for each sampled release, compare the commits since the last tag, and if any were breaking, the
minor number had better have moved. The same table sits in the knowledge base my build machines
read, word for word, because those machines can't see the company drive where the procedure lives.

Foxstash 0.9.0 was the first release after the rule was written down. It's breaking, it bumped the minor, and anyone
on `foxstash-core = "0.8"` stays on 0.8 until they decide to move.

## What I'd tell you if you were buying this work

If you depend on a library that's still below 1.0, read its changelog before each minor upgrade,
and check whether the maintainers treat the minor number as the breaking one. If you're paying
someone to ship a library to you, ask how they number releases and what stops a breaking change
from going out as a patch. "We're careful" isn't an answer. A rule someone audits is.
