versioning-strategy
API and system versioning strategies to enable evolution while maintaining backward compatibility.
安装与使用
请根据来源仓库和你的 AI 工具说明安装此技能。先阅读技能文件和依赖要求,再添加到工作流。
查看来源 ↗使用指南技能说明
Versioning Strategy
Overview
Versioning Strategy defines how systems evolve over time while maintaining compatibility with existing clients. Good versioning enables innovation without breaking existing integrations.
Core Principle: "Never break existing clients. Version explicitly, deprecate gracefully."
1. API Versioning Strategies
URL Path Versioning
GET /api/v1/users
GET /api/v2/users
GET /api/v3/users
Pros: Clear, easy to route
Cons: URL pollution, harder to deprecate
Header Versioning
GET /api/users
Header: API-Version: 2024-01-15
Pros: Clean URLs
Cons: Less visible, harder to test in browser
Query Parameter Versioning
GET /api/users?version=2
Pros: Easy to test
Cons: Can be forgotten, mixes with other params
Content Negotiation
GET /api/users
Accept: application/vnd.myapi.v2+json
Pros: RESTful, flexible
Cons: Complex, less discoverable
2. Semantic Versioning (SemVer)
MAJOR.MINOR.PATCH
1.0.0 → 1.0.1 (Patch: Bug fix, backward compatible)
1.0.1 → 1.1.0 (Minor: New feature, backward compatible)
1.1.0 → 2.0.0 (Major: Breaking change)
Examples
1.0.0: Initial release
1.0.1: Fix authentication bug
1.1.0: Add pagination to /users endpoint
1.2.0: Add /products endpoint
2.0.0: Remove deprecated /legacy endpoint
3. Breaking vs Non-Breaking Changes
Non-Breaking (Safe)
// ✅ Adding optional field
interface User {
id: string;
name: string;
email?: string; // New optional field
}
// ✅ Adding new endpoint
GET /api/v1/users/:id/preferences // New endpoint
// ✅ Adding new query parameter (optional)
GET /api/v1/users?sort=name // New optional param
Breaking (Dangerous)
// ❌ Removing field
interface User {
id: string;
// name: string; // REMOVED - breaks clients expecting this
}
// ❌ Changing field type
interface User {
id: number; // Was string, now number - BREAKING
}
// ❌ Renaming endpoint
GET /api/v1/accounts // Was /users - BREAKING
// ❌ Making optional field required
interface User {
email: string; // Was optional, now required - BREAKING
}
4. Versioning in Code
Version Routing (Express)
import express from 'express';
const app = express();
// V1 routes
app.get('/api/v1/users', v1.getUsers);
app.get('/api/v1/users/:id', v1.getUser);
// V2 routes (with new features)
app.get('/api/v2/users', v2.getUsers); // Includes pagination
app.get('/api/v2/users/:id', v2.getUser); // Includes preferences
Shared Logic
// Shared business logic
class UserService {
async getUser(id: string): Promise<User> {
return await db.users.findById(id);
}
}
// V1 controller
class V1UserController {
async getUser(req, res) {
const user = await userService.getUser(req.params.id);
res.json({ id: user.id, name: user.name }); // V1 response format
}
}
// V2 controller
class V2UserController {
async getUser(req, res) {
const user = await userService.getUser(req.params.id);
res.json({
id: user.id,
name: user.name,
email: user.email, // V2 includes email
preferences: user.preferences // V2 includes preferences
});
}
}
5. Database Schema Versioning
Expand-Contract Pattern
-- Phase 1: Expand (add new column)
ALTER TABLE users ADD COLUMN email_address VARCHAR(255);
-- Dual writes (write to both old and new)
UPDATE users SET email = ?, email_address = ? WHERE id = ?;
-- Phase 2: Migrate data
UPDATE users SET email_address = email WHERE email_address IS NULL;
-- Phase 3: Contract (remove old column)
ALTER TABLE users DROP COLUMN email;
Migration Scripts
// migrations/001_add_email.ts
export async function up(db: Database) {
await db.schema.alterTable('users', (table) => {
table.string('email_address');
});
}
export async function down(db: Database) {
await db.schema.alterTable('users', (table) => {
table.dropColumn('email_address');
});
}
6. Deprecation Strategy
Deprecation Timeline
## Deprecation Timeline for /api/v1/users
- **2024-01-15**: Announce deprecation (6 months notice)
- **2024-04-15**: Add deprecation headers
- **2024-07-15**: Remove from documentation
- **2024-07-15**: Sunset endpoint (stop accepting new clients)
Deprecation Headers
app.get('/api/v1/users', (req, res) => {
res.set('Deprecation', 'true');
res.set('Sunset', 'Sat, 15 Jul 2024 00:00:00 GMT');
res.set('Link', '</api/v2/users>; rel="successor-version"');
// Return data
res.json(users);
});
7. Client SDK Versioning
// SDK v1
import { UserClient } from '@myapi/sdk';
const client = new UserClient({ version: 'v1' });
const user = await client.getUser('123');
// SDK v2
import { UserClient } from '@myapi/sdk';
const client = new UserClient({ version: 'v2' });
const user = await client.getUser('123'); // Returns more fields
8. Versioning Checklist
- Strategy Chosen: URL path, header, or content negotiation?
- SemVer Adopted: Using semantic versioning?
- Breaking Changes Identified: Clear what breaks compatibility?
- Deprecation Policy: Timeline for sunsetting old versions?
- Documentation: All versions documented?
- Monitoring: Track usage of each version?
- Migration Guide: Help clients upgrade?
- Backward Compatibility: Maintain for at least 6 months?
Related Skills
-
59-architecture-decision/deprecation-policy -
51-contracts-governance/api-contracts -
45-developer-experience/release-workflow
使用示例
查看来源仓库中的使用示例与配置说明。
查看来源 ↗来源历史
- 收录时间
- 2026-01-20
- 索引更新时间
- 2026-01-26
- Branch
- main
收录与同步日期属于本站记录。源码修改历史请查看来源仓库。
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!