AI Tools Compared

API versioning is one of those problems that looks simple until you have 50 consumers and a breaking change. AI tools can help in three areas: detecting breaking changes before they ship, generating consumer migration guides automatically, and designing versioning strategies for new APIs.

Table of Contents

Why API Versioning is Hard

Every API change falls somewhere on a spectrum from “obviously safe” to “definitely breaks consumers.” The problem is the middle: changes that seem safe but break specific client patterns.

Adding a new optional field is non-breaking in theory. In practice, a consumer using a schema validator with additionalProperties: false will start rejecting responses. Changing a numeric field from int to float breaks clients that parse responses as integers and then format them with %d. AI tools that understand these failure modes catch problems before they ship.

The other hard part is documentation lag. Engineering moves faster than docs. Migration guides get written days after the breaking change ships, by which point some consumers have already broken. Automating guide generation at the point of the spec change closes that gap entirely.

Breaking Change Detection

The hardest part of API versioning is knowing what actually breaks consumers. Claude can analyze an OpenAPI spec diff and flag breaking vs non-breaking changes with justifications.

# breaking_change_detector.py
import json
import yaml
from pathlib import Path
from anthropic import Anthropic
from deepdiff import DeepDiff  # pip install deepdiff

client = Anthropic()

def load_spec(path: str) -> dict:
    text = Path(path).read_text()
    if path.endswith(".yaml") or path.endswith(".yml"):
        return yaml.safe_load(text)
    return json.loads(text)

def extract_diff_summary(old_spec: dict, new_spec: dict) -> dict:
    """Extract meaningful diff between two OpenAPI specs."""
    diff = DeepDiff(old_spec, new_spec, ignore_order=True)

    summary = {
        "added_paths": [],
        "removed_paths": [],
        "modified_paths": [],
        "schema_changes": []
    }

    # Find path-level changes
    old_paths = set(old_spec.get("paths", {}).keys())
    new_paths = set(new_spec.get("paths", {}).keys())

    summary["added_paths"] = list(new_paths - old_paths)
    summary["removed_paths"] = list(old_paths - new_paths)
    summary["modified_paths"] = [
        p for p in old_paths & new_paths
        if old_spec["paths"][p] != new_spec["paths"].get(p)
    ]

    return summary

def analyze_breaking_changes(old_spec_path: str, new_spec_path: str) -> dict:
    """Detect and explain breaking API changes."""
    old_spec = load_spec(old_spec_path)
    new_spec = load_spec(new_spec_path)
    diff_summary = extract_diff_summary(old_spec, new_spec)

    # Focus Claude on changed endpoints
    changed_old = {p: old_spec["paths"][p] for p in diff_summary["modified_paths"]}
    changed_new = {p: new_spec["paths"].get(p) for p in diff_summary["modified_paths"]}

    response = client.messages.create(
        model="claude-opus-4-6",
        max_tokens=2000,
        messages=[{
            "role": "user",
            "content": f"""Analyze these API changes and classify each as breaking or non-breaking.

Removed paths (always breaking): {diff_summary['removed_paths']}
Added paths (never breaking): {diff_summary['added_paths']}

Modified endpoints - before:
{json.dumps(changed_old, indent=2)[:3000]}

Modified endpoints - after:
{json.dumps(changed_new, indent=2)[:3000]}

For each change, output:
ENDPOINT: [method] [path]
CHANGE: [description of what changed]
BREAKING: [YES/NO]
REASON: [Why this breaks or doesn't break consumers]
MIGRATION: [What consumers need to do, if breaking]

Breaking changes include: removed fields, changed field types, renamed required fields,
changed response codes, removed enum values, stricter validation.

Non-breaking: added optional fields, new enum values, new optional parameters."""
        }]
    )

    return {
        "summary": diff_summary,
        "analysis": response.content[0].text
    }

Migration Guide Generator

When you have breaking changes, generate consumer documentation automatically:

def generate_migration_guide(
    old_spec: dict,
    new_spec: dict,
    version_from: str,
    version_to: str,
    breaking_changes: str
) -> str:
    """Generate a migration guide for API consumers."""
    response = client.messages.create(
        model="claude-opus-4-6",
        max_tokens=3000,
        messages=[{
            "role": "user",
            "content": f"""Write a migration guide for API consumers upgrading from v{version_from} to v{version_to}.

Breaking changes identified:
{breaking_changes}

Format as developer-facing documentation with:
1. Overview: What changed and why (2-3 sentences)
2. Breaking changes: Each change with before/after code examples
3. Step-by-step migration: Ordered list of changes to make
4. Deadline: Template for sunset date of old version

Use real code examples in the language-agnostic HTTP format (curl or similar).
Keep it practical — assume the reader is an engineer with 15 minutes."""
        }]
    )
    return response.content[0].text

