Understand versioning of artifacts
Proper software development practice requires indicating changes to code with the introduction of an increased version number. Understanding how to version artifacts correctly is essential for maintaining stable and predictable software systems.
Version increment principles
However small or large a change, it requires a new version. A component and its package can have independent versions and versioning schemes.
Key versioning concepts:
- Always increment: Every change requires a new version number.
- Independent versioning: Components can version separately from their packages.
- Immutability: Published versions cannot be changed.
- Communication: Version numbers communicate the nature of changes.
Version numbering schemes
The versioning scheme can differ per package type. Typically, it uses a scheme that can indicate the kind of change that is made.
Three types of changes
Most commonly, it involves three types of changes:
1. Major change
Major indicates that the package and its contents have changed significantly.
Characteristics:
- Significant changes: It often occurs at the introduction of a new version of the package.
- Redesign: It can be a redesign of the component.
- Breaking changes: Major changes aren't guaranteed to be compatible and usually have breaking changes from older versions.
- High effort: Major changes might require a large amount of work to adopt the consuming codebase to the new version.
When to use major versions:
- API changes: Breaking changes to public APIs.
- Removed features: Functionality that was deprecated and now removed.
- Architectural changes: Fundamental redesign of the component.
- Dependency updates: Major version updates to critical dependencies.
Example:
1.5.2→2.0.0indicates breaking changes
2. Minor change
Minor indicates that the package and its contents have extensive modifications but are smaller than a major change.
Characteristics:
- New features: Addition of functionality without breaking existing code.
- Backward compatibility: These changes can be backward compatible with the previous version, although they aren't guaranteed to be.
- Moderate effort: Usually requires minimal changes to consuming code.
- Additive changes: New methods, classes, or features added.
When to use minor versions:
- New functionality: Adding new features or capabilities.
- Enhancements: Improving existing features without breaking them.
- Deprecations: Marking features as deprecated (but still functional).
- Performance improvements: Non-breaking performance optimizations.
Example:
1.5.2→1.6.0indicates new features added
3. Patch
A patch or revision is used to indicate that a flaw, bug, or malfunctioning part of the component has been fixed.
Characteristics:
- Bug fixes: Usually, it's a backward-compatible version compared to the previous version.
- No new features: Only fixes to existing functionality.
- Low risk: Minimal risk to consuming applications.
- Quick adoption: Consumers can update with confidence.
When to use patch versions:
- Bug fixes: Correcting defects in existing functionality.
- Security patches: Addressing security vulnerabilities.
- Documentation fixes: Correcting documentation errors.
- Minor improvements: Small improvements that don't add features.
Example:
1.5.2→1.5.3indicates bug fixes
Technical implementation of versioning
How artifacts are versioned technically varies per package type. Each type has its way of indicating the version in the metadata.
Package-specific versioning
Different package types store version information differently:
NuGet packages
- Metadata location: Version stored in
.nuspecfile or project file. - Format: Supports Semantic Versioning 2.0.
- Example:
<version>1.2.3-beta1</version>
NuGet version format:
<package>
<metadata>
<id>MyPackage</id>
<version>1.2.3</version>
<title>My Package Title</title>
</metadata>
</package>
npm packages
- Metadata location: Version in
package.json. - Format: SemVer 2.0 compliant.
- Example:
"version": "1.2.3"
npm version format:
{
"name": "my-package",
"version": "1.2.3",
"description": "My package description"
}
Maven packages
- Metadata location: Version in
pom.xml. - Format:
major.minor.patchormajor.minor.patch-SNAPSHOT. - Example:
<version>1.2.3</version>
Maven version format:
<project>
<groupId>com.example</groupId>
<artifactId>my-artifact</artifactId>
<version>1.2.3</version>
</project>
Python packages
- Metadata location: Version in
setup.pyorpyproject.toml. - Format: PEP 440 compliant.
- Example:
version="1.2.3"
Python version format:
setup(
name="my-package",
version="1.2.3",
description="My package description"
)
Universal Packages
- Metadata location: Azure Artifacts Universal Package format.
- Format: SemVer 2.0.
- Example: Specified during publish with Azure CLI.
Package manager interaction
The corresponding package manager can inspect the version information. The tooling can query the package feed for packages and the available versions.
Package manager capabilities:
- Version discovery: List all available versions of a package.
- Version comparison: Determine which versions are newer.
- Dependency resolution: Select appropriate versions based on constraints.
- Range queries: Find versions matching version ranges (e.g.,
>=1.0.0 <2.0.0).
Query examples:
# NuGet - list versions
nuget list MyPackage -AllVersions
# npm - list versions
npm view my-package versions
# Maven - list versions (in repository)
mvn versions:display-dependency-updates
# Python - list versions
pip index versions my-package
Versioning conventions
Additionally, a package type might have its conventions for versioning and a particular versioning scheme.
Common conventions by ecosystem:
.NET (NuGet)
- Semantic Versioning 2.0: Primary standard.
- Assembly versioning: Separate assembly version and file version.
- Prerelease labels: Common labels include
alpha,beta,rc,preview.
JavaScript (npm)
- Strict SemVer: Enforces Semantic Versioning 2.0.
- Caret ranges:
^1.2.3(compatible with 1.x.x). - Tilde ranges:
~1.2.3(compatible with 1.2.x).
Java (Maven)
- SNAPSHOT versions: Development versions with
-SNAPSHOTsuffix. - Release versions: Stable versions without suffix.
- Qualifier support: Additional qualifiers like
-GA,-RELEASE.
Python (pip)
- PEP 440: Python Enhancement Proposal for versioning.
- Development releases:
.dev0suffix for development versions. - Pre-releases:
.a0(alpha),.b0(beta),.rc0(release candidate).
Best practices for artifact versioning
Consistent numbering:
- Follow conventions: Use the versioning convention for your package ecosystem.
- Be predictable: Increment versions consistently and predictably.
- Communicate clearly: Version numbers should clearly indicate change scope.
Automation:
- Automated versioning: Use build tools to generate version numbers automatically.
- CI/CD integration: Integrate versioning into your CI/CD pipelines.
- Avoid manual versioning: Reduce human error with automated processes.
Documentation:
- Changelog: Maintain a changelog documenting changes in each version.
- Release notes: Provide detailed release notes for each version.
- Migration guides: For major versions, provide migration guides.
See also Publish to NuGet feeds.