Overview
Semantic Versioning (SemVer) provides a formal specification for version numbers that conveys meaning about the underlying changes in each release. The specification defines a versioning scheme based on three numeric components: MAJOR.MINOR.PATCH. Each component increments according to specific rules tied to the type of changes introduced.
The specification originated from the need to solve "dependency hell"—the situation where projects become difficult to upgrade due to unclear versioning practices. When version numbers lack consistent meaning, developers cannot determine whether upgrading a dependency will break their application. Semantic Versioning addresses this by establishing a social contract: version numbers communicate compatibility guarantees.
A semantic version takes the form X.Y.Z where X is the major version, Y is the minor version, and Z is the patch version. Each position increments as an integer, and the version 1.4.13 indicates major version 1, minor version 4, and patch version 13. Pre-release versions and build metadata can extend this format with additional labels.
2.3.1 # Standard semantic version
1.0.0-alpha # Pre-release version
1.0.0+build.1 # Version with build metadata
2.1.0-rc.1+20130313144700 # Both pre-release and build metadata
The specification matters because it transforms version numbers from arbitrary labels into meaningful signals. Libraries that follow SemVer enable automated dependency management, safe upgrades, and clear communication about breaking changes. Ruby projects use semantic versioning extensively, from Rails to small gems, making it fundamental to the Ruby ecosystem.
Key Principles
Semantic Versioning operates on eight core principles that define when and how to increment each version component. These principles create a predictable versioning scheme that both humans and automated tools can interpret.
Version Format Structure
The version number MAJOR.MINOR.PATCH increments according to the nature of changes. The MAJOR version increments when incompatible API changes occur. The MINOR version increments when functionality is added in a backward-compatible manner. The PATCH version increments for backward-compatible bug fixes. Once a versioned package releases, the contents of that version must not change—any modifications require a new version release.
Initial Development Phase
Projects begin at version 0.1.0 for initial development. The 0.y.z version range indicates the public API is unstable and may change at any time. Anything may change during major version zero—breaking changes can occur in minor or patch releases. Major version zero exists for rapid development without the commitment to API stability. Once the API stabilizes for production use, the project releases version 1.0.0.
Version Incrementing Rules
Each version component resets lower components when incremented. Incrementing the major version resets minor and patch to zero (1.4.7 → 2.0.0). Incrementing the minor version resets patch to zero (1.4.7 → 1.5.0). The patch version increments without affecting other components (1.4.7 → 1.4.8).
Versions compare each component numerically from left to right. Version 1.10.0 is greater than 1.9.0 because the minor component 10 exceeds 9, despite 10 having more digits. Version 2.0.0 is greater than 1.99.99 because the major component takes precedence.
Pre-release Versions
Pre-release versions append a hyphen and a series of dot-separated identifiers after the patch version. These identifiers may contain ASCII alphanumerics and hyphens. Numeric identifiers compare as integers, while alphanumeric identifiers compare lexically. Pre-release versions have lower precedence than the associated normal version.
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-rc.1 < 1.0.0
Build Metadata
Build metadata appends a plus sign and dot-separated identifiers after the patch version or pre-release version. Build metadata does not affect version precedence—two versions that differ only in build metadata have the same precedence. Build metadata conveys information about the build process without impacting semantic ordering.
1.0.0+20130313144700
1.0.0-beta+exp.sha.5114f85
Backward Compatibility Guarantees
The specification defines compatibility at the API level. A backward-compatible change allows existing code to continue functioning without modification. Adding a new public method maintains compatibility, while changing a method's signature breaks compatibility. Internal implementation changes that preserve the public interface are compatible.
Public API definition varies by language and project. In Ruby, the public API typically includes all public methods on public classes and modules, excluding those prefixed with underscores or marked private. Documentation should clearly define what constitutes the public API, as the versioning scheme only provides meaning relative to that definition.
Dependency Specification
Projects specify dependencies using version ranges that express compatibility requirements. A dependency on ~> 2.1 accepts any version from 2.1.0 up to but not including 3.0.0. This range assumes the dependency follows semantic versioning and maintains backward compatibility within the major version. The version range >= 2.1.0, < 3.0.0 expresses the same constraint explicitly.
Ruby Implementation
Ruby provides several mechanisms for working with semantic versions, from parsing and comparing versions to managing gem dependencies according to SemVer principles.
Gem::Version Class
Ruby's Gem::Version class parses and compares version numbers following a scheme compatible with semantic versioning. The class handles version comparison, parsing, and validation. While Gem::Version predates the SemVer specification and has slight differences, it works effectively for semantic version operations.
version = Gem::Version.new('2.3.1')
version.segments # => [2, 3, 1]
# Version comparison
Gem::Version.new('1.0.0') < Gem::Version.new('2.0.0') # => true
Gem::Version.new('1.10.0') > Gem::Version.new('1.9.0') # => true
# Pre-release handling
Gem::Version.new('1.0.0.pre') < Gem::Version.new('1.0.0') # => true
Gemspec Version Declaration
Ruby gems declare their version in the gemspec file using semantic versioning. The version typically exists in a separate version.rb file that both the gemspec and application can reference. This pattern allows runtime access to the version number.
# lib/my_gem/version.rb
module MyGem
VERSION = '2.3.1'
end
# my_gem.gemspec
require_relative 'lib/my_gem/version'
Gem::Specification.new do |spec|
spec.name = 'my_gem'
spec.version = MyGem::VERSION
end
Dependency Version Constraints
Gemfiles and gemspecs specify dependency versions using operators that express SemVer compatibility expectations. The pessimistic operator ~> locks the version at the specified precision, allowing only patch-level updates for two-component versions or minor updates for three-component versions.
# Gemfile
gem 'rails', '~> 7.0.0' # >= 7.0.0, < 7.1.0
gem 'rack', '~> 2.2' # >= 2.2.0, < 3.0.0
gem 'pg', '>= 1.2', '< 2.0' # Explicit range
# Gemspec
spec.add_dependency 'faraday', '~> 2.0'
spec.add_development_dependency 'rspec', '~> 3.12'
SemVer Gem
The semantic gem provides a pure SemVer implementation that strictly follows the semantic versioning specification. This gem offers parsing, comparison, and validation according to the official spec, including proper pre-release and build metadata handling.
require 'semantic'
version = Semantic::Version.new('2.3.1-beta.1+build.123')
version.major # => 2
version.minor # => 3
version.patch # => 1
version.pre # => 'beta.1'
version.build # => 'build.123'
# Strict SemVer comparison
v1 = Semantic::Version.new('1.0.0-alpha')
v2 = Semantic::Version.new('1.0.0')
v1 < v2 # => true
# Version bumping
version = Semantic::Version.new('1.4.7')
version.increment!(:major) # => '2.0.0'
version.increment!(:minor) # => '2.1.0'
version.increment!(:patch) # => '2.1.1'
Bundler Version Resolution
Bundler resolves gem dependencies by finding version combinations that satisfy all constraints. The resolver treats version requirements as a constraint satisfaction problem, selecting the newest versions that meet all dependency specifications while respecting semantic versioning compatibility.
# Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 7.0.0'
# Bundler resolves to latest 7.0.x version available
# Assumes Rails follows SemVer within 7.0.x range
# Gemfile.lock records exact versions
# DEPENDENCIES
# rails (~> 7.0.0)
#
# BUNDLED WITH
# rails (7.0.8)
Version Bumping in Rake Tasks
Projects often automate version bumping through Rake tasks that modify the version file and create git tags. These tasks enforce the semantic versioning increment rules programmatically.
# lib/tasks/version.rake
namespace :version do
desc 'Bump major version'
task :bump_major do
current = MyGem::VERSION
parts = current.split('.')
new_version = "#{parts[0].to_i + 1}.0.0"
update_version_file(new_version)
create_git_tag(new_version)
end
desc 'Bump minor version'
task :bump_minor do
current = MyGem::VERSION
parts = current.split('.')
new_version = "#{parts[0]}.#{parts[1].to_i + 1}.0"
update_version_file(new_version)
create_git_tag(new_version)
end
end
Practical Examples
Determining the appropriate version bump requires analyzing the nature of changes between releases. These examples demonstrate common scenarios and their corresponding version increments.
Adding a New Method to a Class
Adding a public method to an existing class adds functionality without breaking existing code. This change represents a backward-compatible feature addition, triggering a minor version bump.
# Version 1.2.0
class DataParser
def parse_csv(data)
# implementation
end
end
# Version 1.3.0 - Added JSON parsing
class DataParser
def parse_csv(data)
# implementation
end
def parse_json(data) # New method
# implementation
end
end
The version increments from 1.2.0 to 1.3.0 because the change adds functionality while maintaining compatibility. Code using the parse_csv method continues working without modification.
Changing Method Parameters
Modifying a method's required parameters breaks backward compatibility because existing calls with the old parameter list will fail. This change requires a major version bump.
# Version 2.1.0
class FileProcessor
def process(file_path)
# implementation
end
end
# Version 3.0.0 - Breaking change
class FileProcessor
def process(file_path, options) # Added required parameter
# implementation
end
end
The version increments from 2.1.0 to 3.0.0 because existing code calling process with one argument will raise an ArgumentError. Adding an optional parameter with a default value would maintain compatibility and only require a minor bump.
Fixing a Bug Without API Changes
Bug fixes that correct behavior without modifying the public API increment the patch version. The fix changes output or behavior but preserves the method signature and compatibility.
# Version 1.4.2
class Calculator
def average(numbers)
numbers.sum / numbers.size # Bug: uses integer division
end
end
# Version 1.4.3 - Bug fix
class Calculator
def average(numbers)
numbers.sum.to_f / numbers.size # Fixed: returns float
end
end
The version increments from 1.4.2 to 1.4.3 because the change fixes incorrect behavior without altering the method signature. While the output changes, the API contract remains the same.
Adding Optional Parameters
Adding optional parameters with default values maintains backward compatibility. Existing calls work without modification, while new calls can utilize the additional parameters.
# Version 2.4.0
class ImageResizer
def resize(image, width, height)
# implementation
end
end
# Version 2.5.0 - Added optional parameter
class ImageResizer
def resize(image, width, height, maintain_aspect_ratio: true)
# implementation
end
end
The version increments from 2.4.0 to 2.5.0 because the addition adds functionality while maintaining compatibility. Code calling resize with three arguments continues functioning identically.
Changing Return Type
Modifying a method's return type constitutes a breaking change when the new type lacks the interface of the old type. Code depending on specific return type methods will break.
# Version 1.8.0
class UserRepository
def find_active_users
User.where(active: true).to_a # Returns Array
end
end
# Version 2.0.0 - Breaking change
class UserRepository
def find_active_users
User.where(active: true) # Returns ActiveRecord::Relation
end
end
The version increments from 1.8.0 to 2.0.0 because code expecting an Array cannot call ActiveRecord::Relation methods without errors. The change breaks the implicit contract about the return value's interface.
Deprecating Methods
Deprecating a method without removing it maintains backward compatibility when the deprecated method still functions. This scenario allows a minor version bump for the deprecation, with major version bump deferred until removal.
# Version 3.2.0
class PaymentProcessor
def process_payment(amount)
charge_customer(amount)
end
def charge_customer(amount)
# implementation
end
end
# Version 3.3.0 - Deprecation warning added
class PaymentProcessor
def process_payment(amount)
warn "[DEPRECATED] process_payment is deprecated, use charge_customer instead"
charge_customer(amount)
end
def charge_customer(amount)
# implementation
end
end
# Version 4.0.0 - Method removed
class PaymentProcessor
def charge_customer(amount)
# implementation
end
# process_payment removed entirely
end
Version 3.3.0 adds the deprecation while maintaining functionality. Version 4.0.0 removes the deprecated method, breaking compatibility and requiring the major bump.
Tools & Ecosystem
The Ruby ecosystem provides tools for managing semantic versions, automating version bumps, and validating dependency compatibility.
Semantic Gem
The semantic gem implements the complete Semantic Versioning 2.0.0 specification. It provides parsing, comparison, and manipulation of semantic versions with strict adherence to the spec.
# Gemfile
gem 'semantic'
# Usage
require 'semantic'
version = Semantic::Version.new('1.2.3-pre.1+build.4')
# Access components
version.major # => 1
version.minor # => 2
version.patch # => 3
version.pre # => 'pre.1'
version.build # => 'build.4'
# Comparison
Semantic::Version.new('1.2.3') > Semantic::Version.new('1.2.2') # => true
Semantic::Version.new('1.0.0-alpha') < Semantic::Version.new('1.0.0') # => true
# Incrementing
v = Semantic::Version.new('1.9.9')
v.increment!(:patch) # => #<Semantic::Version "1.9.10">
v.increment!(:minor) # => #<Semantic::Version "1.10.0">
v.increment!(:major) # => #<Semantic::Version "2.0.0">
Bump Gem
The bump gem automates version bumping in Ruby projects. It modifies version files and creates git tags according to semantic versioning principles.
# Gemfile
gem 'bump', require: false
# Command line usage
rake bump:patch # 1.2.3 -> 1.2.4
rake bump:minor # 1.2.3 -> 1.3.0
rake bump:major # 1.2.3 -> 2.0.0
rake bump:pre # 1.2.3 -> 1.2.4.pre.1
# Programmatic usage
require 'bump'
Bump::Bump.run('major') # Bumps major version
Gem Version Validation
The gem_version_validator gem validates that gem versions follow semantic versioning conventions. It integrates with CI pipelines to enforce versioning standards.
# In CI configuration
gem install gem_version_validator
gem_version_validator validate
# Checks:
# - Version format matches X.Y.Z
# - No leading zeros in components
# - Pre-release format validity
# - Build metadata format validity
Bundler Audit
Bundler Audit checks gem dependencies for known security vulnerabilities. It leverages semantic versioning to identify affected version ranges and recommend safe upgrades.
# Gemfile
gem 'bundler-audit', require: false
# Check dependencies
bundle audit check
# Output identifies vulnerable versions:
# Name: actionpack
# Version: 5.2.0
# Advisory: CVE-2020-8164
# Criticality: High
# URL: https://groups.google.com/g/rubyonrails-security/...
# Solution: upgrade to >= 5.2.4.3
Gemnasium Integration
Gemnasium monitors gem dependencies and alerts when updates are available. It respects semantic versioning constraints to recommend safe upgrades within compatibility ranges.
Dependabot
Dependabot automates dependency updates by creating pull requests for new versions. It understands semantic versioning and can configure different update strategies for major versus minor/patch releases.
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: 'bundler'
directory: '/'
schedule:
interval: 'weekly'
# Separate PR strategy for majors vs minors
open-pull-requests-limit: 10
Semantic Release
The semantic-release tool automates version management and package publishing. While primarily used in JavaScript projects, Ruby projects can adapt the workflow using similar automation that analyzes commit messages to determine version bumps.
License Finder
License Finder analyzes gem dependencies and their licenses. It uses semantic versioning to track license changes across versions, alerting when dependency updates introduce different licensing terms.
Common Patterns
Projects adopt consistent patterns for managing semantic versions across development, release, and deployment workflows.
Version File Organization
Most Ruby gems maintain version information in a dedicated version.rb file within the library namespace. This organization separates version declaration from implementation and allows runtime access to the version.
# lib/my_gem/version.rb
module MyGem
VERSION = '2.4.1'
end
# lib/my_gem.rb
require 'my_gem/version'
module MyGem
class Error < StandardError; end
# Additional implementation
end
# my_gem.gemspec
require_relative 'lib/my_gem/version'
Gem::Specification.new do |spec|
spec.version = MyGem::VERSION
end
Git Tag Workflow
Projects tag releases with version numbers prefixed by 'v' to mark specific commits as releases. The tag serves as an immutable reference to the exact code state for each version.
# After bumping version to 2.4.1
git add lib/my_gem/version.rb
git commit -m "Bump version to 2.4.1"
git tag v2.4.1
git push origin main --tags
Automation tools create these tags programmatically during the release process, ensuring consistency between version numbers and git tags.
Changelog Maintenance
Projects maintain a CHANGELOG.md file documenting changes for each version. The changelog groups changes by type (Added, Changed, Deprecated, Removed, Fixed, Security) and includes version numbers with release dates.
# Changelog
## [2.4.1] - 2024-01-15
### Fixed
- Corrected memory leak in batch processor
- Fixed timezone handling in date parser
## [2.4.0] - 2024-01-10
### Added
- Added support for custom serialization formats
- Introduced configuration validation
### Changed
- Improved error messages for invalid input
Pre-release Version Workflow
Projects use pre-release versions for testing before official releases. The typical progression moves through alpha, beta, and release candidate stages.
1.0.0-alpha.1 # Initial alpha release
1.0.0-alpha.2 # Second alpha with fixes
1.0.0-beta.1 # Feature-complete beta
1.0.0-beta.2 # Beta with bug fixes
1.0.0-rc.1 # Release candidate
1.0.0-rc.2 # Second release candidate
1.0.0 # Official release
Each pre-release version allows testing in progressively realistic environments before committing to the final version number.
Major Version Zero Graduation
Projects transition from 0.y.z to 1.0.0 when the public API stabilizes and the project is ready for production use. This transition signals API stability commitment.
0.1.0 # Initial development release
0.2.0 # Added core features
0.8.0 # Feature complete, entering stabilization
0.9.0 # API finalized, extensive testing
0.9.1 # Bug fixes in final testing
1.0.0 # Production-ready, stable API
The graduation to 1.0.0 represents a commitment to semantic versioning compatibility guarantees going forward.
Monorepo Versioning Strategies
Projects containing multiple gems in a monorepo choose between unified versioning (all packages share one version) or independent versioning (each package has its own version). Unified versioning simplifies coordination but may result in version jumps without changes to specific packages.
# Unified versioning
gems/
core/version.rb # VERSION = '3.2.1'
extensions/version.rb # VERSION = '3.2.1'
# Independent versioning
gems/
core/version.rb # VERSION = '3.2.1'
extensions/version.rb # VERSION = '2.1.0'
Version Constraints in Gemspecs
Runtime dependencies in gemspecs use pessimistic version constraints to allow compatible updates while preventing breaking changes. Development dependencies often allow wider version ranges since they don't affect production.
Gem::Specification.new do |spec|
# Runtime dependencies: strict constraints
spec.add_dependency 'activerecord', '~> 7.0.0'
spec.add_dependency 'faraday', '>= 2.0', '< 3.0'
# Development dependencies: looser constraints
spec.add_development_dependency 'rspec', '~> 3.0'
spec.add_development_dependency 'rubocop', '>= 1.0'
end
Common Pitfalls
Developers frequently misunderstand or misapply semantic versioning principles, leading to version numbers that fail to communicate accurate compatibility information.
Incrementing Patch for New Features
Adding new functionality requires a minor version bump, not a patch bump. The patch position exists exclusively for backward-compatible bug fixes. Incrementing the patch version for a new method misleads consumers about the nature of changes.
# INCORRECT - Should be 1.3.0, not 1.2.1
# Version 1.2.0
class ApiClient
def get(path)
# implementation
end
end
# Version 1.2.1 (WRONG)
class ApiClient
def get(path)
# implementation
end
def post(path, data) # New feature
# implementation
end
end
The version should increment to 1.3.0 because adding the post method introduces new functionality rather than fixing existing bugs.
Treating Bug Fixes as Breaking Changes
Bug fixes that correct documented behavior do not constitute breaking changes, even when the output changes. If the new behavior matches the documented contract, increment the patch version.
# Version 2.1.4 - Bug in implementation
class StringFormatter
# Documentation states: "Returns a titlecased string"
def titlecase(str)
str.upcase # Bug: returns uppercase instead of titlecase
end
end
# Version 2.1.5 (CORRECT) - Bug fix
class StringFormatter
def titlecase(str)
str.split.map(&:capitalize).join(' ') # Fixed to match documentation
end
end
The patch increment is appropriate because the fix aligns implementation with documentation. Code relying on the documented behavior continues working correctly.
Breaking Compatibility in Minor Versions
Changing method signatures, removing public methods, or altering return types in minor versions violates semantic versioning guarantees. These changes require major version increments.
# Version 3.4.0
class DataValidator
def validate(data)
data.all? { |item| item.valid? }
end
end
# Version 3.5.0 (WRONG - should be 4.0.0)
class DataValidator
def validate(data, strict: false) # Changed required to optional parameter
return data.all? { |item| item.valid? } unless strict
data.all? { |item| item.valid? && item.complete? }
end
end
While adding an optional parameter typically maintains compatibility, changing a required parameter to optional with default behavior that differs from the original creates subtle breaking changes that merit a major version.
Resetting Versions Incorrectly
Failing to reset lower version components when incrementing higher components creates confusing version sequences. A minor bump should reset the patch to zero.
# INCORRECT sequence
1.2.3
1.3.3 # WRONG - should be 1.3.0
1.3.4
# CORRECT sequence
1.2.3
1.3.0 # Reset patch when minor increments
1.3.1
Reusing Version Numbers
Publishing different content under the same version number breaks the fundamental immutability principle. Once a version publishes, it must never change.
# WRONG: Modifying version 2.1.0 after release
# Initial release of 2.1.0
Gem::Specification.new do |spec|
spec.version = '2.1.0'
# ... initial implementation
end
# Later modifying and re-releasing 2.1.0 (NEVER DO THIS)
# Should be 2.1.1 or appropriate version
The correct approach creates a new version for any changes, even small fixes applied immediately after release.
Misunderstanding Zero Major Version
Treating 0.y.z versions as stable contradicts the specification. Major version zero indicates unstable API where breaking changes can occur in any release. Projects should avoid extended 0.x releases if they intend to provide stability.
# Project stuck at 0.x with stable API for years
0.1.0 # 2020-01-15
0.2.0 # 2020-03-20
# ... extensive use in production
0.28.0 # 2024-01-10 - Still 0.x despite stability
# Should transition to 1.0.0 once stable
0.28.0 # Final 0.x release
1.0.0 # Stable API committed
Projects providing production-level stability should graduate to 1.0.0 to signal semantic versioning compliance.
Ignoring Dependency Version Constraints
Specifying overly broad version constraints (>= 1.0) or overly narrow constraints (= 2.1.0) causes problems. Broad constraints allow breaking changes, while narrow constraints prevent compatible updates.
# PROBLEMATIC dependency specifications
spec.add_dependency 'httparty', '>= 0.1' # Too broad, allows breaking changes
spec.add_dependency 'nokogiri', '= 1.12.5' # Too narrow, blocks security patches
# BETTER dependency specifications
spec.add_dependency 'httparty', '~> 0.21' # Allows patches and minors in 0.21.x
spec.add_dependency 'nokogiri', '~> 1.12' # Allows updates to 1.x
Version Bumping for Internal Changes
Internal refactoring, performance improvements, or code reorganization that preserves the public API should increment the patch version, not minor or major versions.
# Version 1.8.0
class ReportGenerator
def generate(data)
slow_implementation(data)
end
private
def slow_implementation(data)
# inefficient approach
end
end
# Version 1.8.1 (CORRECT) - Performance improvement
class ReportGenerator
def generate(data)
fast_implementation(data)
end
private
def fast_implementation(data)
# optimized approach
end
end
The patch increment is appropriate because the public API remains unchanged despite the internal refactoring.
Reference
Version Component Increment Rules
| Increment | When To Use | Version Change | Lower Components |
|---|---|---|---|
| MAJOR | Incompatible API changes | 1.4.7 → 2.0.0 | Reset to zero |
| MINOR | Backward-compatible new functionality | 1.4.7 → 1.5.0 | Reset patch to zero |
| PATCH | Backward-compatible bug fixes | 1.4.7 → 1.4.8 | No reset |
Breaking vs Compatible Changes
| Change Type | Example | Version Increment |
|---|---|---|
| Remove public method | Deleted authenticate method | MAJOR |
| Add required parameter | New mandatory argument to existing method | MAJOR |
| Change return type | Array to Hash return value | MAJOR |
| Rename public method | authenticate to login | MAJOR |
| Remove optional parameter | Deleted optional argument | MAJOR |
| Add public method | New helper method added | MINOR |
| Add optional parameter with default | New keyword argument with default value | MINOR |
| Deprecate without removing | Mark method deprecated, still functional | MINOR |
| Fix documented behavior | Correct bug to match documentation | PATCH |
| Performance improvement | Optimize algorithm, same output | PATCH |
| Internal refactoring | Code reorganization, API unchanged | PATCH |
| Security patch | Fix vulnerability, API unchanged | PATCH |
Version Comparison Examples
| Version A | Operator | Version B | Result | Reason |
|---|---|---|---|---|
| 1.0.0 | < | 2.0.0 | true | Major component precedence |
| 1.9.0 | < | 1.10.0 | true | Numeric comparison, not lexical |
| 1.0.0 | < | 1.0.1 | true | Patch component comparison |
| 1.0.0-alpha | < | 1.0.0 | true | Pre-release has lower precedence |
| 1.0.0-alpha | < | 1.0.0-beta | true | Lexical comparison of pre-release |
| 1.0.0-1 | < | 1.0.0-2 | true | Numeric pre-release comparison |
| 1.0.0+build.1 | == | 1.0.0+build.2 | true | Build metadata ignored |
Pre-release Identifier Precedence
| Type | Example | Comparison Method |
|---|---|---|
| Numeric only | 1.0.0-1 | Compared as integers |
| Alphanumeric | 1.0.0-alpha | Compared lexically in ASCII sort order |
| Mixed identifiers | 1.0.0-alpha.1 | Each identifier compared left to right |
| Length matters | 1.0.0-alpha.1 vs 1.0.0-alpha.1.2 | Shorter version has lower precedence |
Gem::Version API
| Method | Purpose | Example |
|---|---|---|
| new | Create version object | Gem::Version.new('1.2.3') |
| segments | Get version components | version.segments → [1, 2, 3] |
| comparison operators | Compare versions | v1 < v2 |
| bump | Increment last segment | version.bump → increments rightmost |
| release | Get non-prerelease version | Gem::Version.new('1.0.pre').release → '1.0' |
| prerelease? | Check if prerelease | version.prerelease? → boolean |
Semantic::Version API
| Method | Purpose | Example |
|---|---|---|
| major | Access major component | version.major → 1 |
| minor | Access minor component | version.minor → 2 |
| patch | Access patch component | version.patch → 3 |
| pre | Access pre-release identifier | version.pre → 'alpha.1' |
| build | Access build metadata | version.build → 'build.456' |
| increment! | Bump specified component | version.increment!(:minor) |
| satisfy? | Check constraint satisfaction | version.satisfy?('~> 1.2') |
Common Version Constraints
| Constraint | Meaning | Allows | Example Match |
|---|---|---|---|
| ~> 2.1.0 | Approximately 2.1.0 | >= 2.1.0, < 2.2.0 | 2.1.5 matches |
| ~> 2.1 | Approximately 2.1 | >= 2.1.0, < 3.0.0 | 2.9.0 matches |
| >= 1.0, < 2.0 | Explicit range | Within specified bounds | 1.5.3 matches |
| = 1.2.3 | Exact version | Only 1.2.3 | 1.2.3 matches |
| != 1.2.3 | Not equal | Any except 1.2.3 | 1.2.4 matches |
Pre-release Progression Pattern
| Stage | Version | Purpose |
|---|---|---|
| Alpha | 1.0.0-alpha.1 | Early testing, incomplete features |
| Beta | 1.0.0-beta.1 | Feature complete, stability testing |
| Release Candidate | 1.0.0-rc.1 | Final testing before release |
| Release | 1.0.0 | Production release |
Version File Structure
# Standard Ruby gem version organization
lib/
my_gem/
version.rb # VERSION constant
my_gem.rb # Main library file
my_gem.gemspec # Gemspec references version.rb
# Version file content
module MyGem
VERSION = '2.3.1'
end
# Gemspec reference
require_relative 'lib/my_gem/version'
spec.version = MyGem::VERSION
Changelog Format
# Changelog format following Keep a Changelog conventions
## [Unreleased]
### Added
- New features in development
## [2.1.0] - 2024-01-15
### Added
- Feature additions
### Changed
- Modifications to existing features
### Deprecated
- Features marked for removal
### Removed
- Deleted features (MAJOR bump)
### Fixed
- Bug corrections
### Security
- Security patches