This isn't necessary. If you're not going to load both versions into the same process, they can overlap symbol names. This is how Linux distro version upgrades work: the system installs libfoo2, then upgrades binaries that use libfoo1 to versions that use libfoo2, then removes libfoo1 when nothing needs it any more. At all times, the system is in a working state; any given binary will load either libfoo1 or libfoo2.
The trouble is that Linux distros tend not to want to provide more security support for libfoo1 than they have to, so if you have software that still requires libfoo1, the easiest approach is to use a container/chroot/VM/whatever with an older distro release, possibly from a different vendor, that's hopefully still under security support.
(If you do care about loading both libraries into the same process, you need symbol versioning / two-level namespaces / direct binding / whatever your ld.so wants to call it, which means that every reference to a dynamic symbol specifies which dynamic library the symbol comes from. The names themselves remain unchanged, but they're referenced by a tuple of library and name. This works. Again, I've shipped software that would crash horribly if this didn't work.)
> Both the software developers and the package managers never intended for incompatible software to be installed at the same time.
I'm not sure that's true for software developers: I can't imagine that, say, the OpenSSL developers do their work by replacing their system OpenSSL every time they recompile. They already know full well how to test an OpenSSL in ~/src/openssl and keep it separate from the one in /usr/lib, without using chroots.
It's true for package managers, but that just means that package managers are failing at delivering a thing users want.
In particular, forcing upstream software to follow conventions and building all software the same way, and patching things as necessary, is the entire job of a distro. If two versions of a distro package conflict, that's because the distro chose not to make them coinstallable. If only one version of a library is available in a distro, that's because the distro chose not to make other versions available. They might have reasons for this (e.g., security support effort) but none of it is fundamental impossibility.
(Also, if you mean B1.1 in a semver sense, or equivalently a libb.so.1.1 sense, upstream is promising that it's backwards-compatible with B1, such that A can dynamically use B1.1 despite being compiled against B1. If that's not true and B1.1 is ABI-incompatible with B1, either upstream or the distro needs to rename B1.1 to B2 / rename libb.so.1.1 to libb.so.2.)
> Devs get to pretend they know how to deploy software or manage systems
I submit that the only valid measure of whether you know how to deploy software or manage systems is whether systems get deployed or systems get managed.
> I submit that the only valid measure of whether you know how to deploy software or manage systems is whether systems get deployed or systems get managed.
If you don't care at all about the result, sure.
It doesn't work because it's incompatible, and so it's complicated. If I build A with B1, and you build C with B1.1, and the user wants both A and C, they need both B1 and B1.1. Which is fine - IF they built B* with unique symbol names, and built their apps against those unique symbol names, and if everyone else in the world follows exactly the same convention. Of course, if anything else changes (cpu architecture, features, ABI, whatever) everything may break anyway. But in general the biggest problem is not everyone builds software the same way.
Both the software developers and the package managers never intended for incompatible software to be installed at the same time. The software devs could make it handle these cases, but they usually don't, so it doesn't work. The package managers could package their software uniquely every time, but that would be annoying, cumbersome and not very useful for managing systems ("do i need to remove db3 before i install db4? what are all the packages called? what's the order? what else will be affected? do i rename everything and rebuild everything with names specific to this one library package name?" etc).
It shouldn't be possible to install conflicting software because the package should be built to fail to install if conflicting software exists, or remove the conflicting software before install. But sadly there also exists the ability to remove all these safeguards, or to install unpackaged software.
Containers are just a wrapper around existing tools, such as package managers. They don't add functionality, they just simplify it. With Docker, you aren't linking to multiple versions of openssl within the same process: you're running one process in one environment with one version of openssl, unless you intentionally get really fancy, which really isn't easy. Package managers never failed, they simply weren't being used right.
Containers won because someone finally realized users don't care how they do what they want, as long as they get to do it without having to know how it actually works. Devs get to pretend they know how to deploy software or manage systems and Ops people get less responsibility because they didn't build the shit so they don't support it. It's a win-win, but it's still a mess, and none of it is new or novel.