
SemVer has failed us - rayshan
http://www.jongleberry.com/semver-has-failed-us.html
======
angersock
Semver hasn't failed anyone--web developers and the bozos in the Node
community (of which I cheerfully count myself as a member) are lacking in
discipline.

You should be fast to get to 1.x.x. If your app breaks on a patch release
(that was only a bugfix and provided that interfaces stayed consistent), maybe
you shouldn't rely on buggy behavior and maybe you should fix your code.

This isn't a failing of semver--this is a failure of being responsible
engineers.

EDIT:

The best example from the article is the bit talking about how people
interpret 0.x vs 1.x vs 2.x. Folks, it's just a damned version number, and a
number that only indicates you may have to refactor you application to handle
changed interfaces. Any other psychological baggage you attach to it is just
that, baggage. Aaaargh.

~~~
nmjohn
I don't disagree, but unfortunately believing that humans - no matter how
logical CS people tend to be - will always think completely logical and
rational is a flawed line of thinking.

Yes, people _should_ get over changes in version number, but in reality they
don't. So library authors cater to reality, not how they wish things should
be. That is the problem, and why semvar is broken. Not in principle it isn't,
but in how it actually functions in the hands of humans.

~~~
cwyers
Well, people use version number to communicate a lot of things that aren't
captured by the strict, rigid form of semver. And when you follow semver, you
omit communication about all of those other things. Now, if you think that the
only things worth caring about are what semver says matters, then you don't
view this as important.

But there's a lot more to the world than package managers, and expecting
people to do things the way that's most convenient for package managers is
just a little bit silly, no? Package managers are there to serve us, not the
other way around. So maybe instead of "fixing" version numbers so that they're
the most convenient for package managers, maybe we can fix package managers so
that they use something better suited to handling dependencies than version
numbers, so that we can use version numbers to communicate with people, not
just package managers.

~~~
nmjohn
I should have added a conclusion to my post but I sometimes feel they don't
always further technical discussions because once a conclusion is applied,
dissenters tend to ignore the logic leading up to it no matter how valid.

But I do agree that package managers are there to serve us, not the other way
around. The unfortunate thing is this issue is one of differences in human
nature and how a given person prefers their system to be - rigid and concrete
maximizing efficiency; or fluid and communicative adding overhead but
increasing understandability.

------
StefanKarpinski
We've used SemVer for Julia for a while now and it seems fine. However,
versioning and compatibility are still hard problems and a perennial,
unavoidable pain. The way I like to think of it is this:

\- patch: forwards and backwards compatible – only bug fixes, no new features,
no changing existing features.

\- minor: backwards compatible – new features allowed, but old code should
keep working.

\- major: all bets are off.

This is pretty much what the SemVer spec says, and it's hard to imagine what
else you could do. The significance of forwards compatibility for patch
versions is that if you bump a patch version and find that it was problematic,
you should be able to revert back to a lower patch version without breaking
new code that's been written since the bump.

The thing is, it's still a judgement call what constitutes a bug fix. If
you're fixing a segfault, that's a no brainer – no one (sane) was relying on
something segfaulting. If you're changing a behavior from one potentially
reasonable behavior to the one that was actually documented, then, yeah, that
might not be "just a bug fix" – people might be relying on it. So we're
_super_ conservative about "fixing" even fairly bad behavior in patch
releases. Still, the only way to make sure an application keeps working
exactly the same is to use the same exact versions – down to the patch level.

The FerVer "spec" is barely coherent, I'm not sure how anyone could consider
that something to follow. SlimVer basically just gets rid of things in SemVer
that you don't have to use anyway. Pre-release versions – especially release
candidates – are incredibly useful. You tag 1.2.3-rc1 and if no issues crop up
in a week, then that becomes your 1.2.3 release (1.2.3-rc1 and 1.2.3 are
_exactly_ the same); if issues do crop up, then you fix them and tag 1.2.3-rc2
in a week and repeat.

I also think that pre-1.0 versions are useful: just treat the minor version
like a major version. For 0.1.2 => 0.1.3 you only fix bugs but for 0.1.3 =>
0.2.0 anything goes. There's no point in doing feature-only, backwards
compatible releases while you're still working out the API for something, and
0.x releases are for precisely that kind of exploratory period.

~~~
Trombone5
The argument in the article seems strange to me: "downstream relied on
undocumented behavior, and now that behavior has changed, so downstream
breaks"

