Here's a non-comprehensive list of issues I have with bundler. Don't get me wrong, I think in many ways it's a lot better than what we had before. But I also don't believe it's advanced much since its initial debut and think we can do better (and yes, I've tried on that front):

* You can't override a dependency:

This may very well be an issue with rubygems, but I'd have hoped bundler adoption would have fixed it if that's the case. Since dependencies and their versions cannot be overridden, transitive dependency conflicts are a constant minefield. The naive solution is to modify your gemspec to be overly permissive and I get pressure to do this frequently with gems I work on. But, now I'm asserting that my gem works with some hypothetical dependency that hasn't been release yet. And I've watched that blow up many times. Semantic versioning does not fix this.

* Dependencies can disappear on you:

Yanked gems irk me, but I appreciate their value in preventing the installation of an unwanted version when one isn't specified. However, it also explicitly blocks installation of a particular version, effectively ignoring the version specification in Gemfile. I may be romanticizing things here, but I don't recall ever seeing a non-SNAPSHOT published artifact being removed when I was working with maven or ivy.

What's frustrating is the gem still exists, but not in the index. I guess I would expect Bundler to realize I do want to install the version I've specified and fetch the file and do the installation. Ultimate control in my dependency graph should rest with me, not the whim of an upstream developer. I realize this is a rubygems.org thing, but it also strikes me as a solvable problem that many don't see as a problem. And it means that checking out historical copies of my app almost certainly will not run without changing the Gemfile(.lock), which seems like a core value in a dependency management tool.

In any event, you will only discover this when deploying to a machine that has come up since the gem was yanked. Common staging server and CI strategies don't catch this since it's essentially a race condition in the gem ecosystem. You simply won't find out until your deploy fails. It undermines any trust in the ecosystem and the only viable solutions are: 1) run your own gem server; 2) modify gemcutter to dismiss all yanks, or 3) vendor every gem your app uses.

* It has weird rules:

gem 'x', platform: :mri
gem 'y', platform: :jruby

works the way you'd expect, but

gem 'z', git: 'whatever', branch: 'mri_compat'
gem 'z', git: 'whatever', branch: 'jruby_compat'

will not. Apparently the dependency graph can't be resolved if the gem names aren't unique. The platform part isn't taken into consideration from what I gather. This made it really hard when I was trying to port some C ext. gems to a JRuby equivalent.

* It promotes bad practices:

This one is admittedly contentious, but given bundler was created basically for Rails 3 and Rails is the worst abuser, it's also hard to divorce the two. But requiring your entire dependency graph up front is just not a good idea. It's bad for performance and it's bad for memory. It's why apps take 30s to boot. Most non-trivial Rails apps I've come across are basically multi-tiered applications in a monolithic codebase. That means fog is getting loaded in controllers and haml is getting loaded in Sidekiq jobs. It's just a very odd situation. I've tried to defer loading, but this is a battle that's hardly worth fighting because of railties. Auditing every gem to see if it has a railtie is tiresome and invalidated as soon as a new version comes out. If a gem has a railtie, it needs to be required at a very specific point in the Rails boot cycle, otherwise your app just won't work the same. Figuring out that difference will likely drive you insane.

Likewise, every new gem created from Bundler has a gemspec that shells out to git at least once. So, if you have a dependency on a gem sourced from git in your Gemfile, you get to pay that cost on every app load. And if you end up somehow getting different results on different machines, then you're not really running the same thing, which seems odd to me for a dependency management tool.

* It's slow:

Gem installation has gotten a lot faster since the early days. And dependency resolution has gotten better as well. So, I'm not saying no work is being done here, but it is still slow. This is a complaint that is always levied at maven, too, so Bundler certainly isn't unique in this regard and I'd argue it got faster in a much shorter timeframe than maven did, so that's promising.

* It was designed for MRI:

This one may be unfair since I don't use rbx, but bundler wasn't really built with JRuby in mind. "bundle exec" is used everywhere now and it forks a process. Forking the JVM is anything but light. The solution is to use binstubs, which avoid the forking. But also means you can't really use both JRuby and MRI in the same codebase.

I really hope that Cargo learns from some of the pitfalls of Bundler. It seemed like Bundler didn't really learn from the pitfalls of other package managers. I wasn't involved with any of the design decisions, so I certainly don't want to say it was pulled together haphazardly. But it also seemed to overlook problems that tools like maven had solved over the past decade. I'm sure a fair bit of that had to do with the underlying rubygems system, but the distinction is also a bit moot from the user perspective.