
Semantic Versioning Is Lying to You
Why semver's clean rules break down against messy reality, creating emotional version numbers that don't reflect actual effort
Semantic versioning promises mathematical precision but delivers emotional chaos. The simple rules of MAJOR.MINOR.PATCH sound elegant until you realize they force you to treat a one-character typo fix as more significant than a year-long feature development.
The Broken Promise of Precision
Semantic versioning’s rules seem straightforward: bump MAJOR for breaking changes, MINOR for backward-compatible features, PATCH for bug fixes. But this creates absurd situations where fixing a single character in a method name warrants a major version bump, while adding an entire new subsystem that took months of development only merits a minor version.
As Jacob Tomlinson notes in his EffVer proposal ↗, “many projects violate the semantics laid out in SemVer because often reality is more complex than the scheme allows for.” The Python ecosystem in particular struggles with this tension between semver purity and practical development realities.
The Emotional Version Number Rebellion
Developers consistently rebel against strict semver interpretation because it feels wrong. When a team spends three months building a massive new feature that adds tremendous value without breaking existing interfaces, calling it version 1.2.0 feels disrespectful to their effort. Conversely, forcing a major version bump because you fixed a typo in documentation feels like bureaucratic nonsense.
This isn’t just about ego, it’s about communication. Version numbers serve as both technical signals and psychological markers. A major version increment tells users “this will require significant effort to adopt”, while also signaling “this represents a major milestone for our project.” When these two meanings collide, teams inevitably prioritize human communication over rigid specification compliance.
Practical Alternatives Emerge
The Elixir ecosystem demonstrates this tension perfectly. Many v0.x.y libraries unofficially treat the x in v0.x.y as the major version, allowing for small but regular breaking changes while staying technically within semver’s rules for pre-1.0 software. This pragmatic approach acknowledges that early-stage projects need flexibility while still providing meaningful version signals.
Tomlinson’s EffVer proposal ↗ formalizes what many projects already do informally: version based on the effort required to upgrade rather than strict semantic rules. Projects including Jupyter Hub, Matplotlib, and JAX have adopted this approach, recognizing that communicating upgrade complexity matters more than adhering to theoretical purity.
The Maintenance Burden of False Precision
The real cost of strict semver adherence hits maintenance teams. Every major version bump creates documentation updates, migration guides, and support overhead. When these bumps don’t align with actual user impact, teams either waste effort on insignificant changes or avoid proper versioning altogether.
Many teams resort to “semver-ish” approaches, following the spirit rather than the letter of the specification. They might bundle several breaking changes into a single major version or use release notes to provide more nuanced guidance than the version number alone can convey. This pragmatic dishonesty serves users better than robotic adherence to rules that don’t match human expectations.
Semantic versioning’s greatest failure isn’t technical, it’s psychological. It assumes developers will prioritize mathematical purity over human communication, when in practice we always choose the latter. The version numbers that actually get used are emotional artifacts, not logical ones, reflecting how changes feel rather than how they classify.