RFC: give up on SemVer

I am opening a new topic because I do not want to sidetrack the one about main(). This is just the latest of “minor breaking changes” that are technically breaking, yet practically harmless and easy to deal with.

SemVer has become a de facto standard for standardize software versioning, especially in FOSS. It is widely understood, practical, detailed. It is just perfect for packages and libraries.

What I am arguing here is that it is not well-suited to a complex interactive language like Julia. It is practically impossible to design a modern language and a closely related standard library in advance, and minor changes will always be necessary. It is not practical for Julia to follow SemVer, and in practice it does not — if it did, the version number would already be higher than 10 because of tiny but technically breaking changes.

We should be upfront and explicit about this, instead of pretending that we stick to SemVer.

What should Julia promise instead? I am opening this discussion to work that out, but my basic proposal is that as long as the major version number does not change, breaking changes will be

  1. infrequent, and only happen for minor releases :white_check_mark:
  2. announced in a prominent position in the release notes,
  3. relatively minor and cosmetic :white_check_mark:
  4. checked for registered packages, the authors of which will be notified in advance :white_check_mark:
  5. easy to locate and potentially update with tooling,
  6. easy to work around so that all versions can be supported :white_check_mark:

I put a :white_check_mark: for those requirements we already satisfy in practice.

So in practice, while there will be minor, technically breaking changes, Julia will remain stable for the majority of end users. You won’t find your arrays indexed from 42, or LinearAlgebra renamed to MatrixThingies.

Packages still stick to SemVer, no change there.

2 Likes

Would it be possible to only have breaking changes in the LTS versions?

Would it be possible to follow Rust’s model of “stability without stagnation”?

I like it more as it is now:
Sticking with SemVer and knowing that Julia developers are doing the best they can to avoid breaking changes and having now and than some exceptions to this rule.

One step further, I would also be fine with, if there would be planed and communicated breaking changes if they would benefit the further evolution of Julia in a major way (not only some convenience things).

Dropping SemVer completely would encourage breaking changes for minor benefits. Not that I expect the main Julia developers to do this, but it may raise the pressure to do e.g. from the community or from some inverstors.

I am more happy with the (current) lighter promise to avoid breaking changes whenever possible, but break it for a greater benefit.

Of course such a modus operandi will result in some repeated discussions here in future, but every single decision in this way in the past was quite reasonable.

1 Like

I too dislike the cavalier attitude around breaking changes, but I’d rather see the Julia project take them more seriously, instead of being even more “eh it’s breaking anyway” about it. The way a language is developed informs how the language is used and its ecosystem develops. Saying “Julia doesn’t use semver anymore because it’s too much of a hassle to actually follow” would be extremely detrimental to continued development & adoption.

15 Likes

Its funny, I just wrote an article on the Forem, that quoted a video, that is totally fitting here as well.

2 Likes

This video is more than an hour long, don’t you have a time stamp or a link to your Forem post?

1 Like

Does the Julia community have a PEP-like mechanism for core Julia developers and key stakeholders to vote on important features like this?

1 Like

No, as a release is marked as an LTS only after a subsequent release has been made.

1 Like

I think that’s a terrible idea. There is a reason that Julia has pledged to not break existing code in v1.x, and that there is no plan for the timeline of Julia 2.0, as keeps being reiterated. There is a reason that at least all the major languages I know about which has been released the past 15 years have all pledged to not break any code in version 1.x. That reason is that people don’t want to use a language where their code keeps breaking.

  • It is so infuriating for your code to constantly break that people will just stop using Julia
  • People will - quite reasonably - not use Julia to begin with for larger projects because they know any code they write will require constant maintenance even to continue working. For example, I would not use Julia for a research project where I was employed temporarily on a grant if I knew my code would break after my employment had stopped and I was not around to maintain it
  • The ecosystem will likely split according to Julia version, as large parts of the ecosystem stick to the LTS releases to avoid the hassle of upgrading
  • The detrimental effect will be self-sustaining: If I only use LTS and many of my users only use LTS, and I can’t guarantee that my code will keep working in the next minor Julia release, it’s prudent for me to cap my Julia compat to the current minor release (or the LTS!). Now, even if my package didn’t break in the last minor Julia release, the package still cannot be installed due to compat reasons.

What should we do instead? Stop breaking people’s code, and be more explicit about what is guaranteed behaviour. Right now there are so many gotchas and bizarre rules lawyering about what is technically not breaking that it’s practically impossible to avoid your code breaking.

20 Likes

This video is a lengthy rant against SemVer (perhaps against any versioning scheme).

The main argument is: even if you try you can’t hold your promise not to break anything. The alternative provided is not to change (e.g. functions) but to rename (means: don’t change a function/module/package/… and possibly break something elsewhere, but to create a new name for the improved functionality, e.g. foo2() ).

My arguments against this rant:

It’s a false dichotomy. You can use SemVer and do the improvements in a better way than breaking things (even for main version changes). Still you may sometimes break something anyways.

The premise of this rant is: not breaking the promise of not breaking anything while still improving the software is an absolute. But building up arguments on absolutisms doesn’t help in the real world. The promise “unless major version number we will never break anything” is clearly overemphasized and should not be used as an absolute. It’s should be understood as “we try as hard as we can” and this is enough to expect.

Two scenarios are completely left out by this talk/video:

  1. How do users/consumers should complain about software errors to the developer, if there is no version number or something similar, to be specific in which realization of the software the error was experienced?

  2. How to address important changes like security issues? These type of changes have to be breaking for the benefit of everybody. Of course, they can be breaking with or without versions, but how to talk about this, which is again 1).

