Detects backward-incompatible changes to public APIs, function signatures, endpoints, and data schemas before they break production. Suggests migration paths.
Detects backward-incompatible changes to public APIs, function signatures, and data schemas before they reach production. Triggers automatically when you modify API endpoints, rename functions, or change data models to suggest migration paths and prevent breaking existing clients.
/plugin marketplace add MacroMan5/claude-code-workflow-plugins/plugin install lazy@lazy-dev-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Purpose: Catch breaking changes early, not after customers complain.
Trigger Words: API, endpoint, route, public, schema, model, interface, contract, signature, rename, remove, delete
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
# 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.
# 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.
# 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.
# 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.
# 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.
# 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.
# 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
# BEFORE
{"id": 123, "name": "Alice"}
# AFTER - Safe! Added field
{"id": 123, "name": "Alice", "created_at": "2025-10-30"}
# Old clients ignore new field: ✅ Works
# New endpoint added
@app.route("/api/v2/users")
def get_users_v2():
pass
# Old endpoint unchanged: ✅ Safe
## 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
# 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
# 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
❌ 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.
# 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