Repository: pyca/cryptography

Issue:
Version Scheme Change
We've talked on and off for quite a few years about our versioning scheme for `cryptography`, but #5771 made it very clear that despite our [versioning documentation](https://cryptography.io/en/latest/api-stability.html#versioning) some users still assume it's [SemVer](https://semver.org) and are caught off guard by changes. I propose that we **switch to a [CalVer](https://calver.org) scheme** for the thirty fifth feature release (which we currently would call 3.5). This has the advantage of being unambiguously not semantic versioning but encoding some vaguely useful information (the year of release) in it.

### Alternate Choices
**Stay the course**
We've successfully used our versioning scheme for a bit over 7 years now and the probability of another monumental shift like this is low. There is, however, a constant (but low) background radiation of people who are tripped up by making assumptions about our versioning so I would argue against this choice.

**SemVer**
Switch to using an actual semantic versioning scheme. Without re-litigating years of conversations, I don't believe either @alex or myself are interested in this option. (See https://github.com/pyca/cryptography/issues/5801#issuecomment-776067787 for a bit of elaboration on our position)

**Firefox/Chrome Versioning**
(From @alex)
"Its merits are that it's technically semver compatible, it's fairly predictable, and it communicates at least a little info on the relative age of the project.

Its demerits are that it probably won't solve the real problem here, which was that folks were surprised a major change happened at all."


Discussion:
In past I have been skeptical of CalVer.

I am now in favor of it.
SemVer would not have prevented the issue. According to SemVer 2.0 definition, the introduction of Rust dependency counts as a minor change.

> What should I do if I update my own dependencies without changing the public API?
> 
> That would be considered compatible since it does not affect the public API. Software that explicitly depends on the same dependencies as your package should have their own dependency specifications and the author will notice any conflicts. Determining whether the change is a patch level or minor level modification depends on whether you updated your dependencies in order to fix a bug or introduce new functionality.

https://semver.org/#what-should-i-do-if-i-update-my-own-dependencies-without-changing-the-public-api

Update: My comment was a reply to another comment that has been removed.
@tiran: To be fair, I don't think your quote fully portrays the idea of semantic versioning:

> If you have a huge audience that will be drastically impacted by changing the behavior back to what the public API intended, then it may be best to perform a major version release, even though the fix could strictly be considered a patch release. Remember, Semantic Versioning is all about conveying meaning by how the version number changes. If these changes are important to your users, use the version number to inform them.

https://semver.org/#what-if-i-inadvertently-alter-the-public-api-in-a-way-that-is-not-compliant-with-the-version-number-change-ie-the-code-incorrectly-introduces-a-major-breaking-change-in-a-patch-release

----

Anyhow - @alex and @reaperhulk seem to have strong opinions here, they have probably heard all the majors arguments, let's focus on something a bit more productive: It seems like quite a few people expect versions that look like they could be SemVer to actually be SemVer. Whether that is justified or not, a scheme that is less likely to be confused with SemVer sounds like a pragmatic and useful improvement to me. I personally like to just bump major versions quite liberally, but CalVer is an alternative as well :-)
Notwithstanding the fact that the comment was deleted, I figured I'd write a few words on SemVer:

The SemVer doc itself is pretty clear that breaking changes are with respect to a documented API, it's not clear "build environment changes" qualify. I think it's a pretty reasonable (though certainly not definitive) that they are not changes to an API in any sense, so you don't need to bump a major version. (Now, I have my doubts with respect to the idea of breaking changes from a documented API, in light of observations such as Hyrum's law. This is why the cryptography test suite actually runs the tests of our substantial downstreams!)

But, if they were, I think we'd actually need to bump our major version pretty regularly -- every time we drop a Python version or an OpenSSL version or a LibreSSL version. All of which happen somewhat regularly (because we support such a large and diverse set of platforms).

Now, bumping major version regularly is perhaps "fine", version numbers are free after all (note to self: find a way to charge money for these). However, if say, 1/2 of all releases as are major version bumps, people are unlikely to accord them the same respect, leading to people being upset when a major version includes a change like this. Of course, they'd be unable to complain they weren't warned, but that's hardly useful. Alert fatigue, normalization of deviance, and other psychological factors are every bit as real as strict adherence to rules when we're considering complex systems failure modes.

With that said, another valid choice I'd propose is the version scheme adhered to by Firefox, Chrome, etc: major version bumps and patch bumps only:

3.5 -> 35.0.0, if we issue a patch release it's 35.0.1, otherwise our next release is 36.0.0.

It's merits are that it's technically semver compatible, it's fairly predictable, and it communicates at least a little info on the relative age of the project.

