Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid the SemVer v0.x.x anti-pattern #4548

Open
1 of 2 tasks
jaydenseric opened this issue Mar 15, 2024 · 11 comments
Open
1 of 2 tasks

Avoid the SemVer v0.x.x anti-pattern #4548

jaydenseric opened this issue Mar 15, 2024 · 11 comments

Comments

@jaydenseric
Copy link

  • This only affects the JavaScript OpenTelemetry library
  • This may affect other libraries, but I would like to get opinions here first

It's an anti pattern to publish any package, for any reason, with a SemVer 0.x.x version. This is because according to SemVer v0.x.x releases are considered major breaking change releases until v1.x.x, so tooling like npm loses the ability to accept semver minor changes in dependencies with a version range specified with ^.

There are only 2 "upsides" to v0.x.x:

  • The package author doesn't have to think about or declare if changes are major and breaking or minor and non-breaking. They can just throw a soup of changes out into the wild. This is really truly awful for anyone depending on your package to deal with though, so I don't consider this an "upside" for the ecosystem. Any time something is published, the package author should be carefully considering and what changes are breaking, and should reflect if there are breaking changes or not in a SemVer major increment, and should document those changes in the changelog and release notes so consumers can decide when and how to adopt the new version.
  • Users can look at the major version number, see it's 0, and have an expectation that the package is experimental. This is a completely worthless "upside" because while this is an extra signal in the version, the user has lost a far more important signal about if there are minor or major changes. Also, users can't trust the stability signal because there have been packages that reached stability a decade ago and are still on v0.x.x, and there are packages with a high major version number that are unstable nightmares riddled with hundreds of bugs. The package readme is the correct place to alert users if a package is experimental or stable.

In my work Node.js project right now, here is the result of running npm ls @opentelemetry/instrumentation:

@[redacted]@ /[redacted]
├─┬ @azure/opentelemetry-instrumentation-azure-sdk@1.0.0-beta.5
│ └── @opentelemetry/instrumentation@0.41.2
├─┬ @opentelemetry/instrumentation-graphql@0.38.1
│ └── @opentelemetry/instrumentation@0.49.1 deduped
├─┬ @opentelemetry/instrumentation-http@0.49.1
│ └── @opentelemetry/instrumentation@0.49.1 deduped
├─┬ @opentelemetry/instrumentation-koa@0.38.0
│ └── @opentelemetry/instrumentation@0.49.1 deduped
└── @opentelemetry/instrumentation@0.49.1

Note that there are 2 versions of @opentelemetry/instrumentation installed; v0.41.2 and v0.49.1. This is through no fault of my own, or even really @azure/opentelemetry-instrumentation-azure-sdk installing the very old one. The fault is the v0.x.x of @opentelemetry/instrumentation preventing the declared dependency rage of ^0.49.1 from being able to pull in any releases with only minor changes:

https://github.com/Azure/azure-sdk-for-js/blob/c6b79e3f1bfd106825ba1e99bbab192eaeea146f/sdk/instrumentation/opentelemetry-instrumentation-azure-sdk/package.json#L75

It's unreasonable to expect the @azure/opentelemetry-instrumentation-azure-sdk package maintainers to subscribe to alerts every time a @opentelemetry/instrumentation v0.x is published (how can they even really do that? It's a mono repo so subscribing in GitHub to releases would spam them with releases of other packages) and manually cut a new release adjusting their @opentelemetry/instrumentation dependency up a 0.x increment.

Now as a consumer, I'm in the awkward position of having to remind (beg) third party packages to bump their v0.x dependency versions, or try to set some sort of package.json resolution overrides to force deduplication to one particular v0.x version. But because there is no signal in v0.x version numbers if there are breaking changes or only minor changes, you have to try to sift through mono repo release notes interspersed with other package release notes and if you can't find them or they aren't detailed enough to tell if updating is safe; reverse engineer the commits or code in each release to try to figure it out manually yourself if there are breaking change. But, let's say you realize there were breaking changes in the release. Is it safe to update? Maybe you have knowledge enough about your own project code to know that, but now you have to have expert knowledge of all the third party dependencies using it also to be able to know it's safe to force the package resolutions to the newer version for all node_modules.

Please, just never use v0.x.x. Just go straight to v1 and go from there. Can you please move all @opentelemetry packages to at least v1? Thanks.

@pichlermarc
Copy link
Member

pichlermarc commented Mar 15, 2024

