API Stability Commands

The api command provides tools for detecting breaking changes in public APIs to maintain backwards compatibility.

Overview

API commands are provided by the structum-cli-tools plugin:

pip install structum-cli-tools

Uses static analysis to detect API changes that would break client code.

Commands

api diff

Compare API surfaces between two git references.

Arguments:

refs

Git reference range (e.g., v0.1.0..HEAD, main..feature)

Examples:

# Compare with last release
structum api diff v0.1.0..HEAD

# Compare branches
structum api diff main..feature/new-api

# Compare specific commits
structum api diff abc123..def456

Breaking Changes Detected:

  • Removed public functions/classes

  • Modified function signatures

  • Changed return types

  • Removed parameters

  • Modified parameter defaults

  • Moved modules/classes

Example Output (planned):

📜 Comparing API: v0.1.0..HEAD

🔍 Base: v0.1.0
🔍 Target: HEAD

❌ BREAKING CHANGES:

- mypackage.auth.login()
  Parameter 'remember_me' removed

- mypackage.User.email
  Property changed from str to Optional[str]

⚠️  API MODIFICATIONS:

- mypackage.config.load()
  New parameter 'validate' added (has default)

Tools: griffe

api validate

Validate current API structure and documentation.

Checks:

  1. ✅ Public APIs have docstrings

  2. ✅ Type annotations present

  3. ✅ Deprecated functions have warnings

  4. ✅ Consistent naming conventions

Examples:

structum api validate

Example Output:

✅ Validating API Structure

🔍 Analyzing API surface...

  ✅ Docstrings
  ✅ Type annotations
  ✅ Deprecated warnings
  ✅ Naming conventions

✅ API Validation Passed

💡 API surface is stable and well-documented

Configuration

API Stability Policy

Define stability guarantees in documentation:

"""
API Stability:

- Public API: Follows semantic versioning
- Private API (_prefix): No guarantees
- Experimental: May change without notice

Breaking changes:
- Major version bump required
- Deprecation warnings for 1 version
- Migration guide provided
"""

Deprecation Pattern

Use warnings module for deprecations:

import warnings

def old_function():
    warnings.warn(
        "old_function() is deprecated, use new_function() instead",
        DeprecationWarning,
        stacklevel=2
    )
    return new_function()

CI/CD Integration

GitHub Actions API Check

name: API Stability

on: pull_request

jobs:
  api-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Install tools
        run: pip install structum-cli-tools

      - name: Check API changes
        run: structum api diff origin/main..HEAD

      - name: Validate API
        run: structum api validate

Best Practices

Maintaining Backwards Compatibility

DO:

  • Add new parameters with defaults

  • Create new functions/classes

  • Extend return types (make more specific)

  • Add optional fields to data classes

DON’T:

  • Remove public functions/classes

  • Change function signatures

  • Remove parameters

  • Change return types incompatibly

  • Move public APIs without aliases

Deprecation Workflow

# Version 0.1.0: Original
def calculate(x, y):
    return x + y

# Version 0.2.0: Deprecate
def calculate(x, y):
    warnings.warn(
        "calculate() is deprecated, use add() instead",
        DeprecationWarning,
        stacklevel=2
    )
    return add(x, y)

def add(x, y):
    return x + y

# Version 0.3.0: Remove
# calculate() removed, only add() exists

Semantic Versioning

Version format: MAJOR.MINOR.PATCH

  • MAJOR: Breaking API changes

  • MINOR: New features (backwards compatible)

  • PATCH: Bug fixes (backwards compatible)

Type Annotations

Complete Type Coverage

from typing import Optional, List

def process_users(
    users: List[str],
    admin: Optional[str] = None
) -> dict[str, int]:
    """Process users and return counts.

    Args:
        users: List of usernames
        admin: Optional admin username

    Returns:
        Dictionary mapping usernames to counts
    """
    ...

Type Checkers

Use mypy for validation:

mypy src/

Troubleshooting

False Positives

Griffe may report false breaking changes for:

  • Internal implementation details

  • Private APIs (_prefix)

Manually review and document intentional changes.

Missing Type Annotations

Add type hints incrementally:

# Find untyped functions
mypy --disallow-untyped-defs src/

See Also