Stable Is a Contract, Not a Mood: Rethinking How Projects Signal Readiness
Somewhere in a sprint planning meeting in Austin, an engineering manager is deciding whether her team can build on a library marked 1.0. Across the country, a maintainer in Portland tagged that same release 1.0 on a Saturday afternoon, mostly because the issue tracker had been quiet for a month. Neither of them is wrong. But they are almost certainly not having the same conversation.
The version number was supposed to solve this. It didn't.
Three Audiences, Three Definitions
Ask a maintainer what "stable" means and you'll usually hear something about test coverage: the suite passes, the rough edges are documented, the code is no longer embarrassing. Ask an enterprise procurement team and the answer is about time — a commitment that interfaces won't shift underneath a multi-year deployment, that security patches will arrive on a predictable cadence, that someone will answer the phone. Ask a hobbyist building a weekend project and stability barely registers as a concept; they'll pull from main if the README looks friendly.
The same label, three incompatible promises. When a project ships a 1.0, it is implicitly signing all three contracts at once while only having drafted one of them.
This gap explains a familiar pattern: an enterprise team freezes on a library, the maintainer ships a 2.0 with modest breaking changes, and the freeze turns into resentment. The maintainer feels the changes were reasonable; the enterprise feels betrayed. Nobody lied. They just never agreed on what the number meant.
Version Numbers Are Folklore
Semantic versioning is a useful convention, but it is not a specification of behavior. Nothing about the string "1.4.2" tells you whether the project has a security policy, whether deprecations get a migration window, whether the public API is the exported surface or the thirteen internal functions everyone imports anyway.
Worse, the culture around version zero has hardened into superstition. Teams treat 0.x as radioactive and 1.0 as a blessing, so maintainers delay the 1.0 for years — not because the code is immature, but because they're afraid of inheriting an unbounded compatibility obligation they can't afford. Meanwhile, other projects slap 1.0 on at birth and discover the enterprise expectations too late.
Folklore doesn't scale. Contracts do.
The Stability Contract
We'd like to see every serious open-source project publish a short document — a stability contract — that answers, in plain prose, the questions version numbers leave ambiguous:
- What surface is covered. Not "the API" but precisely which exports, flags, file formats, and wire protocols carry compatibility guarantees. If internal modules aren't covered, say so by name.
- What changes require a major bump. Enumerate the categories: signature changes, error semantics, behavioral defaults. Ambiguity here is where trust dies.
- **What the deprecation window is." A specific duration — one minor release, ninety days, two quarters — during which deprecated functionality keeps working and emits a warning.
- What support actually includes. Security backports for how long? On which branches? "Best effort" is not an answer; it's an apology in advance.
- What the project will never promise. Maintainers deserve to write this section too. If the plugin API is experimental, if the CLI output is not machine-readable by contract, say it before someone depends on it.
This is not bureaucratic overhead. It's the same instinct that produced well-run projects' SECURITY.md and governance documents: write down the rules so they don't depend on whoever happened to be in the room.
Why This Serves Maintainers, Not Just Users
The predictable objection is that contracts invite liability — that if you promise ninety days and life gets in the way, you've handed people a grievance. In practice the opposite holds. A published contract converts an open-ended obligation into a bounded one. "Stable forever, whatever that means" is the promise that actually burns maintainers out, because every user's interpretation becomes your problem.
A contract also gives maintainers a tool for saying no. When a contributor proposes a breaking change with no migration path, the discussion stops being a matter of taste and becomes a matter of policy. That's a healthier argument to have, and a faster one to win.
A Modest Proposal for Reviewers and Buyers
If you're evaluating a dependency, skip the version number and go looking for the contract — explicit or de facto. Read the changelog for the last year: how often do breaking changes appear, and how much notice preceded them? Check whether deprecation warnings exist in the codebase at all. Look for a MIGRATION.md, a policy document, even a well-maintained GitHub discussion where maintainers explain their thinking.
If you find nothing, that absence is data. It doesn't mean the project is bad; it means the project's stability is currently a rumor, and you'd be building on hearsay.
Version numbers are a compression algorithm, and compression loses information. The industry's habit of treating "1.0" as a legal instrument is a collective fiction we keep paying for. Write the contract. Publish it in the repository, next to the license, where it belongs. Stability isn't a mood the code happens to be in — it's a commitment someone chose to make, and commitments work best when they're legible.