We're fully aware of all the problems releasing 0.x versions of our packages brings, especially in situations as outlined in your issue above. However, all packages that we publish at 0.x are published as such for a reason: there is no guarantee for API stability.

This is often due to a multitude of reasons. Sometimes the underlying specification is not yet stable. Sometimes the semantic conventions are not yet stable. Most of time, the package is simply still under development.

There is also a specified expectation that old major versions of the SDK components will be maintained for a minimum of 1 year after the following major version is released. So quickly releasing new major versions will, within just a few times of doing so, render us absolutely unable to make any changes as the burden of maintaining bugfix backports will be so large that we will not be able to act on anything else anymore.

Our current plan is to resolve this is to

  • drive the @opentelemetry/exporter* packages to a 1.x stable release
  • follow with integrating the @opentelemetry/api-logs package into @opentelemetry/api, effectively stabilizing it
    • release the @opentelemetry/sdk-logs package as 1.x
    • release the @opentelemetry/instrumentation package as 1.x

At this point then we'll be able to release all instrumentation packages in the @opentelemetry namespace as stable that have stable semantic conventions. Until then we need to maintain the ability to break the public API in these packages as we see fit, which, considering the constraints I mentioned above is handled by releasing packages as semver 0.x.

Can you please move all @opentelemetry packages to at least v1? Thanks.

We will, in time.

@pauldraper
Copy link
Contributor

pauldraper commented Mar 18, 2024

It's an anti pattern to publish any package, for any reason, with a SemVer 0.x.x version.

No, SemVer says:

Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.

The experimental packages are 0.y.z; the stable ones are not.

@jaydenseric
Copy link
Author

@pichlermarc

There is also a specified expectation that old major versions of the SDK components will be maintained for a minimum of 1 year after the following major version is released. So quickly releasing new major versions will, within just a few times of doing so, render us absolutely unable to make any changes as the burden of maintaining bugfix backports will be so large that we will not be able to act on anything else anymore.

Supporting npm package major versions for a minimum of 1 year is a truly terrible policy that is divorced from the technical realities of maintaining JavaScript software. How JavaScript and npm packages work is immutable, it's the policy that needs to change. Sometimes a critical bug or security flaw can only be fixed in a semver major release. It will lead to having to support Node.js version that are EOL. It's a bad idea to set expectations to users that they don't need to update their JavaScript dependencies for a long time; as a subject matter expert who authors and maintains packages that get hundreds of millions of installs, building up a huge list of breaking changes in infrequent major releases is a way worse strategy than releasing breaking changes as they come up and allowing users to make smaller migrations over time. You make breaking changes because it makes the software better; by creating a culture of "saving them up" for the delayed next major version, you end up denying the user base the benefits of those changes for a long time. It stunts progress. It becomes much harder to coordinate PRs and maintain vision and velocity when you are juggling a bunch of unreleased changes sitting around in PR branches that can conflict as time goes on.

If you absolutely must have a concept of long term supported major versions, a better approach would be to have a list of particular major versions that you designate as LTS. Don't make every major version LTS. Even Node.js doesn't do that.

@pauldraper You can conform to specs and still commit anti-patterns. Just because you have the ability to publish 0.x releases doesn't mean you should. Nothing in the spec prevents you from publishing v1.0.0 as your first public release:

  1. Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.
  2. Version 1.0.0 defines the public API. The way in which the version number is incremented after this release is dependent on this public API and how it changes.

https://semver.org/#spec-item-4

If you have a public API, as in you are expecting anyone to be using the package at all (otherwise why publish to the public npm package registry?), then you should be publishing at least v1.0.0. There is no spec wording to the effect of "Version 1.0.0 means that everything is mature and stable and there will not be any more breaking API changes". The whole point of SemVer is to deal with breaking changes over time. It's expected for there to be a v2, v3, v4 etc. over time.

Saying "these packages are experimental, so we won't use semver major versions deal with it" is ignoring the reality that you have an ecosystem using these packages. Pretending there is no public API or consumers isn't helping the ecosystem. React was v0.x for years and years before they realised how stupid it was given half the world was using it and they admitted they were wrong and published their first major version at v15.0.0.

@pichlermarc
Copy link
Member

@jaydenseric I don't necessarily disagree.

For instance: @opentelemetry/instrumentation not being stable is a problem that we're affected by ourselves as well as we also deal with that in https://github.com/open-telemetry/opentelemetry-js-contrib. However, there are some other parts of the project that we'll need to stabilize first to be able to mark that package as stable.