# Example output (for a users endpoint field rename):
EXAMPLE_MIGRATION_GUIDE = """
## Migration Guide: API v2 to v3

### Overview
The User object has been updated to use ISO 8601 timestamps and
consolidate address fields. These changes improve consistency with
industry standards.

### Breaking Changes

#### 1. Timestamp format changed from Unix epoch to ISO 8601

**Before (v2):**
```json
{
 "id": "usr_123",
 "created_at": 1704067200,
 "updated_at": 1704153600
}

After (v3):

{
 "id": "usr_123",
 "created_at": "2024-01-01T00:00:00Z",
 "updated_at": "2024-01-02T00:00:00Z"
}

Migration: Replace new Date(user.created_at * 1000) with new Date(user.created_at).

2. address_line1 and address_line2 merged into address.street

Before (v2):

curl /v2/users/usr_123
# Returns: { "address_line1": "123 Main St", "address_line2": "Apt 4" }

After (v3):

curl /v3/users/usr_123
# Returns: { "address": { "street": "123 Main St, Apt 4", "city": "...", "country": "..." } }

Migration Steps

  1. Update timestamp parsing: change epoch handling to ISO 8601 parsing
  2. Update address field access: user.address_line1user.address.street
  3. Update any address write operations to use nested object format
  4. Update integration tests to assert new response structure

Deprecation Schedule

Versioning Strategy Advisor

Before building an API, use Claude to recommend the right versioning approach:

def recommend_versioning_strategy(api_description: dict) -> str:
    """Recommend an API versioning strategy based on API characteristics."""
    response = client.messages.create(
        model="claude-opus-4-6",
        max_tokens=1500,
        messages=[{
            "role": "user",
            "content": f"""Recommend an API versioning strategy for this API.

API characteristics:
{json.dumps(api_description, indent=2)}

Compare these strategies and recommend one:
1. URL versioning (/v1/, /v2/)
2. Header versioning (Accept: application/vnd.myapi.v2+json)
3. Query parameter versioning (?version=2)
4. Date-based versioning (api-version: 2026-01-01)

For the recommended strategy, provide:
- STRATEGY: Which approach
- RATIONALE: Why it fits this API
- IMPLEMENTATION: How to implement it in code (pseudocode OK)
- DEPRECATION_POLICY: Recommended timeline for sunsetting old versions
- EXAMPLE_REQUEST: What an API call looks like"""
        }]
    )
    return response.content[0].text

# Example API description
api_info = {
    "type": "REST",
    "primary_consumers": "third-party developers (public API)",
    "change_frequency": "quarterly breaking changes expected",
    "client_types": ["mobile apps", "web apps", "server-to-server"],
    "company_size": "50 engineer org",
    "existing_versioning": "none — greenfield"
}

strategy = recommend_versioning_strategy(api_info)

For a public API with third-party mobile clients, Claude consistently recommends URL versioning (/v1/, /v2/) with a minimum 12-month deprecation window. For internal APIs with a single consumer team, it usually recommends header versioning to keep URLs clean. The reasoning adapts to the client type, not just pattern-matching.

Automated Version Bump Detection in CI

# .github/workflows/api-version-check.yml
name: API Breaking Change Check

on:
  pull_request:
    paths:
      - 'openapi.yaml'
      - 'openapi.json'
      - 'api/**/*.yaml'

jobs:
  check-breaking-changes:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Get base spec
        run: git show origin/${{ github.base_ref }}:openapi.yaml > /tmp/base_spec.yaml

      - name: Analyze breaking changes
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          pip install anthropic deepdiff pyyaml
          python scripts/breaking_change_detector.py \
            --old /tmp/base_spec.yaml \
            --new openapi.yaml \
            --output /tmp/analysis.json

      - name: Require version bump for breaking changes
        run: |
          BREAKING=$(python3 -c "
          import json
          with open('/tmp/analysis.json') as f:
              data = json.load(f)
          print('yes' if 'YES' in data.get('analysis', '') else 'no')
          ")
          if [ "$BREAKING" = "yes" ]; then
            echo "Breaking changes detected — version bump required"
            exit 1
          fi

This runs on every PR that touches OpenAPI specs. Engineers get an automated comment listing what breaks and what doesn’t before the PR is reviewed, not after it merges.

Consumer Impact Analysis

def assess_consumer_impact(
    breaking_changes: list[dict],
    known_consumers: list[dict]
) -> str:
    """Estimate which consumers are affected by breaking changes."""
    response = client.messages.create(
        model="claude-opus-4-6",
        max_tokens=1000,
        messages=[{
            "role": "user",
            "content": f"""Assess which API consumers are affected by these breaking changes.

Breaking changes:
{json.dumps(breaking_changes, indent=2)}

Known consumers and their usage patterns:
{json.dumps(known_consumers, indent=2)}

For each consumer, output:
CONSUMER: [name]
AFFECTED: [YES/NO/MAYBE]
IMPACTED_FEATURES: [what breaks for them]
MIGRATION_EFFORT: [Low/Medium/High]
CONTACT_PRIORITY: [Immediate/Soon/Low]"""
        }]
    )
    return response.content[0].text

The consumer impact analysis is most useful when you track which endpoints each consumer calls. This data often lives in gateway logs. Export it as a JSON summary and pass it to this function — the output tells you exactly who to contact before the version ships.

Changelog Generation from Spec Diffs

Beyond migration guides, AI can generate changelogs in developer-friendly format:

def generate_changelog_entry(
    version: str,
    breaking_changes: list[str],
    non_breaking_changes: list[str]
) -> str:
    """Generate a CHANGELOG.md entry for an API version."""
    response = client.messages.create(
        model="claude-opus-4-6",
        max_tokens=800,
        messages=[{
            "role": "user",
            "content": f"""Write a CHANGELOG.md entry for API version {version}.

Breaking changes (require migration):
{json.dumps(breaking_changes, indent=2)}

Non-breaking changes (transparent to consumers):
{json.dumps(non_breaking_changes, indent=2)}

Format as standard CHANGELOG.md with:
- ## [{version}] - YYYY-MM-DD header
- ### Breaking Changes section (if any)
- ### Added section (for new endpoints/fields)
- ### Changed section (for non-breaking changes)
- ### Deprecated section (if anything is sunsetted)

Use bullet points. Be specific about field names and endpoints."""
        }]
    )
    return response.content[0].text

Built by theluckystrike — More at zovo.one