Advanced Topics
Software Versioning
Semantic versioning, release management, and version control strategies
Software Versioning
Software versioning helps communicate changes, manage dependencies, and maintain compatibility.
Semantic Versioning (SemVer)
Format
MAJOR.MINOR.PATCH
Example: 2.4.1Version Components
MAJOR (2.x.x)
Breaking changes that require user action.
// v1.0.0
function getUser(id: number): User
// v2.0.0 - MAJOR: Changed parameter type
function getUser(id: string): UserMINOR (x.4.x)
New features, backward compatible.
// v2.4.0 - MINOR: Added new optional parameter
function getUser(id: string, options?: { includeDeleted: boolean }): UserPATCH (x.x.1)
Bug fixes, backward compatible.
// v2.4.1 - PATCH: Fixed email validation bug
function validateEmail(email: string): boolean {
// Fixed regex that was rejecting valid emails
}Version Ranges
npm/pnpm Package Versioning
{
"dependencies": {
"exact": "1.2.3", // Exactly 1.2.3
"patch": "~1.2.3", // >=1.2.3 <1.3.0
"minor": "^1.2.3", // >=1.2.3 <2.0.0
"major": "*", // Any version
"range": ">=1.2.3 <2.0.0", // Custom range
"latest": "latest" // Latest version
}
}Caret (^) - Minor Updates
^1.2.3 → >=1.2.3 <2.0.0
^0.2.3 → >=0.2.3 <0.3.0
^0.0.3 → >=0.0.3 <0.0.4Tilde (~) - Patch Updates
~1.2.3 → >=1.2.3 <1.3.0
~1.2 → >=1.2.0 <1.3.0
~1 → >=1.0.0 <2.0.0Pre-release Versions
Alpha
Early development, unstable.
1.0.0-alpha.1
1.0.0-alpha.2Beta
Feature complete, testing.
1.0.0-beta.1
1.0.0-beta.2Release Candidate
Almost ready for release.
1.0.0-rc.1
1.0.0-rc.2Example Progression
1.0.0-alpha.1 → Development starts
1.0.0-alpha.2 → More features
1.0.0-beta.1 → Feature complete
1.0.0-beta.2 → Bug fixes
1.0.0-rc.1 → Final testing
1.0.0-rc.2 → Last minute fixes
1.0.0 → Stable releaseVersion Management
package.json
{
"name": "my-package",
"version": "1.2.3",
"description": "My awesome package",
"main": "dist/index.js",
"types": "dist/index.d.ts"
}Updating Versions
Manual
// package.json
{
"version": "1.2.3" // → "1.2.4"
}npm version
# Patch update (1.2.3 → 1.2.4)
npm version patch
# Minor update (1.2.3 → 1.3.0)
npm version minor
# Major update (1.2.3 → 2.0.0)
npm version major
# Pre-release
npm version prerelease --preid=alphaAutomatic with Commitizen
npm install -g commitizen
# Interactive commit
git cz
# Automatically determines version bump
npm run releaseChangelog
Keep a Changelog Format
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/),
and this project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased]
### Added
- New feature X
### Changed
- Updated feature Y
### Deprecated
- Feature Z will be removed in v2.0.0
### Removed
- Removed deprecated feature A
### Fixed
- Fixed bug in feature B
### Security
- Fixed security vulnerability in auth
## [1.2.0] - 2024-01-15
### Added
- OAuth2 authentication support
- User profile customization
- Dark mode theme
### Changed
- Improved performance of dashboard loading
- Updated UI components for better accessibility
### Fixed
- Resolved memory leak in real-time updates
- Fixed calculation error in checkout
## [1.1.0] - 2024-01-01
### Added
- API rate limiting
- Request logging
### Changed
- Optimized database queries
## [1.0.0] - 2023-12-15
Initial release
### Added
- User authentication
- Core API endpoints
- Admin dashboardGenerate Changelog Automatically
# Using conventional-changelog
npm install -g conventional-changelog-cli
conventional-changelog -p angular -i CHANGELOG.md -s
# Or with package.json script
{
"scripts": {
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
}
}Git Tags
Creating Tags
# Lightweight tag
git tag v1.2.3
# Annotated tag (recommended)
git tag -a v1.2.3 -m "Release version 1.2.3"
# Tag specific commit
git tag -a v1.2.3 abc123 -m "Release version 1.2.3"
# Push tags
git push origin v1.2.3
# Push all tags
git push --tagsListing Tags
# List all tags
git tag
# List tags matching pattern
git tag -l "v1.2.*"
# Show tag details
git show v1.2.3Deleting Tags
# Delete local tag
git tag -d v1.2.3
# Delete remote tag
git push origin --delete v1.2.3Release Process
Manual Release
# 1. Update version
npm version minor
# 2. Update changelog
npm run changelog
# 3. Commit changes
git add .
git commit -m "chore: release v1.3.0"
# 4. Create tag
git tag -a v1.3.0 -m "Release v1.3.0"
# 5. Push changes
git push origin main --tags
# 6. Build and publish
npm run build
npm publishAutomated Release (GitHub Actions)
name: Release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm build
- name: Semantic Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-releasesemantic-release Configuration
// .releaserc.js
module.exports = {
branches: ['main'],
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
'@semantic-release/changelog',
'@semantic-release/npm',
'@semantic-release/github',
[
'@semantic-release/git',
{
assets: ['package.json', 'CHANGELOG.md'],
message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}'
}
]
]
}API Versioning
URL Versioning
// v1
app.get('/api/v1/users', getUsers)
// v2
app.get('/api/v2/users', getUsersV2)Header Versioning
app.get('/api/users', (req, res) => {
const version = req.headers['api-version']
if (version === '2') {
return getUsersV2(req, res)
}
return getUsers(req, res)
})Accept Header
app.get('/api/users', (req, res) => {
const accept = req.headers['accept']
if (accept.includes('application/vnd.api.v2+json')) {
return getUsersV2(req, res)
}
return getUsers(req, res)
})Breaking Changes
Documenting Breaking Changes
## [2.0.0] - 2024-02-01
### BREAKING CHANGES
#### Changed Function Signatures
- `getUser(id: number)` → `getUser(id: string)`
- Update all calls to use string IDs instead of numbers
#### Removed Deprecated APIs
- Removed `getUserProfile()` (use `getUser()` instead)
- Removed `updateUserData()` (use `updateUser()` instead)
#### Changed Default Behavior
- `createUser()` now throws on duplicate email (previously returned existing user)
- Add error handling for duplicate emails
### Migration Guide
#### Step 1: Update User ID Types
```typescript
// Before
const user = await getUser(123)
// After
const user = await getUser('123')Step 2: Replace Deprecated Functions
// Before
const profile = await getUserProfile(userId)
// After
const user = await getUser(userId)Step 3: Handle Duplicate Email Errors
// Before
const user = await createUser({ email, password })
// After
try {
const user = await createUser({ email, password })
} catch (error) {
if (error.code === 'DUPLICATE_EMAIL') {
// Handle duplicate
}
}
### Deprecation Process
```typescript
/**
* @deprecated Use getUser() instead. Will be removed in v2.0.0
*/
function getUserProfile(id: string): User {
console.warn('getUserProfile() is deprecated. Use getUser() instead.')
return getUser(id)
}Version in Code
Exposing Version
// package.json
{
"version": "1.2.3"
}
// version.ts
import packageJson from '../package.json'
export const VERSION = packageJson.version
// api.ts
app.get('/version', (req, res) => {
res.json({ version: VERSION })
})Version Check
function checkCompatibility(clientVersion: string): boolean {
const [clientMajor] = clientVersion.split('.')
const [serverMajor] = VERSION.split('.')
return clientMajor === serverMajor
}Monorepo Versioning
Independent Versioning
// packages/core/package.json
{
"name": "@myapp/core",
"version": "1.2.3"
}
// packages/ui/package.json
{
"name": "@myapp/ui",
"version": "2.0.1"
}Fixed Versioning
// All packages share same version
{
"name": "@myapp/core",
"version": "1.0.0"
}
{
"name": "@myapp/ui",
"version": "1.0.0"
}Using Changesets
# Install changesets
npm install -D @changesets/cli
# Initialize
npx changeset init
# Create changeset
npx changeset
# Version packages
npx changeset version
# Publish
npx changeset publishBest Practices
Do
✅ Follow semantic versioning ✅ Maintain detailed changelog ✅ Tag releases in git ✅ Document breaking changes ✅ Provide migration guides ✅ Deprecate before removing ✅ Test compatibility
Don't
❌ Change versions arbitrarily ❌ Skip versions ❌ Break compatibility in minor/patch ❌ Forget to update changelog ❌ Remove features without deprecation ❌ Use 0.x for production ❌ Publish without testing
Tools
- semantic-release: Automated versioning
- standard-version: Changelog generation
- Changesets: Monorepo versioning
- Release Please: Automated releases
- Conventional Commits: Commit conventions
Version Strategy
For Libraries
- Strict semantic versioning
- Deprecation warnings
- Migration guides
- Long support windows
For Applications
- Date-based versioning (2024.01.15)
- Calendar versioning (CalVer)
- Marketing versions (Windows 11)
- More flexible approach