Skip to Content
Course content

109: Semantic Versioning for Gems

Click on the "Edit" button in the top corner of the screen to edit your slide content.

What do those three numbers in a version string actually mean?

When you look at a gem version like 2.4.1, you're looking at Semantic Versioning, or SemVer. It's not just a random sequence of numbers; it's a contract you're making with everyone who uses your code. I've seen a lot of developers just bump the middle number whenever they feel like it, but that's a recipe for breaking your users' apps.

Here is the breakdown: Major.Minor.Patch.

  • Patch (1): You fixed a bug. No new features, and nothing changed in how the gem is called. It's a "safe" update.
  • Minor (4): You added a new feature, but you didn't break any existing functionality. Someone using version 2.3.0 can upgrade to 2.4.0 without changing a single line of their own code.
  • Major (2): You changed something fundamentally. Maybe you renamed a primary method or removed a deprecated class. This is a "breaking change." If a user upgrades from 2.x to 3.0, their app will likely crash until they update their code to match your new API.

Imagine you're building a gem called stripe_helper. If you change the method process_payment to execute_transaction, that's a Major bump. If you just add a new method refund_payment, that's a Minor bump.

How do I use these versions to prevent "Dependency Hell"?

You've probably seen the ~> operator in a .gemspec or a Gemfile. We call this the "pessimistic operator." It's the secret sauce that makes SemVer actually useful in Ruby. It basically tells RubyGems: "I want at least this version, but don't give me the version that breaks everything."

Check out the difference in how these are handled:

# Allow any patch updates, but lock the minor version
spec.add_dependency "faraday", "~> 1.4.0" 
# This allows 1.4.1, 1.4.2... but NOT 1.5.0

# Allow any minor updates, but lock the major version
spec.add_dependency "faraday", "~> 1.4" 
# This allows 1.4.1, 1.5.0, 1.9.9... but NOT 2.0.0

I almost always recommend using the two-digit constraint (~> 1.4) for dependencies. It lets you get those sweet new features and bug fixes from the library author without risking a total system collapse caused by a Major version leap.

What happens if I accidentally push a breaking change as a minor update?

Look, it happens to the best of us. You think a change is "backward compatible," you bump the Minor version, push to RubyGems, and then your Slack starts blowing up because half your users' CI pipelines are failing.

In the Ruby ecosystem, you can "yank" a gem version using gem yank [gem_name] -v [version]. This removes the version from RubyGems so new installs won't grab it. But be careful: yanking is a last resort. If people have already locked that version in their Gemfile.lock, yanking won't fix their local environment—it just prevents new people from hitting the wall.

The professional move? Admit the mistake, immediately release a new Patch version that reverts the breaking change, and then plan a proper Major release for that feature. It's better to have a version history that looks a bit messy than a user base that doesn't trust your updates.




📋 Practical Task

Correcting the Version Constraints for the stripe_helper Gem

You are maintaining the stripe_helper gem. Currently, your stripe_helper.gemspec has a dependency on the json gem that is far too restrictive, preventing users from getting important security patches. It also has a dependency on rest-client that is too loose, which will cause the gem to crash when rest-client eventually hits version 3.0.0.

Your Task: Edit the following .gemspec snippet to fix these two constraints:

# CURRENT BROKEN CODE
Gem::Specification.new do |spec|
  spec.name          = "stripe_helper"
  spec.version       = "1.0.0"
  
  # This is too restrictive (only allows exactly 2.3.0)
  spec.add_dependency "json", "2.3.0" 
  
  # This is too loose (allows 3.0.0, 4.0.0, etc., which will break the gem)
  spec.add_dependency "rest-client", ">= 2.0.0" 
end

Modify the json dependency to allow any patch version of 2.3.x, and modify the rest-client dependency to allow any version within the 2.x series, but block 3.0.0.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.