So what should upstream do? After all, the premise must be that the upstream
does not control or even know about all the clients! The entire point is to
separate the concerns, so how could upstream know if a bug fix is a breaking
change if it passes the internal regression tests?

Of course they can't know without asking downstream, and hence the use of
release candidates.

~~~
StefanKarpinski
People rely on undocumented behavior all the time. In fact, most projects
would be unusable if you didn't – it's a rare project that's perfectly
documented. You're not going to hear from most of your users when you break
stuff for them – most of them will just be silently annoyed. Enough annoyance
from enough people and your library/language/whatever develops a bad
reputation.

------
jameshart
SemVer is working fine, you just need to face up to the real impact of a
change on clients, not what you wish was the impact of the change. If a change
breaks clients who rely on buggy behavior, you need to bump a version, not a
patch number.

~~~
cortesoft
This may be true, but in practice (as pointed out in the article), EVERYTHING
can potentially be a breaking change
([http://xkcd.com/1172/](http://xkcd.com/1172/)). Since this is the case, you
either need to always bump the version number (rendering the patch number
useless), or bump the patch number but then have to wait for the bug reports
to come in... once you get one bug report stating your patch broke someone's
code, you would bump the version number... this doesn't seem practical,
however.

------
33a
SemVer 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 invents a viable system for checking invariants and interfaces
on codes statically, semver is pretty much the only show in town for
specifying compatibility between code modules.

~~~
bryanlarsen
Exactly, if you claim to be following semver, and your major version is 1, 2,
3; you're probably not following semver. People have to get used to the fact
that bumping the major version is not a big deal. All that it means is that
there was an API update or breaking change; it may have been tiny. Chrome is
up to version 38; they're doing it right.

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.

------
arohner
Completely 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 constraints, like
"this fn is O(log(n)) or better".

>If you're pinning versions, you're reducing the package manager to a
glorified CURL

I'm actually quite happy with this. I realized it when a few weeks ago, bower
broke for me. Turns out, a grandchild dependency of bower failed to follow
semver. This means that it's not enough for _you_ to follow semver, every
single dependency of all of your dependencies have to follow semver as well.

~~~
AaronFriel
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 type used somewhere. The external
interface might be largely the same, or even identical, but the binary output
is non-backwards compatible. Dynamic libraries and strong, descriptive type
systems aren't very compatible. The result is that a lot of bug fixes become
minor version bumps.

These minor version bumps scare library developers, especially people that
produce libraries that depend on libraries that depend on... and so on. So
packages fail to build too often because of constraints. So the irony of this
thorough process for package building is that humans have mucked it up and
made it break builds. In almost every case in which semantic versions blocked
some update from occurring and caused my work to stop, it was because I needed
to bump the constraints on some third-party package. It's a boring process
that I've only become too accustomed to:

1\. try to build with updated libA 1.1.0-foobar

2\. libB depended on libA < 1.1

3\. download source for libB

4\. update constraint and repackage libB locally

5\. rebuild main application

This happens just about every time there's a new version of any major piece of
Haskell software.

If we really want to move into The Future, we need to start versioning
individual modules or functions, instead of whole suites of software. But the
cognitive burden of doing this is very high, and the software to do it hasn't
been written yet.

[1] The Haskell community doesn't technically use semver, but rather a related
policy called the package versioning policy. That's a nitpick though, and
doesn't affect my argument.

~~~
substack
> If we really want to move into The Future, we need to start > versioning
> individual modules or functions, instead of > whole suites of software. But
> the cognitive burden of doing > this is very high, and the software to do it
> hasn't been > written yet.

I don't think this problem is related to any cognitive burden, but is an
artifact of how the package manager works and how easy it is to liberally
publish packages. Consider npm, where people publish packages with little to
no coordination and everything is mostly fine (whatever the author of the
parent article says). Because it's so easy to make an npm account (anybody can
`npm adduser` and start publishing immediately) and so easy to publish (just
pick an unused name, make a package.json, and `npm publish`), people routinely
make very granular packages, often just a single function. Importantly, these
functions don't need to fit into a taxonomy namespace like in haskell which
prevents namespace hierarchy turf wars for the lesser price of namespace
landgrabs.

Better still, you can have multiple different versions of a library in the
same application. In your example, libB would just get the version of libA
that its constraints satisfy but elsewhere other libraries could depend on a
version of libA that do not satisfy the constraints on libA that libB requires
because every package gets their own copy of their dependencies, with
automatic deduping when the constraints are satisfied to save space.

------
tikhonj
A core problem is that the current system conflates two subtly different
notions of version: the low-level package manager version (is it backwards
compatible? will it build?) and the high-level person version (is this the
same library with some changes, or is it a new generation or significant
redesign?).

The first notion is needed for keeping our code stable but keeping up with
bufixes and security patches. The second one is needed to deal with design
issues: will this change require a lot of reworking? Will it change the
underlying model or abstraction in the library?

Both of these are important. But because Semver requires bumping for _any_
breaking changes—even if they are semantically minor—it can only address the
_first_ , low-level notion of versioning, not the second.

A possible solution? We can have _two_ minor version numbers:

    
    
        4.1.0.2
    

Patches and backwards compatible changes work as always. But if you are making
a breaking change, you have two options. If it's not too big, just increment
the first one:

    
    
        4.2.0.0
    

But if you're really changing things up and creating a new generation of your
library, go for

    
    
        5.0.0.0
    

Of course, the difference between the two major numbers is not well-specified.
But that's fine! From the more technical, low-level perspective, it's no
different from normal Semver: any change in the major version (either of the
two numbers) can be breaking. It just also lets library authors send an
additional signal to developers about the scope of the change.

For example, this scheme gives more information on how "stable" a library is.
If the second major number is high, it means there are a lot of _minor_
breaking changes; it would take a bit of active maintenance to keep up, but as
long as the first major number is not changing, these steps won't be radical.

Personally, this approach makes a lot of sense to me. It's also more or less
what Haskell packages use according to the "package versioning policy", and it
seems to work pretty well. (There are some other pervasive issues with Haskell
version numbers, but not related to how they treat major versions.)

------
xaa
I think the root problem is that SemVer works great, and was designed for,
larger projects with many developers and a relatively stable API.

For smaller projects and libraries, it works very poorly, because these are
usually "itch-scratching" efforts that don't necessarily have a clear endpoint
or goal. For these kinds of packages, I wish people would just use a commit
number. I really hate having to agonize over SemVer for a 500 LOC miniproject.

That kind of project, incidentally, is the most likely to be in "0.X.X" limbo
forever. See for example the Clojure ecosystem where 80% of packages are this
way.

------
nardi
His 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 well, and make version 0
follow the same rules as everything else. This cleanly separates the
marketing/naming aspect of versions from the semantic aspects.

~~~
jongleberry
Could still be just 3. <marketing>.<breaking>.<non-breaking>

~~~
nardi
Yeah, the downside to this is that people like to differentiate between adding
features and fixing bugs, which is the minor vs. patch dichotomy. Still not a
horrible idea.

------
thedaniel
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.

~~~
jongleberry
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.

~~~
shtylman
Pinning in development is equally necessary if you hope to bring sanity to
your development team all using the same codebase. Not pinning may be great
for a one person 'team' or modules where you control everything, but can be a
major time sink when one person is seeing a bug in a patch version they have
and another developer is not seeing the same bug because they are on a newer
patch. Allowing developers to "clone" consistent versions of the codebase is
as important as deploying consistent versions to production.

------
dustingetz
Semver 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...](https://github.com/bower/bower/issues/1404#issuecomment-48764816)

I unfortunately can only complain, I have no solution to offer.

~~~
bhaak
Huh? Which ecosystem advocates automatic updates and doesn't allow locking the
version numbers of the dependencies until a developer manually updates them?

~~~
dustingetz
This particular example is javascript/Bower. A fresh clone and build of any
javascript project using Bower has a non-deterministic build if it uses
Bower's semver capabilites.

~~~
bhaak
Yes, I read up on it. That's quite insane that there is no automatic way of
having the exact versions required when installing something with bower.

Of course there will be lots of pain, it just takes one package to slightly
violate SemVer and you'll have to hunt down the culprit.

What surprises me is that in the article they seem to think that switching to
another versioning scheme will help with this problem. Especially as better
tools would mitigate most of this problem.

------
natrius
As someone who hasn't been paying attention to semantic versioning orthodoxy,
the author's suggested alternative of ferver[1] is what I thought semantic
versioning already was. So yeah, do that. All I care about is breakage, not
whether new features were added.

[1]
[https://github.com/jonathanong/ferver](https://github.com/jonathanong/ferver)

~~~
angersock
It's basically just semver, with a little more indication given to what sorts
of breakages are considered. The problem I have with "minor" vs "major"
breakage is that if I only use one function from your library, and it breaks
and screws up my entire app, that ain't "minor".

This is just more of the same marking of territory that Node is kinda famous
for.

------
dragonwriter
SemVer makes sense as a system for versioning exposed interfaces, and for
versioning products where the product is just an implementation of single,
coherent interface.

When 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.

[1]
[https://news.ycombinator.com/item?id=8155501](https://news.ycombinator.com/item?id=8155501)

------
jpollock
The bigger problem has always been when the version number is marketing.

I worked in one team, and there was a contractual requirement that v 1.16 was
the last version we would ship. So, where before we would increase the number
at certain steps, we stopped at 1.16 and shifted to a third digit. Since we
were a continuous build shop, we ended up with 1.16.110 very quickly. At that
point, we shifted from 3 digit versions to a version number which was
marketing controlled and a build number which was controlled by engineering
(hashed from the tag+date).

This pattern repeated over and over. The version number would be communicated
to the customer, a new version would be needed and so the version number would
grow a digit. It ended up at X.Y.Z.A.B rev C. Yes, it was silly.

So, no matter what you use, make sure you keep your externally visible version
numbers separate from the numbers you use to talk about them internally. :)
Internal numbers should remain completely opaque to the customer.

------
malandrew
Personally, I would far prefer a strong contracts system over semantic
versioning. The ability to say "I rely on these N interfaces to present accept
these argument types and return these types, and here are some test arguments
and the values I expect returned" to be a much better way of describing what
you rely on. Even better is that by defining things in terms of contracts, you
should be able to replace dependencies with any module that matches the
contract you require.

In general, it's always better to have a computer enforced standard over a
human enforced one. Semantic versioning can just become human readable sugar
over a set of contractual expectations.

~~~
jahewson
Unless you checked every possible result for every possible function call you
use then the 3rd party library could have been changed in such a way which
would break your program. There's no way to prevent such situation in general,
e.g when the input value space is non-finite.

Other kinds of specification would seem very hard to test, for example
rendering a 3D scene, if we used a bitmap as test data then we'd get false
positives when the rendering is improved in some minor way, and the spec will
fail, we'd need to use some perceptual metric for image similarity instead.

Version numbers aren't so bad, given their ease of use.

------
nilved
Versioning schemes like semver don't work in practice because change is
breaking by definition. I'm increasingly convinced that the only version
people should use is "latest." Security patches are the only things that
should be retroactively added (maybe even as out-of-band patchfiles clearly
marked with "to be used only until you get up and upgrade your software.") If
you've amassed such tech debt that an upgrade is infeasible, you've forked the
software and are responsible for its upgrades.

------
bcoates
You 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.

~~~
jongleberry
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

~~~
shtylman
Duplicate dependencies affecting client side "size" is highly overrated. We
can make tools (and have tools) that can compare files for same content and
only include them in client side bundles only once. De-duplicating the same
could should be a tooling issue. If I want to use client side modules that
depend on two different versions of the same library I should be able to do
this. Disk space is cheap and we can do the rest via post processing.

------
munificent
I (along with Natalie Weizenbaum) wrote the package manager that Dart uses[1].
It's based on semver. It's good linkbait to characterize semver as broken, but
not at all true. Semver works fine when you realize what it is.

[1]: [http://pub.dartlang.org/](http://pub.dartlang.org/)

Semver _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 <1.3.0 with decent
confidence even though 1.2.4 may not even exist yet. That sort of forward
compatibility makes the package ecosystem a lot more resilient to changes over
time.

    
    
        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 < 1.0.0 do not have semantic versions according
        to semver. These are considered libraries "in development"
        and developers could version however they see fit.
    

This part is straight up annoying especially as the web moves to a world where
damn near everything is <1.0.0. On the Dart team, we've adopted a policy that
for sub 1.0.0 packages, we _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?

------
mattdesl
npm init should really use 1.0.0 as default, and I agree 0.x.x should be
dropped. It causes a lot of problems since most people publishing packages
don't know what 0.x.x means, and they just end up leaving their (stable)
modules at 0.0.0.

------
gourlaysama
I am not sure I agree with all of OP's argument, and I don't really care for
the whole "version numbers as a marketing message" thing, but for me SemVer
fails miserably when it comes to _binary_ compatibility versus _source_
compatibility (and don't even start on _semantic_ compatibility...).

In 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.

