It most definitely was not a solved problem when Perl was big, as far as I know Ruby is the language that finally solved it with bundler, which was released after Rails was, so that's like 2007 or something.
The recipe for the 'solved' deployment:
- a compiler that ships with a build tool (so building is homogenous in the community)
- a centralized or at least uniformly accessible package repository (so dependency acquisition is homogenous in the community)
- a common file format for describing dependencies (so dependency resolution is homogenous in the community)
- a common file format for locking dependency versions (so deployment can be done reliably without vendoring dependencies)
- optional but very nice: a tool for managing compiler versions so it's easy to switch/upgrade projects
Any programming language that has all of these boxes ticked is a modern programming language in my book. As far as I know Ruby is the first that ticked all of them, but other ones I've used that have this: Node.js, Go, Rust, Haskell, Python (though it's a bit messy). I'm pretty sure C# checks them as well nowadays, but I haven't used it in over 5 years so I'm not sure. Same for Java.
Somebody needs to tell the Racket people about this. Dependency management in Racket is still C-style no-version-pinning-anything-goes. The third party dependency managers (see below) are all primitive and do not support multiple versions of the same libraries which is a standard feature in Go/Node.js/Rust/Java class loaders
The recipe for the 'solved' deployment:
Any programming language that has all of these boxes ticked is a modern programming language in my book. As far as I know Ruby is the first that ticked all of them, but other ones I've used that have this: Node.js, Go, Rust, Haskell, Python (though it's a bit messy). I'm pretty sure C# checks them as well nowadays, but I haven't used it in over 5 years so I'm not sure. Same for Java.