This is a generic problem.

JS/TS/npm is just as bad with probably more build tools/frameworks.

Rust is a mess.

Go, well.

Even perl was quite complicated.

Yeah, but it's easily solved, with directives, headers, or make files that specify which language standard it follows. Better yet, you can use different syntax with different language standards, so it's clear which to follow. If a compiler can automatically figure whether I'm compiling C or C++, why can't a Python interpreter figure out if I'm running version two or three, of the same language?

> JS/TS/npm is just as bad with probably more build tools/frameworks.

This is flat out wrong. NPM packages by default are local to a directory. And I haven't seen a package rely on a specific minor version of node in literally years. Node's back compat is also great, there was one hiccup 5 or 6 years ago where a super popular native package was deprecated ago but that's been about it.

I can take current LTS node and run just about any package from the NPM repo written within the last 4 or 5 years and it will just work. Meanwhile plenty of python packages somehow need specific point releases. What the unholy hell.

Node version manager does exist, and it can be setup to work per directory, which is super cool, but I haven't needed NVM in literal years.