All in all the talk/video did not convinced me.
SemVer does not solve anything, but using it is better then not using it. Even if it fits not perfectly for like Julia main development. Still, being as good as possible is better than nothing. But, on the other side: SemVer should not be a barrier to important improvements, if it is important, break it.

Like in every complex real world scenario there is no perfect solution and no cure for all problems and all people. This can only be addressed by good communication.

@Tamas_Papp 's proposal points 1-6 do not solve the issues with future breaking changes because the breaking is not foreseeable as easy as implied. 4 of them are already fine now with SemVer, only 2 missing points which are probably not solved without SemVer and could be tried to be addressed still with SemVer. So dropping SemVer doesn’t help in any way here. Point 2 and 5 remain to be solved but the concrete way is still missing, e.g. if it’s not that easy to foresee the breaking.

1 Like

That’s not at all his argument, and he even says that this is obviously absurd. His core argument is framing the discussion not around “change” but around “what is required/what is provided”. From those definitions “breakage” is simply “requiring more” or “providing less”, nothing more.

That’s easy to say, but a lot harder to do. Where many of these “minor” subtly-breaking changes have occurred is in trying get those more explicit guaranteed behaviors defined.

4 Likes

Ok, but what’s the alternative? Simply not providing guaranteed behaviors? Having breaking releases all the time?

It’s time to be honest with ourselves and accept that the current situation is not good at all, and giving up semver entirely will NOT fix that - it will make this even worse.

2 Likes

You cannot have your cake and eat it — if you are willing to break the API; for whatever reason, you are not following SemVer, even if you claim to.

The alternative is breaking API only when you really need to, in very minor ways, making it very explicit, investing in the infrastructure to assess the consequences (PkgEval), and providing tooling to make the transition as smooth as possible.

That’s a straw man. By definition these things would be super-rare, and only done when absolutely necessary.

It’s not a straw man. By your own admission, we’re currently having breaking releases all the time, so these situations are in fact not “super rare”. The only way to reduce breakage is to be conscious of it, and accept that some things just can’t be done in a non-breaking way, without keeping the old way around and properly deprecating it, all while giving actual guarantees about what is required and what is provided by some functionality (which is the core argument that Rich Hickey is making in the linked keynote).

The fact that Julia currently lacks the facilities to express “what is required/what is provided” is EXACTLY the core issue here. Heck, I’d be fine with having this written down in a docstring, but we’re not even doing that!

2 Likes

I am not sure this is the case, but I will not listen again for over an hour.

But lets assume it is as you say, it is still lengthy without much alternative given. Still a false dichotomy, still not really good arguments against SemVer (only against its strong promise). Still missing important points.

Yes, you are right, obviously.

Still I would prefer having SemVer in place and I would be happy to live with the risk, that at some point in time there is the need to break those promise made, because the reason is important enough (and communicated and discussed enough).

I am absolutely sure that the main Julia developers will not easily do this (if ever).

Breaking changes by accident are not addressed by dropping SemVer nor by sticking to it. They just happen. So I choose to stick with SemVer because it solves other problems.

1 Like

You make a valid point, so let me rephrase: changes to Julia which could have been considered technically breaking for real-life code have been so minor that in practice they do not matter much, especially because their impact has been assessed before merging them. For practical purposes, the magnitude matters more than the frequency.

I think that in practice that is impossible — nontrivial programming languages are way too complex to be described by a formal API. Corner cases and undefined behavior crop up all the time, even for relatively mature languages. Eg the archives of comp.lang.lisp are full of these, even though a lot of effort went into making the language an ANSI standard.

2 Likes

I think this is an important statement, which may lead to improvements.

The truth is: for some they are super rare, others have breaking changes all the time.

At least for me they have been super rare, none in quite a while.

Why is that?

I am not a heavy user of some of the outstanding packages out there. I don’t use Makie, I don’t use DataFrames much, I don’t use machine learning or differentiation stuff,… and more great packages I don’t have use for. My own packages don’t have any or have minimal dependencies. I never use internals.

But what I often see, when I install some package or update it, mainly to find an answer to a question here, what I see is an exorbitant list of other packages installing and updating, like over 200 packages nearly all the time I do this. Pkg shows me the upgrade/install progress by this fancy list of packages involved.

In all cases it’s doing well and after a while I can look for a solution to some question.
(Here not talking about TTFX and other problems which happen with Pkg sometimes)

This, I would argue, can be named as dependency hell. It’s not hell, because it works very well. Except, you guessed it, chances are very good now, that at some point of the dependencies some internal has been used and breaks.

This is, in my opinion, the reason for the current state of things.

Interfaces and other solutions have been discussed already and should find it’s way into V1 of Julia. Of course this would be breaking but could be the last breaking change (for accidental breakings based on usage of internals).

2 Likes

I have not thought this through, but I imagined we could version individual functions.

Screenshot_20230905_204651

That would work similar to simply renaming them by creating new functions, but we would have semantics to actually deal with it.

It could be referred to it, and become more structured, like in the import statement.

Screenshot_20230905_205426

And a library that out phases a function, could use this functionality to signal a change, by increasing the version number for the function, and an empty import statement would always use the most up-to-date version automatically.

So the default workflow would be, that a user will be notified about the changed function, and could either adopt, or simply define the previous version in the import.

I guess it has some benefits, when we target the functions with versions, and not the whole entire library.
I might have some logical fallacies here, this is just the result of me brainstorming while I was watching the talk.

Another thing, that is at least helping to get SemVer a bit more reliable, is when we automatically enforce it, like Elm does.

2 Likes