> When libraries make backwards-incompatible changes it's usually because the original design was flawed in a way that made continuing to support it, while adding new features, infeasible.
I think that "infeasible" is rarely true.
Let's take, as a well-known example, Python3. Well, the python developers wanted to change the default string representation, the fact that "print" was a statement, and the behavior of integer division, but this absolutely could have been done by things such as a new syntax for Unicode strings (such as, huh, u"hello"), making integer division dependent on "import from __future__"statements, using a *.py3 file extension for breaking new syntax, and so on. It would have been more work for developers, true, and this was ultimately their decision, but it was not technically required.
What developers of library and infrastructure need to realize is that very soon there is far more code which depends on established APIs and that breaking this code is, on absolute terms, more work, and is disruptive in collective terms.
It is like a tree where most of the biomass is in the leaves, not in the trunk. For example, there is a Wiki software named MoinMoin Wiki, written in Python, and supporting multiple markup formats, which is written in Python. It is popular and also used for the Python wiki, so you could be assured it is used in infrastruccture. But there is no Python 3 port because the developers did not had time for that, and in this respect the breaking changes of Python 3 are disruptive. There are also many scientific code bases written in Python 2 which will never be ported to Python 3, because there is simply no funding and no scientific career for such work.
And this becomes very visible in larger projects which depend on a lot of libraries which in turn have transitive dependencies on multiple levels. Just look at the full dependency graph of something like Gazebo, Tensorflow or Gimp. The former might well have several hundred dependencies As soon as one of there libraries demands that a newer version of the dependency is used, and this dependency introduces breaking change, it is likely that another dependency in the same project is broken by it. And telling to users "to just fix and upgrade" is not going to work, it does not scale, and people will at some point stop to upgrade your stuff altogether. Imagine the Linux kernel worked this way! No sane person would use it.