Jean Boussier (byroot) published a post this weekend cataloguing Bundler features that exist but aren’t well-known. He’s one of the people actively maintaining Bundler, so when he writes about what’s missing or underused, it’s worth paying attention. Reading it got me thinking less about the features he mentioned and more about the broader shape of what Bundler still lacks compared to where the rest of the package management world has landed.
Bundler is genuinely good software. It solved Ruby’s dependency hell back when the community needed it, and it introduced the lockfile pattern that has since become table stakes everywhere. But the surrounding ecosystem has not stood still, and the gap between what Bundler offers and what Cargo or modern npm offers is wide enough to notice daily.
Workspaces Are the Obvious Gap
If you’ve worked in a Rust or JavaScript monorepo, you’ve used workspace support. Cargo lets you declare a [workspace] section in your root Cargo.toml, and every member crate shares a single Cargo.lock. Dependencies get deduplicated across the whole workspace; you run cargo test --workspace and everything builds together with a single dependency resolution pass.
Npm has had workspaces since v7, released in late 2020. The pattern is the same: a root package.json declares workspace globs, member packages share a single node_modules, and the lock file covers the whole tree.
Bundler has nothing equivalent. If you maintain a monorepo with multiple gems, you either:
- Give each gem its own
Gemfileand accept that lock files diverge and you’ll spend time chasing version inconsistencies between packages - Put everything under a single
Gemfileat the root, which works until you want to publish individual gems with their own declared dependencies - Use a tool like gel or reach for custom scripting
The bundler-multilock plugin from Instructure is an interesting third-party attempt, but needing a plugin for something this fundamental tells you something. Shopify’s internal tooling handles a lot of this through convention, but convention doesn’t ship with Bundler.
This matters more as Ruby projects grow. Rails applications that split into engines, gem-based plugin systems, libraries maintained alongside their consuming applications: all of these hit the workspace problem eventually.
Checksum Verification Arrived, But Late
Bundler 2.4 added checksum verification to the lockfile format. The Gemfile.lock now optionally stores CHECKSUMS for each gem, and bundle install can verify that what you’re installing matches what was originally locked. This is a real security improvement and it was a long time coming.
For comparison, Cargo has shipped with content-addressed, checksum-verified downloads since its earliest versions. The Cargo.lock stores a checksum field for every dependency, and there is no opt-in required. npm added package-lock integrity using Subresource Integrity hashes years ago.
The gap here wasn’t just technical. It reflects a cultural difference: the Ruby ecosystem historically trusted RubyGems.org to be authoritative, and tooling around verification felt less urgent. The 2023 RubyGems supply chain incident pushed this higher on the priority list. The feature landed, which is good. But a lot of Ruby code in production is still running older Bundler versions with lockfiles that have no checksums at all.
Running bundle lock --add-checksums on existing projects is something most teams haven’t gotten around to yet, and there’s no tooling to enforce it in CI by default.
Security Auditing Is Not Bundler’s Job, Apparently
If you want to check your Ruby dependencies against known CVEs, you install bundler-audit separately. It’s a fine tool. You run bundle exec bundle-audit check --update and it fetches the ruby-advisory-db and reports on vulnerable gem versions in your lockfile.
In Cargo, cargo audit is a separate crate too, but it’s developed under the RustSec organization with tight integration into the broader Rust security ecosystem. Python’s pip audit was added to pip itself. GitHub’s Dependabot handles this automatically for many ecosystems.
The philosophical question is whether security auditing belongs in the package manager or adjacent to it. Reasonable people disagree. But the practical outcome for Ruby projects is that bundle audit is one more thing to add to a CI pipeline, one more tool to version and maintain, and something that many smaller projects simply never get around to adding.
bundle exec Has a Cost Nobody Talks About
Every time you run bundle exec some-command, you’re paying the cost of Bundler’s activation code: loading the lockfile, activating the correct gem versions, adjusting the load path, then finally executing your command. On a warm system with a cold Ruby process, this is typically in the 200-500ms range depending on the number of gems in the bundle.
For interactive development this is mostly invisible. For test runners that invoke many subprocesses, or for shell scripting that calls a Ruby CLI tool in a loop, it adds up. The spring preloader and bootsnap from Shopify both attack this from different angles, but they’re additional dependencies with their own configuration surface.
Rust’s cargo has a fundamentally different model: compiled binaries don’t need a runtime dependency resolver to start. This comparison isn’t entirely fair since Ruby is interpreted, but it’s worth understanding what the cost is and why other ecosystems don’t have an equivalent concept.
bundle update —conservative Is Underused
One thing byroot’s post is presumably getting at (given his role in the project) is that Bundler has features that developers don’t know exist. The --conservative flag on bundle update is a good example. By default, bundle update GEM will also update any of that gem’s transitive dependencies if doing so allows a newer version. --conservative restricts the update to only the named gem, touching nothing else.
# Updates rails and anything it needs updated to get there
bundle update rails
# Updates only rails itself, leaves transitive deps alone if possible
bundle update --conservative rails
The distinction matters when you’re trying to make a minimal, reviewable change. A bundle update rails on a large app can produce a diff of twenty gems, making it hard to reason about what actually changed. --conservative gives you something closer to what npm’s --save-exact workflow produces.
Similarly, bundle update --patch restricts updates to patch-level changes only, and --minor to minor-level. These are good tools for teams that want to batch small updates separately from major version bumps. They don’t appear in most team’s workflows because the documentation doesn’t surface them prominently.
The Private Gem Server Story
For teams running private gem servers, Bundler’s source block syntax handles the basic case:
source 'https://gems.example.com' do
gem 'our-internal-gem'
end
The source block syntax, added to prevent the global source confusion vulnerability, is actually a case where Bundler got ahead of other package managers on a real security concern. npm took until late 2021 to ship equivalent scoping for private registry packages.
But Bundler’s credentials management for private sources is still awkward. You set credentials via bundle config, which writes to a ~/.bundle/config file, or you embed them in the source URL (which is terrible for secrets). There’s no first-class concept of credential helpers the way npm has with npm_config_ environment variables or Docker’s credential store integration. Most teams end up managing this through CI-specific environment variables and some shell scripting.
What Would Closing the Gap Look Like
Workspace support is the largest missing piece and the hardest to add without breaking compatibility. The lockfile format would need to change, and the resolution algorithm would need to handle multi-root dependency graphs. This is not a small project, but it is the kind of thing that other communities have decided is worth doing.
Checksum verification being opt-in should probably change to opt-out, at least for new projects generated with bundle init. The security benefit is real and the cost is minimal.
First-class auditing, even if it’s bundle audit shipped as a subcommand rather than a separate gem, would lower the barrier for projects that haven’t gotten around to setting it up.
Bundler’s history is one of solving real problems incrementally, often ahead of where other ecosystems were. The lockfile was ahead of its time. The source block security model was proactive. The development has not stopped. But the pace of improvement in competing ecosystems has been faster in recent years, and some of the gaps that felt theoretical five years ago feel concrete now.
The post byroot wrote is worth reading for what it reveals about features that already exist and aren’t being used. The next question worth asking is what should exist that doesn’t.