Its demerits are that it probably won't solve the real problem here, which was that folks were surprised a major change happened at all.
I had made a comment, but then realized I was sent to another issue from a reddit thread that was specifically linked to an archived version to prevent commenting. So I removed my comment to try to follow the spirit of that move and not to add a "piled on" feeling to yall. I think you'll figure something reasonable out without my input.
@ggggggggg I know emotions can get the best of people. I personally appreciate that you considered the situation in choosing to remove the comment, so thank you for your empathy! We will definitely consider your concerns as part of our decision-making process.
For all the tooling involved (e.g. requirements files, setup.py, etc), while none of them prescribe anything more than increasing tuple separated by dots, its kinda useful if everything is on a semvaresque scheme at least

```
a ^= 12.0.0
b ^= 24.0.0
c ^= 42.0.0
```
would have value if it means that I can deploy those dependencies on a system running the same version of the OS I tested against (la a deployment scenario)
> Its demerits are that it probably won't solve the real problem here, which was that folks were surprised a major change happened at all.

People with whom I had the opportunity to speak about it were surprised because cryptography was a transitive dependency. They didn't change anything in their repositories, yet on Monday 9th CIs suddenly stopped accepting their code. Your proposition _would_ actually help here, albeit at a cost of either having projects use outdated Cryptography versions, or creating lots of unresolvable dependency conflicts between different packages (or both).

Whatever you'll come up with, making sure such updates come with the "major" bumped is the only way they won't incite backlash. _Even_ if it isn't necessarily _lege artis_ wrt. the scheme; precise procedures may work nice, but "common sense™" works even better (not that I'm accusing anyone of not having it; it's just a basic fact).

You won't be able to put everything into a tuple of numbers, Tesler's law applies here. No one follows SemVer to a T. Setting the right expectations, knowing what your users expect, and playing along that is IMO the way to go.

> version scheme adhered to by Firefox, Chrome

TIL: the scheme Chrome uses isn't based on CalVer. I wonder where I got that misconception from :smile:.
> Your proposition would actually help here, albeit at a cost of either having projects using outdated Cryptography versions, or creating lots of unresolvable dependency conflicts between different projects (or both).

This would be a substantial negative.

I think it's also worth noting that currently a lot of our dependents don't have _any_ upper version bound pin:
- paramiko
- pyopenssl
- twisted
- aws-encryption-sdk-python
- aws-dynamodb-encrytion-python
- certbot
- josepy
- acme

This means that _no_ version scheme would have prevented unexpected updates beyond a boundary.
> I think it's also worth noting that currently a lot of our dependents don't have any upper version bound pin:

But if you'd bump major, that wouldn't necessarily be _your_ problem then, innit :wink:? In that case, I would consider you extremely kind and helpful if you'd even _passingly_ tried to notify the maintainers / users of these libraries about the upcoming major - as far as I'd be concerned you'd do 120% of what you should, the rest is on the maintainers of each of the projects you mentioned.

I'm not everyone, but I reckon the distribution of sensible people would be in your favor.

FWIW I wouldn't like people bashing on paramiko maintainer, dude has already said that he's burnt out, and I suspect the backlash would have landed on him.
> People with whom I had the opportunity to speak about it were surprised because cryptography was a transitive dependency. They didn't change anything in their repositories, yet on Monday 9th CIs suddenly stopped accepting their code.

As a maintainer of some wsgi software, almost no-one seems to actually pin dependencies properly. Most transitive dependencies are un-versioned and the same people would still have complained. Secondly, most people don't pay attention to the versioning scheme used by projects, and thus if the do pin, the pins are likely wrong (as is the reason why this ticket even exists in the first place)
 > As a maintainer of some wsgi software, almost no-one seems to actually pin dependencies properly. Most transitive dependencies are un-versioned and the same people would still have complained.
 
 How do you know that? (edit: I'm not trying to argue against your point, I'm genuinely curious)
@MrMino the Github "uses" feature to go over various repositories, looking at large companies that use the software, as well as knowing that I've broken peoples software in the past because of changes which bumped the version number (the projects I work on don't use SemVer) removing features that have been deprecated for several versions and thus were already returning warnings.

Second, version pinning in `setup.py` is generally not recommended, so when you go down into the dependency tree it is likely that you will find a pin that is `>=` and doesn't pin the upper version, so that the package can make sure that its dependency meets a certain requirement (or has a certain API) but doesn't limit any potential for a newer version.

I've even been bit by this myself in various projects where a dependency of a dependency didn't correctly pin, and my top-level project didn't version lock.
@bertjwregeer would the conclusion for this be then, that cryptography shouldn't actually bump major in case of disruptive changes?

