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"
    }
  ]
}

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?

Leave your comments here: https://news.ycombinator.com/item?id=24571722