We've not been very transparent with our plans to stabilize packages in the past and we'll try to do better in the future. We're committed to eventually releasing our packages as 1.x. To start, I've started to document parts of the process towards releasing more packages of this repository as 1.x and actions that we need to take to get there (some of them are still incomplete, but I'll continue to refine them in the next few days: #4586, #4582, #4585, Logs API stabilization issue currently pending).

We've also already started considering a 2.0 release, and we are also considering increasing the frequency of major releases in the future. As you've seen in the spec document I linked earlier, this will likely also require us to drive changes in the specification.

We understand that these issues affect people right now, but all these changes are not something that we'll be able to make immediately, it will rather be a gradual process.

@pauldraper
Copy link
Contributor

pauldraper commented Mar 29, 2024

as in you are expecting anyone to be using the package at all

This package is experimental. As in, you can experiment with it, use it, provide feedback, etc. Be aware there is not yet a commitment to stability.

I'm having trouble deciphering what the complaint is.

  1. 0.x.y is the inappropriate version. No, this is a non-stable package, and per SemVer any non-stable package should be 0.x.y.
  2. It should be stable. Brilliant suggestion.
  3. 0.x.y packages should not be published to npm. This convenient for trial uses. You're welcome to ignore them.

@jaydenseric
Copy link
Author

@pauldraper

I'm having trouble deciphering what the complaint is

I have extremely well explained the issue, and reasoned a better approach, to the point that it's hard to interpret your mischaracterizations of it as being in good faith.

  1. 0.x.y is the inappropriate version. No, this is a non-stable package, and per SemVer any non-stable package should be 0.x.y.

As I have explained, SemVer allows people to publish 0.x versions, but it doesn't require you to. Due to the many problems I have already explained that 0.x releases cause, they should never be used for any reason.

  1. It should be stable. Brilliant suggestion.

I'm not asking for any sort of code stability or changes, only to change the version numbers. Publish a v1, v2, v3 as fast as you like! The faster the iteration the better. I don't expect v1 to be "stable". I do however expect breaking changes to be documented in the SemVer major version numbers and in the changelog / GitHub release notes. Early in a project when frequently lots of breaking changes are happening, that's the most critical time to make it easy for consumers to understand the differences and manage their dependency updates. Right now, with 0.x releases, you are making it the hardest for people to understand at the time in the project it matters the most.

  1. 0.x.y packages should not be published to npm. This convenient for trial uses. You're welcome to ignore them.

This has nothing to do with anything I have said. Publish something experimental, but as v1 and not v0.1.

If you publish something to npm, it means you intend for other packages to install it. At this point, an ecosystem has already adopted OpenTelemetry JS so telling users like me that are required to use it for work to just go away, stop using this software (or the rest of the ecosystem that uses it), and to disregard my contributions of expert advice, is unwarranted hostility. I'm not some random novice spewing uninformed advice; I'm an npm package author and ecosystem expert with over a billion installs under my belt. I specialize in optimal module design and optimizing npm package dependency graphs.

@pichlermarc thanks for your comment :) It's good to have some insight into the challenges and where things are headed. That policy about major versions being supported for a year is a nightmare; I would just change the policy ASAP (assuming it's not legally binding, it's published under the Apache license which explicitly waivers liability or warranty) before too many people adopt this dependency under that expectation, and cop the flak from anyone that complains. Chances are no-one will care at this early stage. If the policy for some reason can't be amended, then I would suggest archiving this package and republishing under a new name with a fresh (no) policy. Only a commitment to adhere to SemVer, which is implied anyway when publishing to npm.

@Flarna
Copy link
Member

Flarna commented May 3, 2024

What is the benefit in releasing new major versions frequently instead of new 0.x versions?
People usually use something like ^13.0.0 so new majors are not picked up similar as new minors are not picked up in the 0.x range. As a result the main pain remains.

What would be lost is to move into what is named stable here which comes along with a stability guarantees for some time.
Well, we could decide that every 10th major version is an LTS version or so.

@jaydenseric
Copy link
Author

@Flarna imagine hypothetically you are publishing an update to a package foo that is currently v0.1.0, that only includes 3 minor new features and no breaking changes. You publish that as v0.2.0. Now all projects that have a dependency foo: "^0.1.0" won't automatically install the v0.2.0 newer version, because tooling considers a 0.x release a major change.

So now, as a user, you see when you run npm outdated in your project that there is a foo update available from 0.1.0 -> 0.2.0, you wonder, are there any breaking changes in that? Is it safe to update? You have to google the repo for that dependency, and click around in GitHub releases, see it's a mono-repo with all sorts of mingled releases, so you try clicking through the mono-repo directories to look for the changelog for just that specific package, to try to see if the last 0.2.0 release of foo has anything breaking in it. Finally you find out that it only contains new features and no breaking changes, so you bump in your project package.json the dependency foo to ^0.2.0.

But, unbeknownst to you, several other dependencies in your project's dependency graph also depend on the package foo, and they are depending on ^0.1.0 still. So now you get in your node_modules both the 0.1.0 installed by dependencies, and 0.2.0 installed directly by your project. Having multiple versions of the same thing installed in the same project can cause all sorts of problems beyond a bloated hard drive. It can bloat bundles in browsers with multiple versions of the same thing, it can cause instanceof checks to fail because a class instance is being constructed from one version and is being compared against the class from another. Module singletons can stop working properly.


Now, consider an alternative reality where foo was v1.0.0, and you published a new v1.1.0 containing only minor changes. Literally no-one had to do anything at all, next time npm install happens your project and it's dependencies that have a "foo": "^1.0.0" dependency will pull in the updated version.

When it comes time to publish a breaking change, people will see foo is now v2.0.0 and can tell just by looking at it that it's not safe to install without looking up the breaking changes are.

@Flarna
Copy link
Member

Flarna commented May 3, 2024

ok so your assumption is that no breaking changes happen usually.
The 0.x range supports this by incrementing patch and patterns like ^0.x would work.

As we are on 0.50.x now either the judgment of breaking/non breaking was incorrect in the past or we would be on major version 50 now.

I assume the combined release/versioning of all modules here has also quite an impact. A breaking change in one experimental module results in minor (or in your proposal major) of all modules.

Independent version numbering would likely require some sort of compatibility table.

@jaydenseric
Copy link
Author

The 0.x range supports this by incrementing patch and patterns like ^0.x would work.

No, the semver spec makes 0.x versions special, in that breaking changes are allowed to be published in 0.x versions. So tools like npm etc. will not install v0.2.0 if your dependency range is declared as ^0.1.0. Neither humans or tools can tell if there are breaking changes or not just by looking at a SemVer of 0.x.

If you really published 50 versions that had breaking changes, then it's perfectly reasonable to be at v50.0.0 at this time.

I assume the combined release/versioning of all modules here has also quite an impact. A breaking change in one experimental module results in minor (or in your proposal major) of all modules.

Independent version numbering would likely require some sort of compatibility table.

You are not independently publishing versions for each package? That's a big anti pattern. A package's SemVer should reflect what changes have happened in that specific package, not what has happened elsewhere in other packages. Mono-repo tooling allows independently versioning packages. Popular projects have switched in the past from a shared version to individual versioning because it's simply better for the ecosystem. Publishing new versions for things where nothing has changed in them is hugely disruptive. It creates large diffs in package dependencies and lock files, it causes duplication and bloat in node_modules, it creates more work for tools that audit or analyse package versions, it spams people's attention when they run npm outdated and they see 5 things have a new version available (but actually none have even changed, because a package in the same mono-repo you aren't even installing has a change). It causes dependabot noise in peoples repos, It makes it hard to tell if it's safe to update your dependency that has a new major version available, because maybe that major version increment might only be because of a major change that happened in a different package not installed in your project that happens to be maintained in the same monorepo.

@Flarna
Copy link
Member

Flarna commented May 3, 2024

No, the semver spec makes 0.x versions special, in that breaking changes are allowed to be published in 0.x versions. So tools like npm etc. will not install v0.2.0 if your dependency range is declared as ^0.1.0. Neither humans or tools can tell if there are breaking changes or not just by looking at a SemVer of 0.x.

Seems you misunderstood me. Non breaking changes in the 0.x range should not result in minor updates, only patch. So your pattern ^0.1.0 would pick up the 0.1.1, 0.1.2, 0.1.3,... releases.

My feeling is OTel JS at a whole is in a too early stage for you. You expect a sort of stability which is simply not yet reached - at least not in all parts you would like to use. Just changing the numbering doesn't change the reality.

Work is ongoing in the right direction. There is a lot work to do but the number of people actually picking up the work is limited.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants