breaking-change-detector
Detects backward-incompatible changes to public APIs, function signatures, endpoints, and data schemas before they break production. Suggests migration paths.
When & Why to Use This Skill
The Breaking Change Detector is a specialized Claude skill designed to safeguard software stability by identifying backward-incompatible modifications in public APIs, function signatures, and data schemas. It proactively flags risks such as removed parameters or renamed endpoints before they reach production, offering clear migration paths and automated fix suggestions to ensure seamless version transitions and maintain API contract integrity.
Use Cases
- Automated Pull Request Audits: Automatically scan code changes during the review process to flag critical breaking changes like renamed methods or removed response fields before merging.
- API Versioning Strategy: Receive expert guidance on transitioning from v1 to v2, including suggestions for permanent redirects (301) and maintaining backward compatibility through aliasing.
- Schema Evolution Safety: Monitor changes in data models and database schemas to prevent breaking downstream microservices or external client integrations.
- Semantic Versioning (SemVer) Enforcement: Use the tool to determine whether a code update necessitates a Major, Minor, or Patch version bump based on the detected impact on public interfaces.
- Deprecation Workflow Management: Generate standardized deprecation warnings and documentation for legacy code to guide users toward new implementation paths without immediate service disruption.
| name | breaking-change-detector |
|---|---|
| description | Detects backward-incompatible changes to public APIs, function signatures, endpoints, and data schemas before they break production. Suggests migration paths. |
Breaking Change Detector Skill
Purpose: Catch breaking changes early, not after customers complain.
Trigger Words: API, endpoint, route, public, schema, model, interface, contract, signature, rename, remove, delete
Quick Decision: Is This Breaking?
def is_breaking_change(change: dict) -> tuple[bool, str]:
"""Fast breaking change evaluation."""
breaking_patterns = {
# Method signatures
"removed_parameter": True,
"renamed_parameter": True,
"changed_parameter_type": True,
"removed_method": True,
"renamed_method": True,
# API endpoints
"removed_endpoint": True,
"renamed_endpoint": True,
"changed_response_format": True,
"removed_response_field": True,
# Data models
"removed_field": True,
"renamed_field": True,
"changed_field_type": True,
"made_required": True,
# Return types
"changed_return_type": True,
}
# Safe changes (backward compatible)
safe_patterns = {
"added_parameter_with_default": False,
"added_optional_field": False,
"added_endpoint": False,
"added_response_field": False,
"deprecated_but_kept": False,
}
change_type = change.get("type")
return breaking_patterns.get(change_type, False), change_type
Common Breaking Changes (With Fixes)
1. Removed Function Parameter ❌ BREAKING
# BEFORE (v1.0)
def process_payment(amount, currency, user_id):
pass
# AFTER (v2.0) - BREAKS EXISTING CODE
def process_payment(amount, user_id): # Removed currency!
pass
# ✅ FIX: Keep parameter with default
def process_payment(amount, user_id, currency="USD"):
"""
Args:
currency: Deprecated in v2.0, always uses USD
"""
pass
Migration Path: Add default value, deprecate, document.
2. Renamed Function/Method ❌ BREAKING
# BEFORE
def getUserProfile(user_id):
pass
# AFTER - BREAKS CALLS
def get_user_profile(user_id): # Renamed!
pass
# ✅ FIX: Keep both, deprecate old
def get_user_profile(user_id):
"""Get user profile (v2.0+ naming)."""
pass
def getUserProfile(user_id):
"""Deprecated: Use get_user_profile() instead."""
warnings.warn("getUserProfile is deprecated, use get_user_profile", DeprecationWarning)
return get_user_profile(user_id)
Migration Path: Alias old name → new name, add deprecation warning.
3. Changed Response Format ❌ BREAKING
# BEFORE - Returns dict
@app.route("/api/user/<id>")
def get_user(id):
return {"id": id, "name": "Alice", "email": "alice@example.com"}
# AFTER - Returns list - BREAKS CLIENTS!
@app.route("/api/user/<id>")
def get_user(id):
return [{"id": id, "name": "Alice", "email": "alice@example.com"}]
# ✅ FIX: Keep format, add new endpoint
@app.route("/api/v2/user/<id>") # New version
def get_user_v2(id):
return [{"id": id, "name": "Alice"}]
@app.route("/api/user/<id>") # Keep v1
def get_user(id):
return {"id": id, "name": "Alice", "email": "alice@example.com"}
Migration Path: Version the API (v1, v2), keep old version alive.
4. Removed Endpoint ❌ BREAKING
# BEFORE
@app.route("/users")
def get_users():
pass
# AFTER - REMOVED! Breaks clients.
# (endpoint deleted)
# ✅ FIX: Redirect to new endpoint
@app.route("/users")
def get_users():
"""Deprecated: Use /api/v2/accounts instead."""
return redirect("/api/v2/accounts", code=301) # Permanent redirect
Migration Path: Keep endpoint, redirect with 301, document deprecation.
5. Changed Required Fields ❌ BREAKING
# BEFORE - email optional
class User:
def __init__(self, name, email=None):
self.name = name
self.email = email
# AFTER - email required! Breaks existing code.
class User:
def __init__(self, name, email): # No default!
self.name = name
self.email = email
# ✅ FIX: Keep optional, validate separately
class User:
def __init__(self, name, email=None):
self.name = name
self.email = email
def validate(self):
"""Validate required fields."""
if not self.email:
raise ValueError("Email is required (new in v2.0)")
Migration Path: Keep optional in constructor, add validation method.
6. Removed Response Field ❌ BREAKING
# BEFORE
{
"id": 123,
"name": "Alice",
"age": 30,
"email": "alice@example.com"
}
# AFTER - Removed age! Breaks clients expecting it.
{
"id": 123,
"name": "Alice",
"email": "alice@example.com"
}
# ✅ FIX: Keep field with null/default
{
"id": 123,
"name": "Alice",
"age": null, # Deprecated, always null in v2.0
"email": "alice@example.com"
}
Migration Path: Keep field with null, document deprecation.
Non-Breaking Changes ✅ (Safe)
1. Added Optional Parameter
# BEFORE
def process_payment(amount):
pass
# AFTER - Safe! Has default
def process_payment(amount, currency="USD"):
pass
# Old calls still work:
process_payment(100) # ✅ Works
2. Added Response Field
# BEFORE
{"id": 123, "name": "Alice"}
# AFTER - Safe! Added field
{"id": 123, "name": "Alice", "created_at": "2025-10-30"}
# Old clients ignore new field: ✅ Works
3. Added New Endpoint
# New endpoint added
@app.route("/api/v2/users")
def get_users_v2():
pass
# Old endpoint unchanged: ✅ Safe
Detection Strategy
Automatic Checks
- Function signatures: Compare old vs new parameters, types, names
- API routes: Check for removed/renamed endpoints
- Data schemas: Validate field additions/removals/renames
- Return types: Detect type changes
When to Run
- ✅ Before committing changes to public APIs
- ✅ During code review
- ✅ Before releasing new version
Output Format
## Breaking Change Report
**Status**: [✅ NO BREAKING CHANGES | ⚠️ BREAKING CHANGES DETECTED]
---
### Breaking Changes: 2
1. **[CRITICAL] Removed endpoint: GET /users**
- **Impact**: External API clients will get 404
- **File**: api/routes.py:45
- **Fix**:
```python
# Keep endpoint, redirect to new one
@app.route("/users")
def get_users():
return redirect("/api/v2/accounts", code=301)
```
- **Migration**: Add to CHANGELOG.md, notify users
2. **[HIGH] Renamed parameter: currency → currency_code**
- **Impact**: Existing function calls will fail
- **File**: payments.py:23
- **Fix**:
```python
# Accept both, deprecate old name
def process_payment(amount, currency_code=None, currency=None):
# Support old name temporarily
if currency is not None:
warnings.warn("currency is deprecated, use currency_code")
currency_code = currency
```
---
### Safe Changes: 1
1. **[SAFE] Added optional parameter: timeout (default=30)**
- **File**: api_client.py:12
- **Impact**: None, backward compatible
---
**Recommendation**:
1. Fix 2 breaking changes before merge
2. Document breaking changes in CHANGELOG.md
3. Bump major version (v1.x → v2.0) per semver
4. Notify API consumers 2 weeks before release
Integration with Workflow
# Automatic trigger when modifying APIs
/lazy code "rename /users endpoint to /accounts"
→ breaking-change-detector triggers
→ Detects: Endpoint rename is breaking
→ Suggests: Keep /users, redirect to /accounts
→ Developer applies fix
→ Re-check: ✅ Backward compatible
# Before PR
/lazy review US-3.4
→ breaking-change-detector runs
→ Checks all API changes in PR
→ Reports breaking changes
→ PR blocked if breaking without migration plan
Version Bumping Guide
# Semantic versioning
Given version: MAJOR.MINOR.PATCH
# Breaking change detected → Bump MAJOR
1.2.3 → 2.0.0
# New feature (backward compatible) → Bump MINOR
1.2.3 → 1.3.0
# Bug fix (backward compatible) → Bump PATCH
1.2.3 → 1.2.4
What This Skill Does NOT Do
❌ Catch internal/private API changes (only public APIs) ❌ Test runtime compatibility (use integration tests) ❌ Manage database migrations (separate tool) ❌ Generate full migration scripts
✅ DOES: Detect public API breaking changes, suggest fixes, enforce versioning.
Configuration
# Strict mode: flag all changes (even safe ones)
export LAZYDEV_BREAKING_STRICT=1
# Disable breaking change detection
export LAZYDEV_DISABLE_BREAKING_DETECTOR=1
# Check only specific types
export LAZYDEV_BREAKING_CHECK="endpoints,schemas"
Version: 1.0.0 Follows: Semantic Versioning 2.0.0 Speed: <3 seconds for typical PR