If you are looking for a language agnostic version manager, the nix package manager [0] might be worth a try. In combination with lorri [1] you will be dropped into a shell with all required development dependencies available, when you enter the project directory. This does not only include the interpreter/compiler versions but also any other dependency you can think of, like specific libraries the project links against (well almost any, it has to be available as a nix expression, best case directly in upstream nixpkgs).

For me, it has come so far that I don't have any interpreters or compilers in my main OS environment and even for one off REPL sessions, I'll use `nix shell nixpkgs#python3 -c python3`.

[0]: https://nixos.org/

[1]: https://github.com/nix-community/lorri/

Based on other comments like this, I gave it a try.

Be prepared to sink dozens and dozens of hours learning the language, it’s features, and switching everything over. Be prepared to get stuck when you are required to use an old version of a language and things don’t install correctly — and sometimes blocked by nix itself from installing it. Be prepared to get stuck if you are not in the happy path. Be prepared to get lost in the sometimes scant documentation.

I love the idea of nix, but it did not work for me except for the simplest cases.

I have the exact opposite experience. Nix is the best solution out there especially in the sort of situations that you describe.

Nix provides a level of flexibility that other solutions simply do not offer. If a package in Nix doesn't fit your requirements for whatever reasons, you can create a modified version of a package with ease. For example, say that you need a version of Nginx built against a custom version of OpenSSL. You can do just that with a few lines of code:

    let
      mypatch = pkgs.fetchpatch {
        url = "https://example.com/bugfix-for-openssl.patch";
        sha256 = "...";
      };

      openssl = pkgs.openssl.overrideAttrs (old: {
        # add build flags
        configureFlags = old.configureFlags ++ [ "--enable-foo" ];

        # add dependencies
        buildInputs = old.buildInputs ++ [ pkgs.foo ];

        # add patches
        patches = old.patches ++ [ mypatch ];
      });
    in
    nginx.override { inherit openssl; }
Nix will even know which packages it'd need to build locally instead of downloading a prebuilt binary.

You can't do that with other common package managers. You're stuck with whatever the package manager provides you. So if you're not in the happy path, you're out of luck. You'd either have to give up or build from scratch. If you need a bugfix for a particular package, you'd have wait until the fix reaches the package repository. When I was using Ubuntu, that was often until the next major release. None of this stuff is a problem with Nix, which allows for customization with very little effort.

It's easy to learn about Nix if you know where to look for. Nix Pills [1] would be a good start. The core language is the easiest part. It's JSON, but with functions and variables for proper abstraction. The documentation, while imperfect, is quite extensive compared to a majority of other high profile open source projects. If the documentation fails you, the Nixpkgs repository [2] is an even more rich source of information. The code is well organized, and I was able to get familiar with writing Nix packages fairly quickly by grepping the codebase. And finally, Nix has a sizable community so you could always ask if you're stuck.

[1]: https://nixos.org/guides/nix-pills/

[2]: https://github.com/NixOS/nixpkgs