DEV Community

Cover image for Beyond Semantic Versioning
Gregory Witek
Gregory Witek

Posted on • Originally published at notonlycode.org on

Beyond Semantic Versioning

An idea on how to improve software versioning and managing dependencies came to my mind a few days ago. I'm yet to figure out how it would exactly work, but I thought I'd share it first.

Who follows Semantic Versioning?

Semantic Versioning is a great idea, a lot of projects aim to follow it, and yet not all of them do it in 100%. The main issue is increasing major version whenever a backward-incompatible change is introduced. People don't want to release MyLib 2.0 when the current version is 1.3 and the new release fixes just 1 bug in a way that in some cases breaks backwards compatibility. On the other hand as someone whose application or library depends on MyLib, I'd like to be able to safely update it across all 1.x versions without worrying that some change will break my code.

And when some project really increases major version even for tiniest incompatibility, I find is scary to see that 3 months after upgrading library from v5.0 to v8.0, now I need to update it to v12.0, because there were a few minor, but breaking changes. Normally I expect major version to introduce, well, major changes - and while I know that this is not what SemVer claims, I can't stop thinking that moving from v3 to v4 should bring something more than a single security fix.

I believe my expectations are not really baseless - for most of larger libraries I've used, the next major version is something maintainers plan carefully, consider breaking changes, introduce new features and sometimes do quite massive rewrites. They keep using X.Y.Z versioning and refer to releases as major, minor and tiny. Yet they don't follow Semantic Versioning strictly.

SemVer -> breaking.json?

So I've been thinking what can be done about it and I thought - what if we can mark breaking changes in the repository in another way? Let's say by having one file like breaking.json that would contain list of versions with breaking changes:

{
  "3.5.0": [
    {
      "change": "myProperty function has been removed",
      "details": "https://github.com/MyName/MyRepo/issue/4321",
      "impact": "low"
    }
  ],
  "3.0.0": [
    {
      "change": "myname function renamed to myName",
      "details": "https://github.com/MyName/MyRepo/issue/4002",
      "impact": "high"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

This way when running npm update or some other command I could see what are the breaking changes happening between my current version and the version to which I want to upgrade, with some details and potentially instructions what to do, and then I could decide whether I want to proceed or pick lower version instead. It could be run with some predefine settings - always stop when incompatibility found, or always ask what to do, or print the list of the latest versions I can use without any backward-incompatible changes. There are many possible ways to develop this idea and while it would make updating libraries a bit more involving, it would give developers information about what changes right on their screen.

Pros

  • minor breaking changes could be done in minor releases - potentially authors could be introducing small breaking changes in a few minor releases instead of dropping massive upgrade guide once every 2 years
  • allows to keep major versions for major changes
  • allows user to find out what is the latest version without breaking changes ant update to that particular version
  • this is a bit far fetched: it could potentially allow to override strict dependencies - let's say MyApp uses MyLib 1.5 and MyOtherLib 1.3. I want to update MyOtherLib to 2.0, but I can't, because MyLib depends on MyOtherLib version 1.x; currently I need to ask maintainer of MyLib to bump dependency or fork the library. With this feature I could see myself list of breaking changes and if I feel it's safe, I could force installation of MyOtherLib 2.0 instead.

Cons

  • requires additional support from the package managers: NPM/Bundler/Hex and other package managers would need to approve such feature and maintain the support
  • the dependency management would become more tricky, let's say I'm updating my dependencies and one of my indirect dependencies has a breaking change - what should I do? I don't even know the library, how can I decide whether it's safe to update it or not?
  • requires maintainers to keep a separate list of changes in another file and to keep it up-to-date (unless it becomes part of the changelog?)

Is almost-SemVer the best we can get?

My idea is rather idealistic and probably quite challenging to implement. I'm also not sure whether it would actually improve the situation. Maybe the current way where people mostly follow SemVer is the best we can get and having some breaking changes here and there is the cost of relying on other software?

I'm not sure whether there is any practical way to improve the situation, but I feel adding list of breaking changes that could be displayed to the user while upgrading is at least a step towards more awareness and it could save open source maintainers some time they spend answering the same questions and closing duplicate issues.

Do you think there are any other ways to improve software versioning and managing dependencies?

Top comments (4)

Collapse
 
yoursunny profile image
Junxiao Shi

I hate SemVer yet a lot of tools require it.
Thus, I version my libraries using dates. For example, 0.0.20200923.
In SemVer terms, every patch version within 0.0 is potentially breaking. I'd let the user decide whether to upgrade.
As for what were the (breaking) changes, I post them on Twitter with a hashtag that represents the project.

Collapse
 
gregorywitek profile image
Gregory Witek

I follow something similar for versioning of our closed-source libraries. We need to have release numbers due to corporate rules, but they don't enforce SemVer, so we switched to CalVer instead (calver.org). It works great because we never have to think which number we increase and the version number always increases, so all the version comparing tools work.

It doesn't work very well if you maintain multiple lines of a project. I'm thinking of some bigger projects, let's say Django devs find some security issue. They can add it to the latest version, but a lot of devs still run version 2.6 and they can't upgrade to 3.0 immediately, so Django team releases 2.6.1 and 3.1.2 independently.

Collapse
 
kayis profile image
K

ComVer seems to try things a bit different.

gitlab.com/staltz/comver

Collapse
 
gregorywitek profile image
Gregory Witek

Oh, never heard of ComVer, I like the idea with badges, so simple! I think this is something that could be picked up from the changelog.md file to display warnings in console when updating library, without maintaining a separate file (although then the changelog needs to follow some strict formatting)