Stop Abusing Semver
samver.org
samver.org
That said, personally, my most hated anti-pattern (outside of not properly tagging broken interface versions), is people refusing to tag a 1.0. In theory, this allows folks to continue breaking the interface without calling it a broken interface. In practice, these packages are already widely deployed and NOT tagging a major version completely invalidates the versioning mechanism all together.
The versioning scheme cannot do its job unless if you use all three fields. So release a damn 1.0.0 already! Your software will never be perfect. it's ok.
If you're demanding that package managers tag a 1.0.0 version, you're either demanding that they make their product stable now, or you're demanding that they lie to you.
It's not like it's going to be more stable if the authors are forced to mark it 1.0.0.
At least authors are honest that their goal at the moment of release is not stability but their freedom to break it every release if they see a reason for it.
In particular, what the signal says is, "We aren't yet ready to think about or support the needs of someone who's trying to use this in production."
Which, if the project is also then pitching the software as production-ready, is a terrible mixed message. Maintainers really should pick one story and stick with it.
> Which, if the project is also then pitching the software as production-ready, is a terrible mixed message. Maintainers really should pick one story and stick with it.
Yes, that's true, but you've got to realize that these messages are often coming from different teams. Versions are more likely to come from engineers, whereas pitches are more likely to come from sales. As an engineer, version numbers are one of the few tools I have for telling the truth when sales is lying. Engineers generally don't want to promise stability that isn't fully-formed, because if a valuable enough client starts using it, then we end up supporting some horrible half-baked interface until they decide to upgrade.
In that context, when you see version numbers disagreeing with marketing, don't point the finger at semantic versioning as "refusing to tag a 1.0.0 release". They're the ones telling you the truth. Point the finger at marketing.
In a larger sense, the sooner you start viewing all marketing as untrustworthy and basing your decisions on evidence instead, the happier you'll be. Version numbers are a small signal. Actual trial periods and getting to use the software should be the real deciding factor. And don't make the mistake of turning a trial period into a commitment without actually evaluating whether the software does what you want it to.
I think in the case of OpenSSL, there was a huge need for what OpenSSL was doing which justified a lot of people taking a calculated risk to use it. I'll concede in that case that the risk may have actually even been justified.
That same logic doesn't apply to blahblah.io's "revolutionary" 0.x.y packages on npm or pip, or the bleeding-edge SaaS of the week.
At a 1.0.0 you expect the authors to not change the API for what ever the API is for the thing. Your interactions with the software have an expected level of stability and authors don't need to take into account prior to that.
Stable means no changing or firmly established. Once you tag a 1.0.0 you say that some things will no longer change. Hence, an element of stability.
If you're depending on the software in your dependencies, you can mark >1.0.0,<2.0.0 and if people are correctly doing semver, your software won't break. When you need to move to another major version, you'll need to ensure your usage hasn't broken with the change.
Other people's poor decisions do not confer any obligation on the person doing the versioning.
It implies that all releases are potentially equivalent to semver-major releases, but that doesn't invalidate the mechanism. It means there is no such thing as a safe upgrade, though.
(I think it's a mistake that semver doesn't didn't keep patch versions as having the same semantics in pre-1.0 releases as post-1.0, though.)
Or "the available tooling isn't good enough to make strong promises about API stability, and I'd prefer not to lie to you".
The difference is that someone doing that on semver at least may cross the threshold into sanity one day, or is signalling that it's possibly in their roadmap. Seems like a notch better than the rest of software.
But I think your frustration makes more sense reserved for the 99% rest of software.
And that IaaC is about the I rather than the C.
HCL seems to be a language designed to avoid deadlocks, similar to eBPF, so the "code" of it is weird.. but I'm not sure I fully understand this comment otherwise. :S
Then, to be blunt, you are not following SemVer
> Software has milestones. Lets make them obvious.
SemVer is about signalling to clients what expectations they should have by being able to look at your version number changes. If you don't follow it then you are confusing your clients.
> If from 0.0.1 to 0.7.4 you had like 5 major changes but nothing was broken API wise those should of been 5 major releases...
Anything <1.0.0 is already, by definition, subject to any change so your example is a bit off.
However SemVer, in my opinion, is primarily used for libraries that are part of systems of dependency resolution.
If you have a desktop app used by end-users then I wouldn't recommend using SemVer, just do what marketing requires.
Notable offenders listed here: 0ver.org
(Not 100% sure it isn't sarcasm, too.)
My software isn't version 0.x because it isn't stable. It's version 0.x because there's never been a breaking change necessitating a major version bump.
https://semver.org/spec/v2.0.0.html#spec-item-4
"Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable."
There is no code change that makes 0.x.y turn into 1.0.0. This is covered in the FAQ
https://semver.org/spec/v2.0.0.html#how-do-i-know-when-to-re...
It is a case of something vaguely along the lines of "0.3.5 has been running in prod for X time, I am now confident, I will now tag that same code as 1.0.0"
I always assumed that if you were doing Semver right, then you could end up at 0.124.2 or something. Glad to learn that’s not the case.
Say libfoo 0.4.1's furbinate() function draws a pretty picture. When libfoo 0.4.2 comes out and someone calls furbinate(), they may be surprised to have all their files deleted and their screen covered with filthy Latin poetry, but it is allowed per the semantic versioning spec.
Something is missing from this discussion, ninja edit?
If the software had a stable API at that point, but they were still using a 0.x.y version number, then they wouldn't be using Semver properly, since 0.x.y versions are for unstable initial development releases.
I thought with semver if you never did a major breaking change then you had to keep the major version 0 forever.
As an example: Lets say I have an open-source library that I am developing. It is currently version 0.x.y and someone depends on it (without my knowledge because that is usually how it is).
Is that now in Production? Should I be forced to make it 1.0.0 even though I am still making large numbers of breaking changes?
That is a simple example off the top of my head. I am watching https://www.youtube.com/watch?v=tISy7EJQPzI (Linked from another comment) and he goes into good details about issues with Semver.
Yes, but then your next version could be 1.1.x and might even include the odd interface/API changes without feeling at all guilty.
I think geofft sums it up best elsewhere in this thread where it is about interface stability guarantees being the deciding quality.
Whether that correlates with production is up to the engineering team putting it into production. If they budget for dealing with incompatible interface changes when they want/need to update and they don't have automated untested updates of the dependency, it is entirely reasonable to decide to depend on 0.x in production. In turn, it's entirely reasonable for the owner of the dependency to say, this is fine to go into production (it's stable, secure, feature-complete, etc.), I just have a couple of small planned changes to the interface so I don't want to commit to it yet.
Another way of thinking about this: semver doesn't say anything about the current version of the code. It says how that code compares to past and future versions.
If you’re saying your software is stable at 0.x, just drop the zero. Otherwise you’re likely giving the wrong impression.
What semver is meant to symbolise is ä that minor and patches from 1.0 forward will remain stable and up until 2.0 during which breakage is to be expected. This means that whatever your project status was at 1.0 should function the same at the minimum level on all versions beginning with 1.
This couldn't possibly work if you start counting breaking changes from 0 because right after your first lines of code it's already breaking. You'd have to bump major versions every day.
I version the API with semver. If I add a new optional field, that's a bump in minor. If I add to a list of enumerated strings, that's a bump in minor.
But if I change my internal database schema, that's a change in my deployed service without any change in the API.
I use date-based release IDs for the service, because it marks a point in time of my development.
So release 2020-07-29 of MyService might support API versions 1.0, 1.1, 1.2 and 2.0. Clients using 1.2 are backward compatible with 1.0 and 1.1. Clients using 2.0 are not.
2020-07-29 is useful to SREs running MyService. It's irrelevant to the clients unless it changes that list of available semver'd API definitions.
SemVer is very particularly about versioning implementations of the API, not just the API itself. Only SemVer major and SemVer minor are about the abstract API, SemVer patch is about implementation changes that correct bugs in the implementation of the API, and the optional build part of SemVer (if it changes without any higher level change) is about implementation releases that have no impact on the behavior specified in the API.
You can obviously use SemVer-ish versioning for APIs, but a major reason for SemVer is for automated tooling that relies on product version numbers and doesn't separately understand identifying APIs and their version numbers as implemented in the products.
Semver does not take into account ABI compatibility, which might also have a role if you version a interface with it.
I don't think SemVer is about choosing which version to use; it's about being able to plan the impact of upgrades. So the only requirement really is for your users to be able to actively choose when to upgrade.
Which does still mean that breaking your API should be avoided: all a major version release does is communicating to your users that it is likely to be more work than a non-major upgrade, not actually lessen the work.
See also https://vincenttunru.com/semver-explained
I also don't buy this:
> Semver throws a complexity wrench in what would otherwise be straight-forward automated releases.
SemVer is part of proper communication with your users. So is a proper changelog that explains how a release will impact them. If you have a properly updated changelog, defining the scope of the version upgrade is trivial; if not, you have the task of updating the changelog as a wrench in your release anyway.
This follows from the preceding sentence, emphasis mine:
> Another small note, when dealing with automated CI/CD pipelines, the developer must always specify the semver to the automation.
That needn't necessarily be at the time of the release, btw. If you have a consistent process to update your changelog, then if you do have some completely automated release system that just releases every x time, it can simply look at the changelog to determine what version number to give it.
Distro maintainers have always been doing something like Semver, with their own “compatibility versions” distinct from the upstream’s versions. Semver is basically just a push to get the upstream developers to do the distro maintainers’ work for them, by versioning things the way the maintainers would.
Note that this has little to do with language package ecosystems in the constraint-resolution / bundling / locking of deps sense. Rubygems et al get frozen at build time (and as such, don’t need Semver — see how long Golang got along without module versions.)
Semver is instead for version negotiation at runtime (or “ops time”, if you prefer): it’s for resolving symbols using dynamic library loading; or for resolving which version of a daemon to install, to be compatible with a given client library’s wire protocol ABI level.
And for those use-cases, Semver is pretty much essential. If you’re not doing it for your package, someone else downstream of you still has to.
An “automatic” update of a language-ecosystem build-time dependency package, is still curated by your (not usually fully-automatic) release-management process, like any other codebase change. With full CI/CD automation, some bot (e.g. https://dependabot.com/) notices that a dep has updated, and “proposes” a PR that updates your project’s lockfile; your CI buildbot then notices the new PR, and tries building that branch to see whether it compiles and all the tests pass; and, if everything looks good, it merges the PR into the main development branch. Even then, it’s now just merged into the ‘develop’ branch, and is not necessarily going to be a part of a cut release yet. A CD bot may put it into a QA environment from there; divert a percentage of traffic to it (ala https://github.com/github/scientist); and then maybe eventually make the call that it’s not hurting your metrics, and so switch the traffic fully over to it. But more often, there are usually humans in charge of that final cut-over switch, whoget to make a final call, before this kind of “automatic” update fully hits prod.
Whereas, with OS packages, there’s no CI/CD pipeline — the version number on the package (coupled with your auto-update config) is the “last line of defense” standing between the package and your production system. You can set up a mirroring QA environment that gets updates first; but this is effectively just “smoke testing” — you don’t really get to “run all the tests” for your entire OS and application layer over again, in light of e.g. a new libc, to “prove”—at least in some minimal sense—that things will be fine in prod. You just deploy the configuration, and observe that it’s “working.” Maybe you will have metrics on OS log volume or something similar; but OS package updates often introduce new spurious logs that you don’t care about, so this is a bad metric to track.
(Of course, you can do the QA traffic-splitting thing for your OS-package updates as well... but this means slowing the cadence of OS updates / bundling updates together so that you can gather enough data to say something about whether each new update-bundle is “working.” And if you bundle enough updates together, you’re effectively taking the reins of the OS from its maintainers, with the OS becoming just another part of your release-management process, i.e. with you effectively cutting whole-VM-image “releases.” Which somewhat removes the key advantage of automatic updates — especially automatic security updates, and especially especially automatic kernel security updates [ala https://ubuntu.com/livepatch]. You make yourself vulnerable to these discovered attacks in the interrim, in the name of stability.)
When you think about it, as long as you’re operating in “full paranoia” mode in both cases (as described above), then for the “build-time dependency” packages, all the stuff the CI/CD pipeline does kind of obviates versioning. An incompatible new version of a dep that breaks your software, will just be caught during the regular CI process. You may as well use the sort of timestamp+gitsha versioning that the OP proposes; nothing would really change. It’s only in the OS package case, where semver is getting you a benefit you wouldn’t otherwise get just from good release hygiene.
What? What was the problem?
I often use git describe + tags + $(wc -l release history) + date + version as I thought appropriate. Then I expose these strings at key places. x.y.z is fine, do that, but we also don't need to be so precious with our bytes now. A 30 byte version string is totally fine.
From my perspective, things should be as nearly replicable as possible for bug tracking and fixing. The current version (as in what explicit almost reproducible build) some random person is using should be easily discoverable
When things break, I document it using these numbers. When things are added, I document it using these numbers.
When tests fix defects, that as well.
Defects should have a story. When they came in, when they went out, what was affected, etc.
I view them more as narrative checksums bringing sanity to the moving goalposts of any project.
A competent person should be able to glance at it, see things in order, consistent, and well described and not be confused.
That's the real purpose of this stuff. Anything less is failing.
The marketing number is just that. It's why slackware skipped a bunch. For that, Ubuntus YY.MM system is the clearest one I've seen. 14.04 tells you exactly when it was released, dates are kinda magical like that.
As long as it maintains total ordering. The primary job of a version number is so that you can compare two of them to tell whether one version is behind or ahead the other one (and, preferably, by roughly how much).
I've seen people using VCS commit hashes as version numbers, which defeats the purpose of having such a number.
The stated problems are caused by breaking API changes. The author says it clearly: don't break your API.
Now, how does a different version naming convention change anything? You still have breaking changes, just with a different name. And I'm not sure at all that dates make it any easier to spot breaking changes.
There's this very illustrative talk explaining why breaking API changes are such a bad thing. https://github.com/matthiasn/talk-transcripts/blob/master/Hi...
> Make a new thing, name it something else, figure out the migration that makes sense for you and your users.
If you follow that advice, you don't have a broken API and the rest still follows.
• Most often people break semver by accident, so they wouldn't know when a release should have been "a new thing" instead. You can't replace QA with a versioning scheme.
• Hyrum's Law means you can't really change anything without risk of breaking someone somewhere. It's hard to know what can you change without breaking someone. If you try to never break anything, it leads to absurd trade-offs (every release becomes a thing you support forever).
• And when APIs are broken intentionally, it may be unavoidable necessity. Sometimes some external dependency changes (OS, hardware, other APIs, 3rd party vendors, laws) and the breakage has to be propagated through. Sometimes APIs reach their scaling limits and can't support increased amounts of data (in library APIs you may have O(n^2) interface, in web APIs you may have exposed something that requires full DB sort or scan, in binaries you may run out of bits in field sizes).
It's much more effective to focus on a single thing (breaking change rejection) than to pretend that one thing can be magically accomplished through another.
https://m.youtube.com/watch?v=tISy7EJQPzI
TL;DW: By Hyrum’s law, at sufficient scale, there’s no such thing as a non-breaking change (including changing comments). Embrace that, and test sufficiently that you can always take the latest dependency but detect when it breaks.
I don't disagree with you that unexpected breakage can come from the strangest things, especially as systems get larger and more complicated.
That said, that's not what SemVer is about. SemVer is explicitly about public APIs. It's about communicating from upstream to downstream what level of changes they should expect from the new version. Breakage caused by unintentional bugs, badly designed clients making incorrect assumptions, and things that fiddle around with non-public interfaces is all out of scope.
If I have an API that returns a named array as part of the response in version 1.0, and I add a few new entries to that array, that's version 1.1 to me. It should not break anything. It certainly can break something, for example if a naive developer decided to reference parts of the array by index value or just iterate through and assume they're always in the same order, but that does not mean it should be called a breaking change.
From the perspective of SemVer, a non-breaking change means it will not break a client that correctly implements a public interface. If we have to care about incorrect implementations and non-public interfaces then it all becomes meaningless.
So in your mind because bugs exist there is no purpose in communicating whether an update intentionally changes things? I can not see your logic.
Bugs will always exist. No meaningfully complicated software will ever be bug free. By your logic we should just never upgrade anything? Except that there are time-based bugs all over the place, so not upgrading anything is also guaranteed to break things (2038 anyone?)
No change no matter how small should give anyone confidence that something won't break. I don't think it reduces the value of semver though. It's just another attempt in a long line of attempts to categorise changes into something quickly digestible and it does that well enough.
Huh? I didn't watch, but practically speaking, how do you break something by editing comments? Let's assume it's an actual comment and not a shebang line or something.
Does your language have exceptions? Do the exceptions support stack traces? Do those stack traces have line numbers? Well then...
// old code: version 1.2.3
if(problem1) throw new Exception();
if(problem2) throw new Exception();
Meanwhile, we have client code that catches the exception and then inspects the line number to see whether it was caused by problem1 or by problem2.(You may say that this is crazy and no one would do such a thing. Maybe so. But this at least proves that it's theoretically possible. Also, you'd be surprised at what some people will do.)
Anyway, we upgrade to the new code:
// new code: version 1.2.4
// Helpful comment here; no other changes.
if(problem1) throw new Exception();
if(problem2) throw new Exception();
And of course this comment messes up all the line numbers, at which point we can have anything from misleading error messages to system meltdown, depending on what the client code used the line number for.More on Hyrum’s Law: https://www.hyrumslaw.com/
SemVer is a claim about the public API, not the total of behavior you could potentially observe and react to.
I really struggle to see any meaningful downside in using semver, even if you don't do it semantically, if all you really need is some ever increasing number that is trackable through your system, it does that job as well.
It erodes the social contract of semver.
Without a common understanding and trust of semver you lose the ability to have an educated guess on whether 2.4.3->2.5.0 is an API-breaking change. Naturally, you cannot truly rely on this anyway (and dependency versions updates need to be tested), but being able to have a general idea about your dependencies' API breakage at a glance is a great help.
This becomes even more important in the case of transitive depdencies: something that might be drive-by bumped by your direct dependency (satisfied by the fact that it's only a minor release bump and _their_ use doesn't break) might actually cause your direct use of that transitive dependency to break.
It seems to me that this same information could be communicated by combining the minor and patch versions into one number, and changing the name of the library rather than changing the major version number.
Muddle that with minor revisions and that information is lost. Minor revisions, in addition to signaling new functionality, also signal the possibility of the introduction of new bugs.
I believe the point is that even if you follow the recommendations to the letter, and do everything correctly and never even accidentally change API behaviour between minor versions, anyone using your product still cannot be sure that that is actually the case.
When I upgrade a piece of software, can I rely on the version number actually reflecting the compatibility? Of course not. As long as even a minority of software doesn't follow these recommendations (and to be realistic, they never will) you can never actually rely on these numbers for anything useful in the general case.
In the special case, sure. Some vendors have very strict compatibility guarantees and that is really useful. But to take advantage of these guarantees you need to read their documentation on the topic, and if you read the documentation it doesn't really matter what versioning scheme they follow.
https://pypi.org/project/better_exchook/
Current version: 1.20200318.213331. The date/time is taken from the latest Git commit. Via this code:
https://github.com/albertz/py_better_exchook/blob/2c106820ea...
If I need to break the API, or want to change the versioning scheme, I can just increase the major version number.
For logging purpose, I also append the Git hash, and maybe "dirty", like this:
https://github.com/rwth-i6/returnn/blob/7fa1f3e0595247c035b2...
>Are you releasing software to other developers who are making concious choices on when and how to depend on your software, as part of a dependency tool (like ruby gems) across an organizational boundary? Then maybe use semver.
And earlier:
>Do your users even care what version of your software they're using? Or do they just want it to work?
Clearly semver has useful information imbedded into it, which a date+shortsha doesn't, but the big take away from Sam is that there's a good chance your version number doesn't matter to your users. Marco Arment (ATP, Overcast) spoke about this on Under The Radar[1] but, as an example, semver for an iOS app is meaningless for the typical iPhone user.
I personally use a date+sha versionish on a project because there's only one way to use the software. The date tells users when anything got added or broken, and is useful for communicating updates; the sha is mostly for me.
This might work for a web page but an app... an app in any kind of environment that tries to control for security (like some large corps)... likely not.
And, for server side software.... yeah, not going to do that.
This whole thing doesn't work well for shipped software.
If you don't like semver for shipped software... there's always something like https://calver.org/.
Then there's all the different versions of stdlibs, language, etc. etc.
We should actually give up and fix our dependencies. (Maybe not for libraries if you can 'recompile' as needed, but...)
From my perspective both schemes provide useful information but for different purposes.
Going from "2020.05.04 => 2020.06.20" doesn't tell me _anything what so ever_ about backwards compatibility or risk that I'm taking on. Is that the equivalent of going from "1.x to "2.x" or "1.x" to "8.x"? Who knows.
Over the course of 5 years and several thousand bug reports from end-users and 2nd/3rd line support I've never once seen a bug report that mentioned a version number. It was always "the version that was deployed last week" or something to that effect despite the version number being visible in the app at all times.
For APIs and libraries semver makes a lot of sense, I agree, but not all code is APIs or libraries.
For APIs or libraries, yeah, semver is cool and dandy, but not all code is APIs or libraries.
Take classic java for example. If you have a public interface adding a method can break someone's client code because nothing can keep them from implementing that interface and the implementing class might already have a method with a colliding interface. If you went completely over the top with compatibility expectations you could even argue that it's impossible (pre-modules I think?) to add any class or interface at all because someone at foo.com might have rudely put a client code class with the same name in your precious org.bar namespace. Nothing in the language prevents that.
Trying to design out the possibility of breakage isn't worth it, you'll end up with conventions that soften the definition of "breaking change" anyways (or with the antipattern of major-only increments)
No, in fact Ubuntu 20.04 gives me more information, as it includes the date!
No, you cannot do this.
If the semantics of an API have changed in a backwards-incompatible way, even if the structure (name, parameters, types) did not, then you must do a semver major bump.
If you somehow are disciplined enough to never change semantics incompatibility for a defined API and you make a new endpoint, then you don't really need semver at the product level. You need a way of conveying what APIs exist, but a mechanical version like 1.200.0 would work for that, or just client discovery.
> Without outside information, when trying to communicate a feature or a bugfix, whats easier, "/foo was added in version 27" or "/foo was added in 2008.02.28" ?
Using the date does not work if you release version 26.1 on March 1st.
CalVer is great for the limited use cases where you never maintain stable branches and all your clients can use the latest version - but at that point, ideally you don't need versions at all, just use HEAD. If your clients cannot reliably and automatically use the latest HEAD, then that's a sign that stable branches are a problem you will soon have to think about.
CalVer where you pick some date of branching and add a point release (e.g., "Ubuntu 18.04.2 LTS," released well after April 2018 but part of a stable branch cut in April 2018) also works well, but cannot be automated in the fashion the author advocates, and certainly cannot answer "When was this feature added," because the feature could have been backported to the stable branch.
https://www.debian.org/doc/debian-policy/ch-controlfields.ht...
This makes talking about and planning for a release unnecessarily verbose. You can only describe your releases in terms of relative time. Item A is going in the next release. Or the release after next. Or three releases from now. It's much easier to know the name of a release ahead of time.
I did away with semver for my company awhile ago, moved to dates for a little while, and now just do plain numbers. It's basically just a simplified version of semver without a distinction between MAJOR or MINOR patches. This works just fine when you're building a web app and not a library to be consumed by others.
It's about conveiing when something might potentially have broken due to API changes.
And it's about doing so in a way which can be interpreted by an algorithm.
The title is correct. It calls out that individuals are abusing semver, rather than calling out semver itself.
Then it goes on to list examples of such abuse. Fine.
Then, it concludes by proposing to REPLACE SEMVER!!!??
At no point has the article called out any problems with semver itself, other than people use it incorrectly.
Even if only 5% of people use semver correctly, that's still 5% of uses that give more beneficial information than this proposed alternative.
The first third of the article is insightful. The rest is mostly bait.
This author has not done this.
[1] https://gitlab.com/staltz/comver
[2] https://staltz.com/i-wont-use-semver-patch-versions-anymore....
Basically, it's the same principle as comver, where any breaking change must increment the first identifier, but with some added metadata to hint how big of a change the developer intended this to be. You can omit the patch if you want, in release announcements, so marketing can have the "6.0 released!" moment (I am considering omitting the patch letter for the first major release, so 4a.0 follows 4.0).
Make sure you have a git tag corresponding to your release versions. That's the main thing they're good for.
I agree that semver as defined only really makes sense for libraries. When working on internal code, “breaking changes” are rarely a concern.
We instead use it to mean <major overhaul>.<feature change>.<minor bugfix>. That works well as semver-ish, and is more useful than just a date or a commit hash.
You don't bump the semver because you've fixed internal server bugs, you don't bump it if you've added logging to your code, you don't bump it if you've refactored internally.
All of those would be patch-level changes in semver (assuming they were in an actual release.)
If you change something internal that has nothing to do with a client's use of your API, then there's no change to the semver.
Your package/container/program may change version in deployment, but that has nothing to do with the API.
If I decide to move from redis to elasticache or mysql to postgres but my API hasn't changed, that's not a change in the semver.
If they don't effect behavior of the API, they'd be build-level, which is optional in SemVer.
> If you fix an internal server bug that relates to a visible change in the API, then yes, it's a patch in the semver.
Visible changes to the API are SemVer minor if they are backward compatible, SemVer major if they are not. The lower level fields of SemVer are for things that don't change the expected behavior of the public API, including (at the build level) those that don't effect visible behavior of the API.
SemVer is not API versioning, it's product versioning for a product that implements an API. (I agree that something like SemVer used to version independent APIs implemented by a product is in some respect a better idea than SemVer itself, except that package management systems don't support API identification and versioning in a way which would let it be useful in most cases; SemVer is product versioning for the world where your product version needs to both tell about product increments that don't effect the public API and those that do.)