Peter Stuifzand

Semantic versioning is outnumbered by inherent conflict

We version our code with Semantic versioning. Semantic versioning adds structure and rules for working with versions. Semantic versioning promises to solve the dependency hell problem. According to the website, dependency hell is that sometimes dependencies are specified too loosely, compatibility is assumed with too many future versions. When the dependency is too strict, you have to update the package to be able to use new version. And with version numbers following the rules, the promise is they will reliably communicate compatibility information.

One of the packages you’re using has a new feature you need. The feature could help with your current task. But you see the major version changed. Should you upgrade for the new feature or stay on the current version? A difficult question that semantic versioning cannot answer.

Semantic versioning cannot solve dependency problems, because it tries to resolve an inherent conflict through numbering. Library authors need freedom to improve their code, while users need stability. No versioning scheme can eliminate this fundamental tension. To understand why this conflict is inevitable, let’s examine what each side actually needs.

What are the needs of library authors?

Library authors want to improve their software through bugfixes, new features, fixing bad design, and cleaning up technical debt. But improvements that help new users, often create problems for existing users. When we fix a typo in a config field from databse_url to database_url, then new users get clear documentation, but existing users find their configuration ignored with no obvious explanation.

What are the needs of developers?

Developers want new features and stability. Stability is even more important than new features. Sometimes features can be important, but only as long as the stability can be maintained.

Upgrading a library and finding that you need to check every call to a function is not fun, especially after releasing a new version to production.

Now that we understand the needs of both sides, we can see why these needs inevitably conflict.

Why are these needs in conflict?

Library improvements often require breaking changes. When developers see that new features come with compatibility breaks, upgrades become costly decisions instead of simple package manager updates.

Even when version numbers correctly signal breaking changes, developers still face the same problem. A developer needs the new token reset feature in version 3.0.0, but they’re on 2.7.6. The package manager upgrade is easy, but now they must test login, logout, and token refresh across their entire application.

When the users stay logged in until they refresh the page, the developer now has a new bug. If the change was well-documented and works first time, they’re lucky. If not, they face a costly choice, debug the new version or find a different solution entirely.

With this conflict clearly established, we can see why semantic versioning cannot resolve it.

Why can’t semantic versioning resolve this conflict?

Semantic versioning only labels the conflict, it doesn’t resolve it. Even with perfect version numbering, developers still face the same costly trade-offs. The version number only tells you a breaking change exists, but you still have to do all the work: testing your application, debugging new behaviors, and deciding whether the upgrade is worth the effort.

The fundamental problem isn’t communication; it’s that improving software often requires breaking existing functionality. No numbering scheme can make incompatible changes compatible.

© 2025 Peter Stuifzand