- It doesn't allow you to install minor versions of the same library at the same time (thats how you end up with all these 1.so, 2.so)
- So if you want a binary to work across multiple distributions, you can build it on a really old CentOS and if you are lucky it will work, but I have no trust in this.
- Forward-compatibility is not really considered, where you update your libraries and your existing app becomes more powerful.
- The dynamic linker doesn't allow you to load two different library versions at the same time (without a lot of contortions). You would think you could do `dlopen` and `dlsym` on two different `.so` files, and then just have separate function pointers to each version's functions. But the linker loves to load all the symbols into a global namespace for some reason.
I'm sure there are ways to overcome all of this, but I feel too young to understand how it got this way and to old to learn it propertly :-P. If I were to design a new system today, I'd probably do something like .NET's global assembly cache: Just dump all library versions in /lib, and have the linker pick the best one at runtime. And make it easy to detect and react to a missing lib at runtime.