How to Implement and Manage Software Versioning
Purpose: A comprehensive guide for implementing semantic versioning in software projects
Audience: Developers, DevOps engineers, and project maintainers
Updated: 2025-08-12 UTC
Why Versioning Matters
Proper versioning is crucial for:
- Tracking Changes: Know exactly what's in each release
- Communication: Clear communication about breaking changes
- Rollback Capability: Quickly revert to previous versions
- Dependency Management: Other projects can depend on specific versions
- Professional Practice: Industry standard for software releases
Overview
This guide shows you how to implement a professional semantic versioning system for any software project, using Sasha Studio as a practical example. You'll learn how to:
- Set up semantic versioning
- Automate version management
- Integrate with Docker builds
- Create a CI/CD versioning pipeline
- Handle releases properly
Understanding Semantic Versioning
The Standard Format
Semantic Versioning (SemVer) follows Semantic Versioning 2.0.0 specification:
MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]
Version Components Explained
Core Version Numbers
MAJOR (X.0.0): Breaking changes that are incompatible with previous versions
- API changes that break existing integrations
- Removal of features
- Major architectural changes
MINOR (0.X.0): New features added in a backward-compatible manner
- New functionality
- New API endpoints
- Deprecation notices (but not removal)
PATCH (0.0.X): Backward-compatible bug fixes
- Security patches
- Performance improvements
- Documentation updates
Optional Components
PRERELEASE: Optional pre-release identifier (alpha, beta, rc)
- Used for testing versions before official release
- Example:
1.0.0-beta.1
BUILD: Optional build metadata (date, commit hash)
- For tracking specific builds
- Example:
1.0.0+20240111.abc123
Real-World Examples
1.0.0 # First stable release
1.1.0 # Added new feature (backward compatible)
1.0.1 # Bug fix to 1.0.0
2.0.0 # Breaking change (not backward compatible)
2.0.0-beta.1 # Beta version for testing
2.0.0-rc.1 # Release candidate
1.0.0+20240111 # Build with metadata
When to Increment Each Number
| Change Type | Version Change | Example |
|---|---|---|
| Fix typo in docs | 1.0.0 β 1.0.1 | PATCH |
| Add new API endpoint | 1.0.1 β 1.1.0 | MINOR |
| Remove deprecated API | 1.1.0 β 2.0.0 | MAJOR |
| Security fix | 1.1.0 β 1.1.1 | PATCH |
| Performance improvement | 1.1.1 β 1.1.2 | PATCH |
| Add optional parameter | 1.1.2 β 1.2.0 | MINOR |
| Change required parameter | 1.2.0 β 2.0.0 | MAJOR |
Setting Up Version Management
Step 1: Create a Central Version File
Create a single VERSION file at your project root:
# Create VERSION file
echo "1.0.0" > VERSION
# Verify it was created
cat VERSION
# Output: 1.0.0
Important: This file should contain ONLY the version number (no extra text or whitespace) and serves as the single source of truth for all versioning operations.
Step 2: Update package.json (for Node.js projects)
{
"name": "your-project",
"version": "1.0.0", // Keep in sync with VERSION file
"scripts": {
"version:sync": "node scripts/sync-version.js"
}
}
Step 3: Set Up Automatic Version Bumping
Option A: Auto-bump on Build
Create a build script that automatically increments the patch version:
#!/bin/bash
# build.sh - Auto-bumps patch version on each build
# Read current version
CURRENT_VERSION=$(cat VERSION)
# Bump patch version (unless --no-bump flag is used)
if [[ "$1" != "--no-bump" ]]; then
# Split version into components
IFS='.' read -r major minor patch <<< "$CURRENT_VERSION"
patch=$((patch + 1))
NEW_VERSION="${major}.${minor}.${patch}"
# Update VERSION file
echo "$NEW_VERSION" > VERSION
echo "Version bumped: $CURRENT_VERSION β $NEW_VERSION"
fi
# Continue with build...
Option B: Manual Version Control
For more control, use manual version bumping:
./scripts/version.sh patch # 1.0.0 β 1.0.1
./scripts/version.sh minor # 1.0.1 β 1.1.0
./scripts/version.sh major # 1.1.0 β 2.0.0
Creating a Version Management Script
Complete Version Script Example
Create scripts/version.sh to manage versions:
#!/bin/bash
# scripts/version.sh - Semantic version management
VERSION_FILE="VERSION"
# Read current version
CURRENT_VERSION=$(cat $VERSION_FILE 2>/dev/null || echo "0.0.0")
case "$1" in
current)
echo "Current version: $CURRENT_VERSION"
;;
major)
# Bump major version (X.0.0)
IFS='.' read -r major minor patch <<< "$CURRENT_VERSION"
NEW_VERSION="$((major + 1)).0.0"
echo "$NEW_VERSION" > $VERSION_FILE
echo "Version bumped: $CURRENT_VERSION β $NEW_VERSION (MAJOR)"
;;
minor)
# Bump minor version (0.X.0)
IFS='.' read -r major minor patch <<< "$CURRENT_VERSION"
NEW_VERSION="${major}.$((minor + 1)).0"
echo "$NEW_VERSION" > $VERSION_FILE
echo "Version bumped: $CURRENT_VERSION β $NEW_VERSION (MINOR)"
;;
patch)
# Bump patch version (0.0.X)
IFS='.' read -r major minor patch <<< "$CURRENT_VERSION"
NEW_VERSION="${major}.${minor}.$((patch + 1))"
echo "$NEW_VERSION" > $VERSION_FILE
echo "Version bumped: $CURRENT_VERSION β $NEW_VERSION (PATCH)"
;;
prerelease)
# Create pre-release version
TYPE=${2:-beta}
if [[ $CURRENT_VERSION == *"-${TYPE}."* ]]; then
# Increment existing pre-release
PRERELEASE_NUM=$(echo $CURRENT_VERSION | sed -n "s/.*-${TYPE}.\([0-9]*\).*/\1/p")
NEW_VERSION=$(echo $CURRENT_VERSION | sed "s/-${TYPE}.[0-9]*/-${TYPE}.$((PRERELEASE_NUM + 1))/")
else
# Create new pre-release
BASE_VERSION=$(echo $CURRENT_VERSION | cut -d'-' -f1)
NEW_VERSION="${BASE_VERSION}-${TYPE}.1"
fi
echo "$NEW_VERSION" > $VERSION_FILE
echo "Pre-release version: $CURRENT_VERSION β $NEW_VERSION"
;;
set)
# Set specific version
NEW_VERSION="$2"
echo "$NEW_VERSION" > $VERSION_FILE
echo "Version set to: $NEW_VERSION"
;;
*)
echo "Usage: $0 {current|major|minor|patch|prerelease [type]|set <version>}"
echo ""
echo "Examples:"
echo " $0 current # Show current version"
echo " $0 patch # Bump patch: 1.0.0 β 1.0.1"
echo " $0 minor # Bump minor: 1.0.1 β 1.1.0"
echo " $0 major # Bump major: 1.1.0 β 2.0.0"
echo " $0 prerelease beta # Create beta: 1.0.0 β 1.0.0-beta.1"
echo " $0 set 2.0.0 # Set specific version"
exit 1
;;
esac
Make the script executable:
chmod +x scripts/version.sh
Integrating Versioning with Docker
Docker Build Integration
The enhanced claudecodeui/scripts/docker-build.sh script provides comprehensive versioning support and now uses Dockerfile.sliplane by default (optimized for Sliplane deployment):
# Production build with automatic versioning
./claudecodeui/scripts/docker-build.sh production
# Development build
./claudecodeui/scripts/docker-build.sh development
# Custom tagged build
./claudecodeui/scripts/docker-build.sh production my-custom-tag
Docker Tags Created
For a production build of version 1.2.3, the following tags are created:
sasha-studio:1.2.3- Full version tagsasha-studio:1.2- Major.minor tagsasha-studio:1- Major version tagsasha-studio:latest- Latest stablesasha-studio:production- Production tag
Additional tags for development:
sasha-studio:dev- Development buildsasha-studio:dev-abc123- Dev with commit hashsasha-studio:feature-branch- Branch-based tagsasha-studio:1.2.3-dev.20240111.abc123- Dev with full metadata
Build Metadata
Each Docker image includes metadata labels:
org.opencontainers.image.version- Version numberorg.opencontainers.image.created- Build timestamporg.opencontainers.image.revision- Git commit hashorg.opencontainers.image.title- Application nameorg.opencontainers.image.description- Application description
Testing with Versions
The docker-test.sh script automatically uses the current version:
# Run test environment with current version
./docker-test.sh
# The test container will display:
# π Version: 1.0.0
CI/CD Integration
GitHub Actions
The repository includes GitHub Actions workflows for automated builds:
- On Push to Main: Builds and tags as
latest - On Git Tag: Builds with semantic version tags
- On Pull Request: Builds with PR number tag
Creating a Release
# 1. Update version
./scripts/version.sh minor
# 2. Commit changes
git add -A
git commit -m "chore: bump version to 1.1.0"
# 3. Create git tag
git tag v1.1.0
# 4. Push to GitHub
git push origin main
git push origin v1.1.0
# This triggers GitHub Actions to build and publish
Version Display
Health Endpoint
The version is exposed via the health endpoint:
curl http://localhost:3005/api/health
# Response includes:
{
"version": "1.0.0",
"gitCommit": "abc123",
...
}
Docker Image Inspection
# View image labels
docker inspect sasha-studio:latest | grep -A5 Labels
# View all tags
docker images sasha-studio
Build Information
After each build, a .last-build.json file is created with build details:
{
"version": "1.0.0",
"buildDate": "2024-01-11T10:00:00Z",
"gitCommit": "abc123",
"gitBranch": "main",
"gitStatus": "clean",
"tags": ["sasha-studio:1.0.0", "sasha-studio:latest"],
"target": "runner"
}
Best Practices and Guidelines
Version Bumping Decision Tree
Version Bumping Guidelines
PATCH - Bug fixes, security updates, dependency updates
./scripts/version.sh patchMINOR - New features, non-breaking changes
./scripts/version.sh minorMAJOR - Breaking changes, major refactors
./scripts/version.sh major
Pre-release Workflow
# Start beta phase
./scripts/version.sh prerelease beta
# Test and iterate
./scripts/version.sh prerelease beta # -> beta.2, beta.3, etc.
# Move to release candidate
./scripts/version.sh prerelease rc
# Final release
./scripts/version.sh set 1.1.0
Development Builds
For development builds with uncommitted changes:
- The build script automatically detects "dirty" state
- Creates a unique dev tag with timestamp and commit
- Example:
1.0.0-dev.20240111-143022.abc123
Registry Management
Local Registry
# Run local registry
docker run -d -p 5000:5000 --name registry registry:2
# Tag and push
docker tag sasha-studio:1.0.0 localhost:5000/sasha-studio:1.0.0
docker push localhost:5000/sasha-studio:1.0.0
GitHub Container Registry
# Set registry environment variable
export DOCKER_REGISTRY=ghcr.io/yourusername
# Build and push
./claudecodeui/scripts/docker-build.sh production
Rollback and Recovery
Quick Rollback Strategy
# List available versions
docker images sasha-studio --format "table {{.Tag}}\t{{.Created}}"
# Run specific version
docker run -p 3005:3005 sasha-studio:1.0.0
Version History
Keep track of deployed versions:
# Tag production deployments
docker tag sasha-studio:1.0.0 sasha-studio:prod-20240111
# View deployment history
docker images sasha-studio | grep prod-
Troubleshooting
Common Issues
Version Mismatch
# Sync package.json with VERSION file ./scripts/version.sh currentBuild Cache Issues
# Clear Docker build cache docker builder prune -aTag Conflicts
# Force retag docker tag -f sasha-studio:new sasha-studio:latest
Version Verification
# Check VERSION file
cat VERSION
# Check package.json
grep version claudecodeui/package.json
# Check running container
curl http://localhost:3005/api/health | jq .version
# Check Docker image
docker inspect sasha-studio:latest | grep -i version
Migration from Old System
If migrating from an unversioned system:
- Determine current version based on features
- Create VERSION file:
echo "1.0.0" > VERSION - Tag existing images:
docker tag sasha-studio:latest sasha-studio:1.0.0 - Update CI/CD pipelines to use new versioning
- Document version history in CHANGELOG.md
Changelog
Maintain a CHANGELOG.md file documenting version changes:
# Changelog
## [1.1.0] - 2024-01-11
### Added
- Semantic versioning system
- Automated Docker tagging
- Version display in health endpoint
### Changed
- Enhanced build scripts with version support
- Updated CI/CD workflows
### Fixed
- Build reproducibility issues
Summary and Benefits
What You've Learned
You now know how to:
- Implement semantic versioning in any project
- Create automated version management scripts
- Integrate versioning with Docker builds
- Set up CI/CD with proper version tagging
- Handle pre-releases and release candidates
- Maintain clear version history
- Implement rollback strategies
Key Benefits of Proper Versioning
- Professional Standards: Follow industry best practices
- Clear Communication: Everyone knows what changed
- Easy Rollback: Quick recovery from issues
- Dependency Management: Other projects can depend on stable versions
- Automation Ready: Integrates with CI/CD pipelines
- Audit Trail: Complete history of changes
Next Steps
- Implement VERSION file in your project
- Create version management scripts
- Update build processes to use versions
- Set up CI/CD with version tags
- Start following semantic versioning rules
- Document your versioning strategy
Additional Resources
- Semantic Versioning Specification
- Conventional Commits - Automated version bumping from commits
- Keep a Changelog - How to maintain a good changelog
- Git Tags Best Practices
This guide provides a complete framework for implementing professional versioning in your software projects. Apply these principles consistently for better software management.