I guess one could use your arguments against any semantic-esque version bumps.
> Whatever you'll come up with, making sure such updates come with the "major" bumped is the only way they won't incite backlash.

I'm thinking about this as SemVer+, as in "SemVer plus major bumps where common sense suggests" (i.e. _for_ disruptive dependency changes like now with rust, but not for e.g. dropping python versions)
Surely dropping a python version is incredibly disruptive if you rely on
that Python version?

To really hammer this point: our 3.4 release also dropped Python 2, by any
standard a large and significant change. Not one person has complained that
that violated semver.

On Fri, Feb 12, 2021 at 2:49 PM h-vetinari <notifications@github.com> wrote:

> Whatever you'll come up with, making sure such updates come with the
> "major" bumped is the only way they won't incite backlash.
>
> I'm thinking about this as SemVer+, as in "SemVer plus major bumps where
> common sense suggests" (i.e. *for* disruptive dependency changes, but not
> for dropping python versions)
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/pyca/cryptography/issues/5801#issuecomment-778416048>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAAAGBFNC424ZHCWCPXCMOTS6WA3ZANCNFSM4XLGJSXQ>
> .
>


-- 
All that is necessary for evil to succeed is for good people to do nothing.

> Surely dropping a python version is incredibly disruptive if you rely on
that Python version?

