소스 정보
- 저장소
- tools-only/X-Skills
- 최근 소스 활동
- 2026년 3월 1일 03:37
- 감지된 SKILL.md 언어
- 영어
- 스타
- 7
- 포크
- 1
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tools-only/X-Skills --skill api-api-versioning명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Index of Build Systems Skills
Coordination patterns for distributed dataflow systems including barriers, epochs, and distributed snapshots
Windowing, sessionization, time-series aggregation, and late data handling for streaming systems
SOC 직업 분류 기준
SKILL.md 표시 중
| name | api-api-versioning |
| description | Planning API changes and deciding on versioning approach |
Scope: API versioning strategies, breaking changes, deprecation workflow, migration patterns Lines: ~200 Last Updated: 2025-10-18
Activate this skill when:
Problem: APIs evolve over time. Changes can break existing clients.
Solution: Maintain multiple API versions simultaneously, allowing clients to migrate gradually.
Benefits:
Cost:
Format: /v1/users, /v2/users
Example:
GET /v1/users/123
GET /v2/users/123
Implementation (FastAPI):
from fastapi import FastAPI
app = FastAPI()
# Version 1
@app.get("/v1/users/{user_id}")
async def get_user_v1(user_id: int):
return {"id": user_id, "name": "Alice"}
# Version 2 (with email field)
@app.get("/v2/users/{user_id}")
async def get_user_v2(user_id: int):
return {
"id": user_id,
"name": "Alice",
"email": "alice@example.com"
}
Pros:
Cons:
Best for: Public APIs, REST APIs, simple versioning schemes
Format: Accept: application/vnd.api.v1+json
Example:
GET /users/123
Accept: application/vnd.api.v2+json
Implementation (FastAPI):
from fastapi import FastAPI, Header, HTTPException
app = FastAPI()
@app.get("/users/{user_id}")
async def get_user(
user_id: int,
accept: str = Header(default="application/vnd.api.v1+json")
):
if "v2" in accept:
return {
"id": user_id,
"name": "Alice",
"email": "alice@example.com"
}
elif "v1" in accept:
return {"id": user_id, "name": "Alice"}
else:
raise HTTPException(400, "Unsupported API version")
Pros:
Cons:
Best for: Internal APIs, strict REST adherence, content negotiation
Format: /users?api_version=2
Example:
GET /users/123?api_version=2
Implementation (FastAPI):
from fastapi import FastAPI, Query
app = FastAPI()
@app.get("/users/{user_id}")
async def get_user(
user_id: int,
api_version: int = Query(default=1)
):
if api_version == 2:
return {
"id": user_id,
"name": "Alice",
"email": "alice@example.com"
}
else:
return {"id": user_id, "name": "Alice"}
Pros:
Cons:
Best for: Simple internal APIs, quick prototypes
Format: X-API-Version: 2
Example:
GET /users/123
X-API-Version: 2
Implementation (FastAPI):
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/users/{user_id}")
async def get_user(
user_id: int,
x_api_version: int = Header(default=1)
):
if x_api_version == 2:
return {
"id": user_id,
"name": "Alice",
"email": "alice@example.com"
}
else:
return {"id": user_id, "name": "Alice"}
Pros:
Cons:
Best for: Internal APIs, microservices, controlled environments
Can deploy without versioning:
✅ Adding optional fields to request:
// Before
{"name": "Alice"}
// After (optional email)
{"name": "Alice", "email": "alice@example.com"}
✅ Adding new fields to response:
// Before
{"id": 1, "name": "Alice"}
// After (added email)
{"id": 1, "name": "Alice", "email": "alice@example.com"}
✅ Adding new endpoints:
POST /v1/users (existing)
POST /v1/users/bulk (new, safe)
✅ Adding new optional query parameters ✅ Expanding enum values (if clients ignore unknown) ✅ Relaxing validation (accepting more input)
Must introduce new version:
❌ Removing fields from response:
// Before
{"id": 1, "name": "Alice", "email": "alice@example.com"}
// After (removed email) - BREAKS CLIENTS
{"id": 1, "name": "Alice"}
❌ Renaming fields:
// Before
{"user_id": 1}
// After - BREAKS CLIENTS
{"id": 1}
❌ Changing field types:
// Before
{"created_at": "2025-01-15"}
// After - BREAKS CLIENTS
{"created_at": 1736899200}
❌ Making optional field required ❌ Removing endpoints ❌ Changing URL structure ❌ Stricter validation (rejecting previously valid input) ❌ Changing authentication scheme
Before introducing breaking change:
[ ] Identified all affected clients
[ ] Planned new version number/identifier
[ ] Implemented both old and new versions
[ ] Created migration guide for clients
[ ] Set deprecation timeline (e.g., 6 months)
[ ] Added deprecation warnings to old version
[ ] Updated documentation
[ ] Communicated timeline to stakeholders
[ ] Monitored usage of old version
[ ] Planned sunset date for old version
Actions:
Example (FastAPI):
from fastapi import Response
@app.get("/v1/users/{user_id}")
async def get_user_v1(user_id: int, response: Response):
# Add deprecation warning
response.headers["Deprecation"] = "true"
response.headers["Sunset"] = "2025-07-01"
response.headers["Link"] = '</v2/users>; rel="successor-version"'
return {"id": user_id, "name": "Alice"}
Communication:
Subject: API v1 Deprecation Notice
We're deprecating /v1/users in favor of /v2/users.
Timeline:
- Today: v2 available, v1 still supported
- Month 3: Deprecation warnings added to v1
- Month 6: v1 sunset (will return 410 Gone)
Migration guide: https://docs.api.com/v1-to-v2
Questions? Contact api-support@example.com
Actions:
Example:
@app.get("/v1/users/{user_id}")
async def get_user_v1(user_id: int, response: Response):
response.headers["X-Deprecation-Warning"] = "v1 will sunset on 2025-07-01"
return {
"id": user_id,
"name": "Alice",
"_deprecation": {
"message": "This endpoint will be removed on 2025-07-01",
"migration_guide": "https://docs.api.com/v1-to-v2"
}
}
Actions:
410 Gone for old versionExample:
@app.get("/v1/users/{user_id}")
async def get_user_v1(user_id: int):
raise HTTPException(
status_code=410,
detail={
"error": "This API version has been sunset",
"sunset_date": "2025-07-01",
"migration_guide": "https://docs.api.com/v1-to-v2",
"new_endpoint": "/v2/users/{user_id}"
}
)
Month 0: Announcement
- Release v2 alongside v1
- Announce deprecation timeline
- Update documentation
Month 1-2: Migration Period
- Monitor v1 usage
- Provide migration support
- Send reminder emails
Month 3: Warning Phase
- Add deprecation headers
- Log clients using v1
- Contact heavy users directly
Month 4-5: Final Warning
- Increase warning visibility
- Offer migration assistance
- Set hard sunset date
Month 6: Sunset
- Return 410 Gone for v1
- Redirect to migration guide
- Monitor for issues
Adjust timeline based on:
Recommended timelines:
Strategy: Run old and new versions side-by-side.
# v1/users.py
@router.get("/v1/users/{user_id}")
async def get_user_v1(user_id: int):
return {"id": user_id, "name": "Alice"}
# v2/users.py
@router.get("/v2/users/{user_id}")
async def get_user_v2(user_id: int):
return {
"id": user_id,
"name": "Alice",
"email": "alice@example.com"
}
Pros: Clean separation, easy rollback Cons: Code duplication, maintenance burden
Strategy: Single core implementation, adapters for each version.
# core/users.py
def get_user_data(user_id: int):
return {
"id": user_id,
"name": "Alice",
"email": "alice@example.com"
}
# api/v1.py
@app.get("/v1/users/{user_id}")
async def get_user_v1(user_id: int):
data = get_user_data(user_id)
# Adapter: remove email for v1
return {"id": data["id"], "name": data["name"]}
# api/v2.py
@app.get("/v2/users/{user_id}")
async def get_user_v2(user_id: int):
return get_user_data(user_id) # Full data
Pros: Single source of truth, less duplication Cons: Adapter complexity increases over time
Strategy: Use flags to toggle new behavior.
from functools import wraps
def version_aware(func):
@wraps(func)
async def wrapper(user_id: int, version: int = 1):
data = await func(user_id)
if version == 1:
# Remove new fields for v1
return {k: v for k, v in data.items() if k in ["id", "name"]}
else:
return data
return wrapper
@version_aware
async def get_user(user_id: int):
return {
"id": user_id,
"name": "Alice",
"email": "alice@example.com"
}
Pros: Single endpoint, flexible Cons: Complexity in logic, harder to test
Rule: Only add, never remove or change.
// Version 1
{"id": 1, "name": "Alice"}
// Version 1.1 (backward compatible)
{"id": 1, "name": "Alice", "email": "alice@example.com"}
// Version 1.2 (still backward compatible)
{"id": 1, "name": "Alice", "email": "alice@example.com", "phone": "555-1234"}
Clients ignore unknown fields → No breaking changes.
Strategy: Provide defaults for new required fields.
from pydantic import BaseModel, Field
class User(BaseModel):
id: int
name: str
email: str = Field(default="noreply@example.com") # Default for old clients
Old requests (no email) → Use default New requests (with email) → Use provided value
Strategy: Support both old and new field names.
from pydantic import BaseModel, Field
class User(BaseModel):
user_id: int = Field(alias="id") # Accept both "id" and "user_id"
name: str
Accepts:
{"id": 1, "name": "Alice"} // Old clients
{"user_id": 1, "name": "Alice"} // New clients
| Strategy | Discoverability | Caching | Simplicity | Best For |
|---|---|---|---|---|
| URL Path | ⭐⭐⭐ High | ⭐⭐⭐ Easy | ⭐⭐⭐ Simple | Public APIs, REST |
| Header (Accept) | ⭐ Low | ⭐ Complex | ⭐⭐ Moderate | Strict REST, Internal |
| Query Param | ⭐⭐ Medium | ⭐⭐ Moderate | ⭐⭐⭐ Simple | Prototypes, Internal |
| Custom Header | ⭐ Low | ⭐⭐ Moderate | ⭐⭐ Moderate | Microservices, Internal |
Recommendation: Use URL Path Versioning for simplicity and discoverability unless strict REST compliance required.
fastapi-routing.md - Organizing API routes and versioningapi-design-patterns.md - RESTful API design principlesapi-documentation.md - Documenting versioned APIs (OpenAPI/Swagger)database-migrations.md - Versioning database schemas alongside APIsfeature-flags.md - Using feature flags for gradual rolloutsLast Updated: 2025-10-18 Format Version: 1.0 (Atomic)