As Linus points out, the utility of shared libraries depends on the situation. Widely used libraries like libxcb, libGL, and zlib that tend to have reasonably stable ABIs are good candidates for sharing, while the "couple of libraries that nobody else used. Literally nobody" should obviously be statically linked. Software engineering is mostly about learning how to choose which method is appropriate for the current project.

However, there important advantage of using .so files instead of building into a big statically linked binary is NOT that a library can be shared between different programs! The important benefit is dynamic linking. The ability to change which library is being used without needing to rebuild the main program is really important. Maybe rebuilding the program isn't practical. Maybe rebuilding the program isn't possible because it's closed/proprietary or the source no longer exists. If the program is statically linked, then nothing can happen - either it works or it doesn't. With dynamic linking, changes are possible by changing which library is loaded. I needed to use this ability last week to work around a problem in a closed, proprietary program that is no longer supported. While the workaround was an ugly LD_LIBRARY_PATH hack, at least it got the program to run.

I don't think that being able to replace a library at runtime is a useful enough feature to justify the high maintenance cost of shared libraries. Like I complain about in a comment below, the cost of shared libraries is that an upgrade is all-or-nothing. If one program on your system depends on a quirk of libfoobar-1.0.3, and another program on your system depends on a quirk of libfoobar-1.0.4, you're fucked. You can't have both programs on your system, and Linux distributors will simply stop updating one of them until the author magically fixes it, which happens approximately never. And, the one they stop updating is the one that you want to update to get a new feature, 100% of the time.

It's just not worth it. A binary is a closure over the entire source text of the program and its libraries. That's what CI tested, that's what the authors developed against, and that's what you should run. Randomly changing stuff because you think it's cool is just going to introduce bugs that nobody else on Earth can reproduce. Nobody does it because it absolutely never works. You hear about LD_PRELOAD, write some different implementation of printf that tacks on "OH COOL WOW" to every statement, and then never touch it again. Finally, dynamic loading isn't even the right surface for messing with the behavior of existing binaries. It has the notable limitation of not being able to change the behavior of the program itself; you can only change the results of library calls.

I never want to see a dynamic library again. They have made people's lives miserable for decades.

>If one program on your system depends on a quirk of libfoobar-1.0.3, and another program on your system depends on a quirk of libfoobar-1.0.4, you're fucked.

Why? You can have both e.g. /lib/libfoobar-1.0.4.so /lib/libfoobar-1.0.3.so

Usually you would have /lib/libfoobar-1.so -> /lib/libfoobar-1.0.4.so, but it doesn't prevent you from linking the problematic program directly with libfoobar-1.0.3

> but it doesn't prevent you from linking the problematic program directly with libfoobar-1.0.3

I feel that patchelf[0] is an ingenious little tool for exactly that purpose that isn't getting enough love. Not as useful for FOSS stuff, but it's been really useful for the times I had to relink proprietary things on our cluster in order to patch a security vulnerability or two.

[0] https://github.com/NixOS/patchelf