I'd say no, because that's a normal part of life in the python ecosystem (especially now that we've left the 2/3 schism behind). Also, with correct package metadata, you simply won't get the newer packages that your old python doesn't support, so you cannot be broken unexpectedly.

> To really hammer this point: our 3.4 release also dropped Python 2, by any
standard a large and significant change. Not one person has complained that
that violated semver.

Well, for once, because your documentation stated that you're not using SemVer, and two, because the python 2 drop was well-prepared, came way after the official EOL, and was well-anticipated.

What I'm suggesting is SemVer + major bumps for _exceptional_ circumstances. Probably nothing comparable to the addition of the rust-dependency (or arguably, the dropping of python 2) is on the horizon that would qualify for what I'd consider "exceptional", but at least it would have made the current situation a tad easier.
Our documentation stating we don't do semver has in no way stopped people
from complaining our Rust introduction violated semver.

My point (hopefully clear) is that the standard you are articulating is (a)
not an objective one, (b) not the one proposed by the semver document
itself.

People's subjective views of what changes are disruptive to _them_ are
driving the requests for major-version-bumps.

On Fri, Feb 12, 2021 at 3:10 PM h-vetinari <notifications@github.com> wrote:

> Surely dropping a python version is incredibly disruptive if you rely on
> that Python version?
>
> I'd say no, because that's a normal part of life in the python ecosystem
> (especially now that we've left the 2/3 schism behind). Also, with correct
> package metadata, you simply won't get the newer packages that your old
> python doesn't support, so you cannot be broken unexpectedly.
>
> To really hammer this point: our 3.4 release also dropped Python 2, by any
> standard a large and significant change. Not one person has complained that
> that violated semver.
>
> Well, for once, because your documentation stated that you're not using
> SemVer, and two, because the python 2 drop was well-prepared, came way
> after the official EOL, and was well-anticipated.
>
> What I'm suggesting is SemVer + major bumps for *exceptional*
> circumstances. Probably nothing comparable to the addition of the
> rust-dependency (or arguably, the dropping of python 2) is on the horizon
> that would qualify for what I'd consider "exceptional", but at least it
> would have made the current situation a tad easier.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/pyca/cryptography/issues/5801#issuecomment-778427145>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAAAGBDSU4XIDIV37O5SY7TS6WDNBANCNFSM4XLGJSXQ>
> .
>


-- 
All that is necessary for evil to succeed is for good people to do nothing.

> My point (hopefully clear) is that the standard you are articulating is (a) not an objective one, (b) not the one proposed by the semver document itself.

I understand, and I'm acknowledging myself that it's not objective. In this case, I think most people would have agreed that adding rust was disruptive enough for earning a major bump despite not being formally covered by the SemVer document. The point is that perhaps this subjective middle-ground is still preferable to following SemVer to the letter (as you [mentioned](https://github.com/pyca/cryptography/issues/5801#issuecomment-776067787)) or not at all (echoing @MrMino's [point](https://github.com/pyca/cryptography/issues/5801#issuecomment-776960516)).

Said otherwise, there will never be 0 complaints about a disruptive change, but it can still be a valid tradeoff to "subjectively bump" the major version (with the SemVer-esque implication that something bigger-than-usual is happening) compared to unwittingly breaking people's (admittedly suboptimal) setups and the reactions & frustrations on all sides that follow.
As a casual exercise I went through our changelog and I roughly estimate we'd be at version 24 right now based on the "semver but also major bumps for things we think might be disruptive" metric. Given that we've done 34 feature releases nearly 71% of all releases would be major version increments.

I don't believe changing our versioning scheme can fix the ultimate problem here (which is really rooted in a variety of historical ecosystem challenges around pinning tooling and the relatively recent rise of CI systems that can automatically bump those deps), but we can at least make it clear that **in general** a feature release for us is likely to contain the end of a deprecation cycle or some other potentially incompatible change. This makes me lean towards the Firefox versioning scheme Alex proposed, where our thirty fifth release (and the first one **requiring** Rust) becomes 35.0.0 instead of 3.5.


> [....] based on the "semver but also major bumps for things we think might be disruptive" metric.

Thanks for giving this a shot. I don't have a horse in this race, I was just trying to suggest a middle ground; I won't belabour the point further.
FWIW, even though I don't agree with you, arguing with you has helped
me clarify my thinking.

 We exist in the quantum superposition of "lots of releases
technically break stuff (such as by dropping Python or OpenSSL
versions), but we're super conservative about what those things are
(for the most part we don't drop things until their usage numbers are
very low) so in practice you almost always want to just upgrade". This
type of rationale doesn't fit into a number.

On Fri, Feb 12, 2021 at 3:37 PM h-vetinari <notifications@github.com> wrote:
>
> [....] based on the "semver but also major bumps for things we think might be disruptive" metric.
>
> Thanks for giving this a shot. I don't have a horse in this race, I was just trying to suggest a middle ground; I won't belabour the point further.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub, or unsubscribe.



-- 
All that is necessary for evil to succeed is for good people to do nothing.

BTW. if you change `requires-python` is it still disruptive? It would only mean that some of your users won't see the update. Am I missing something here?
The ``requires-python`` feature only works with recent pip and setuptools. Users may be running an old pip that does not understand the flag.
> The ``requires-python`` feature only works with recent pip and setuptools. Users may be running an old pip that does not understand the flag.

Thanks, I haven't considered it. Although it's supported since 9.0.0, which was released a month before python 3.6. 

Do you support earlier versions of pip, even those that predate lowest python version specified to be supported by cryptography?
We aggressively encourage people to update `pip` (and we require quite new versions for wheels in some cases), but since it's how people install our package we have some limits on our abilities. 😄 

This discussion shows another example of the challenge inherent in an objective measurement of major changes. If we assume we support `pip` greater than or equal to that which existed at the time the versions of Python we support were released (possibly feasible now, but not a possibility when `cryptography` started) then every Python version drop until present would have required a new major version. (Although we wouldn't need them for future releases)
Thank you to everyone for the discussion around this. It has been helpful! We have decided that the project will be switching to Firefox-style versioning for the next feature release. This release will be the first one to hard-require Rust (as a reminder in 3.4.x it is possible to disable it with the `CRYPTOGRAPHY_DONT_BUILD_RUST` environment variable) and will be versioned 35.0.0.

No decision here can really solve the underlying problem, but this new versioning scheme has the benefit of being SemVer compatible so users who assume SemVer will not be unexpectedly broken. It has the **disadvantage** of potentially introducing security issues for users who are not diligent about using tools like [dependabot](https://dependabot.com) to automatically keep their pins up-to-date, but given the feedback we've received this is still an improvement. (It is notable that a more traditional SemVer would still have this issue, albeit slightly less often. https://github.com/pyca/cryptography/issues/5801#issuecomment-778439532 contains some relevant statistics)

Resolving patch:
diff --git a/src/cryptography/__about__.py b/src/cryptography/__about__.py
--- a/src/cryptography/__about__.py
+++ b/src/cryptography/__about__.py
@@ -21,7 +21,7 @@
 )
 __uri__ = "https://github.com/pyca/cryptography"
 
-__version__ = "3.5.dev1"
+__version__ = "35.0.0.dev1"
 
 __author__ = "The Python Cryptographic Authority and individual contributors"
 __email__ = "cryptography-dev@python.org"
diff --git a/vectors/cryptography_vectors/__about__.py b/vectors/cryptography_vectors/__about__.py
--- a/vectors/cryptography_vectors/__about__.py
+++ b/vectors/cryptography_vectors/__about__.py
@@ -18,7 +18,7 @@
 
 __uri__ = "https://github.com/pyca/cryptography"
 
-__version__ = "3.5.dev1"
+__version__ = "35.0.0.dev1"
 
 __author__ = "The Python Cryptographic Authority and individual contributors"
 __email__ = "cryptography-dev@python.org"


Test patch: