I want to agree with the sentiment about the human side of versioning being unreliable, but when the author "pins" the argument on the fact that pinning all versions is "annoying" s/he loses me. Pinning (and vendoring) everything is the only way to know that exactly what you expect to be deployed will be deployed when you deploy it. Anything less is inappropriate for production environments.
You're assuming that development and production version ranges will always be the same, which is false. This is what npm shrinkwrap is for, and not pinning during production is stupid. But pinning everything in development is unnecessary until you stage/deploy.
SemVer has failed us
41–50 of 52 posts
Re: SemVer has failed us
#42Semver is busted because it makes builds non-deterministic. Building the same code tomorrow may fail if a third party dependency publishes a change violating semver. For example: https://github.com/bower/bower/issues/1404#issuecomment-4876... I unfortunately can only complain, I have no solution to offer.
Huh? Which ecosystem advocates automatic updates and doesn't allow locking the version numbers of the dependencies until a developer manually updates them?
Re: SemVer has failed us
#43You had me until "Duplicate dependencies are bad". The reason every package manager except npm and Apple's bundles are a piece of shit is because they attempt to install random dependent garbage globally and by default.
You're assuming "no duplicate dependencies" is equivalent to "install globally", which is false. In the article's context, the argument is against duplicate dependencies per project
Re: SemVer has failed us
#44In languages where that difference matters, let's say Java/Scala [1], there are way too many possibilities:
- bug fix: forward and backward binary compatible; basically a drop-in replacement. Package managers are supposed to resolve conflicts between those automatically,
- new feature, but backward binary compatible; just some new code alongside the old. Package managers should also be able to resolve a conflict between dependencies that depend on different versions of those.
- bug fix: forward and backward source compatible; here we have a problem: we can guarantee developers that they won't have to edit a single line of code to migrate, but we can't allow (binary) package managers to do anything. They have to rebuild their library, which means bumping their own version number. And does that means that their new version becomes binary-incompatible with the previous one, or just their dependencies?... who knows? Maybe it depends on if the consumer of that library calls into the conflicting dependency directly or only uses it indirectly? Maybe not? Can we tell?
- new feature, backward source compatibility, but no binary compatibility; same as above: package managers cannot do anything, and conflicting transitive dependencies on the new and the old may or may not be solved by recompiling one's library. The only safe bet is to wait for the dependencies to upgrade.
- all bets are off (possibly split in minor and major): here at least we know there is nothing to be hoped. But in practice after upgrading to the new API, you are faced with the same questions concerning that thing's dependencies...
And if every dependency manager within the same packaging eco-system doesn't enforce a single global versioning scheme, how can they handle any dependency version conflict anyway? They could if packages could auto-describe their semantic versioning, so basically they can't, so they don't, and we're back to source dependencies, with recompile and bump in version number for every dependency change (ok, sometimes they do try and we get JAR hell...). And since a single versioning scheme isn't enforced, people use hundreds of different ones (or just none), and the maintainer of a library has to make all of those decisions, recursively (except that below 1 level of dependency, he/she might not be able to do anything as long as even a single dependency keeps an old conflicting dependency around, but they still have to check, in case they are lucky and can do something), taking into account the different versioning semantics of each dependency. That's madness.
So in the end, if we get a conflict between dependencies far down in the dependency tree, it becomes something pretty ugly:
- if guaranteed binary compatibility, everything is fine,
- otherwise:
0) look at that dependency's official versioning scheme, usually find nothing (except for big famous libraries)
1) pick the highest common-one, try to recompile your library
2) if it works, try the tests (your library's tests, not your dependencies' tests, mind you. Even though those should probably be run, considering they directly depend on it, and use (and hopefully test) more stuff from that library)
3a) it it works, ship it and pray.
3b) it will probably fail somewhere since the dependencies in between have not be recompiled; then you can only wait for them to upgrade.
I know in the end this doesn't work so bad (well, everything is relative...), but it still makes me sad that upgrading dependencies is still human-based trial and error.
Oh. I guess this is a rant.
[1] that's just what I am the most familiar with, but you could say the same with C packages in a distro's package repo, for example.
Re: SemVer has failed us
#45His new versioning scheme ("ferver") is built on the premise that "people don't like it when major versions change often." Seems like a fourth version number is the solution, not breaking the semantics of SemVer. I propose 4Ver. Just add a marketing version to the beginning. Update that one whenever you want (may include breaking changes). Since you have 4 numbers, you can remove the exceptions for the 0th version as…
Could still be just 3. . .
Re: SemVer has failed us
#46Semver is a way to communicate between people about compatibility expectations they should have between two versions of a package. When library maintainer says "this version changes from 1.2.3 to 1.3.0" that tells something useful to a human consumer of that package.
It absolutely does not communicate anything precise in the language of machines. It's not a formal system, and compatibility isn't formally defined. If you want it to be that, you're setting yourself up for hearthbreak.
Code reuse is a social process. Understand that semver is a tool at that level and you'll be pretty happy with it. In my mind, the most important feature of semver is that by associating pre-arranged semantics to versions, consumers can specify version ranges before those versions exist.
If I'm using foo 1.2.3, I can depend on foo >=1.2.3
Part of the major issues with semver is that patchs could
break. A feature could be "fixed", but a consumer could
have relied on that very buggy feature, and patching it
would break their app.
This is true. One man's bug is another man's feature. If you want to be fully strict, all of the behavior of a program is significant which means you can never release anything that isn't a breaking change.Being that rigorous isn't useful. Instead, realize that most patches don't break most users. For the ones that do, that's why you pin. You are pinning, aren't you?
Versions
This part is straight up annoying especially as the web moves to a world where damn near everything is do semantically version them. We just shift the numbers one to the right.So, going from 0.1.0 to 0.2.0 is a breaking change, 0.1.0 to 0.1.1 is an API addition, and 0.1.0 to 0.1.0+1 is a patch.
It seems to work really well for us.
The current solution to the above problems is to pin all
dependencies. However, this is absolutely stupid and
annoying to me. ... When every library pins, you're going
to have a lot of duplicate dependencies, even if they're
the same version!
No no no no no.You don't pin versions in libraries, you pin them in applications.
Almost every package in the dependency graph should be using version ranges. That avoids version lock and makes it easier to satisfy shared dependencies. It also means libraries don't all have to bump patches that just change dependency versions.
But, in your application, in the root of the dependency graph, you pin everything that you're using right now. You check that in to ensure that everyone on your team and every machine gets the same versions of all of the dependencies.
You get good code reuse and deterministic builds.
This is exactly why Bundler separates out the lockfile from the Gemspec. It's unintuitive at first, but it works better than any other system I've seen once you grok it.
There is one think in semver that I think is totally inane. The latest version of the spec says build suffixes have no relative ordering. We don't do that in pub because it makes no sense. If a user has uploaded foo 1.0.0+a and foo 1.0.0+b, which version should an application get that depends on foo? Pick randomly?
Re: SemVer has failed us
#47SemVer isn't broken, and you already outlined the obvious solution: 1. Don't depend on 0.x.x version modules unless you are willing to accept the pain of updating versions constantly. 2. Bump major versions liberally. If enough people just get used to this reality, then everything will be fine. The problem is trying to impose artificially conceived notions of how things "ought" to work on top of this. Until someone i…
For example, IIRC, Rails 2.3.5 introduced some API changes. That should have bumped the major version.
Rails 3.0 was a "rewrite your code" type bump; that should have been signified with a name change or other similar type of marketing codename. Rails+, for example.
I was really disappointed that Linus went with "Linux 3.0" rather than "Linux 40" as the version following 2.6.39 when he finally acknowledged that the '2.6' no longer had any meaning.
Re: SemVer has failed us
#48When a product is not principally an implementation of a single interface, including notably the case where it exposes multiple interfaces that are somewhat orthogonal (as might be the case, e.g., with an RDBMS where the SQL language support, the TCP/IP interface, etc., are all interfaces which might sensibly be versioned), it probably makes sense to use separate semver-like versions for the individual interfaces and something else for the product.
Language implementations naively seem like really good SemVer candidates by that criteria, but the sibling comment [1] addressing binary vs. source compatibility, etc., reveals how such implementations can present multiple different perspectives that are problematic.
Re: SemVer has failed us
#49Semver is busted because it makes builds non-deterministic. Building the same code tomorrow may fail if a third party dependency publishes a change violating semver. For example: https://github.com/bower/bower/issues/1404#issuecomment-4876... I unfortunately can only complain, I have no solution to offer.
Huh? Which ecosystem advocates automatic updates and doesn't allow locking the version numbers of the dependencies until a developer manually updates them?
Re: SemVer has failed us
#50Completely agree. For a long time, I've thought that humans shouldn't be in charge of version numbers at all. The main problem is that a human isn't reliable at determining "is this change breaking?". Unfortunately our software isn't well-specified enough to allow the computer to determine version numbers automatically yet. To make this a reality, you'd need Haskell-level type safety, along with specifying other cons…
For anyone in the Haskell world, it's obvious that semantic versioning[1] doesn't work. Some non-trivial amount of that is likely due to how horribly broken Cabal is, a problem the community remains in staunch denial of. But for the rest, one problem is that because Haskell more thoroughly specifies types, individual functions can remain the same in behavior but change in type because a bug-fix, say, fixes a record t…