# Versioning Conventions

## Overview

The SDK uses semantic versioning and adapter patterns to maintain API stability while supporting multiple backend API versions. This document defines versioning strategies and migration patterns.

## Semantic Versioning

### Version Format

**Use Semantic Versioning 2.0.0**:

```
MAJOR.MINOR.PATCH

Example: 3.2.1
- MAJOR: 3 (breaking changes)
- MINOR: 2 (new features, backward compatible)
- PATCH: 1 (bug fixes, backward compatible)
```

### Version Increments

**MAJOR** (Breaking Changes):
- Removed public methods/properties
- Changed method signatures
- Changed return types
- Renamed classes/modules
- Removed configuration options

```python
# v2.x
pc.list_indexes() -> list[str]

# v3.0 (MAJOR: Changed return type)
pc.list_indexes() -> list[Index]
```

**MINOR** (New Features):
- Added new methods/properties
- Added optional parameters
- Added new modules
- New functionality

```python
# v3.1
pc.list_indexes() -> list[Index]

# v3.2 (MINOR: Added new method)
pc.list_indexes(filter: str | None = None) -> list[Index]
```

**PATCH** (Bug Fixes):
- Bug fixes
- Performance improvements
- Documentation updates
- Internal refactoring

```python
# v3.2.0
def list_indexes() -> list[Index]:
    # Bug: Returns wrong data

# v3.2.1 (PATCH: Bug fix)
def list_indexes() -> list[Index]:
    # Fixed: Returns correct data
```

## Backend API Versioning

### Principle: Target Latest Stable API

**Default approach**: SDK targets the latest stable backend API version.

```python
# ✅ Good: Simple, always use latest API
class HTTPClient:
    def __init__(self, api_key: str):
        self._api_key = api_key
        self._base_url = "https://api.pinecone.io"
        self._headers = {
            "Api-Key": api_key,
            "X-Pinecone-API-Version": "2024-07",  # Latest stable
        }
```

**Rationale**:
- Supporting multiple API versions is complex and costly
- Maintenance burden grows exponentially with each version
- Users benefit from latest features and fixes
- Breaking backend API changes should trigger SDK MAJOR version bump

### When Backend API Changes

**Breaking backend API changes** → Bump SDK MAJOR version

```python
# Backend API 2024-07 breaking changes
# SDK v3.x.x supports 2024-07 API
# SDK v2.x.x supports 2023-12 API

# SDK v3.0.0 (targets 2024-07 API)
class HTTPClient:
    def __init__(self, api_key: str):
        self._headers = {
            "Api-Key": api_key,
            "X-Pinecone-API-Version": "2024-07",
        }

# SDK v2.x.x (targets 2023-12 API)
class HTTPClient:
    def __init__(self, api_key: str):
        self._headers = {
            "Api-Key": api_key,
            "X-Pinecone-API-Version": "2023-12",
        }
```

**Avoid complex adapter layers**:
- Don't build translation layers between API versions
- Don't maintain multiple code paths for different API formats
- Keep it simple: one SDK version = one API version

## Deprecation Policy

### Deprecation Warnings

**Warn before removing features**:

```python
import warnings

# ✅ Good: Deprecation warning
def old_method(self) -> str:
    """
    .. deprecated:: 3.1.0
        Use :meth:`new_method` instead. Will be removed in 4.0.0.
    """
    warnings.warn(
        "old_method is deprecated, use new_method instead",
        DeprecationWarning,
        stacklevel=2
    )
    return self.new_method()

# ❌ Bad: Immediate removal
# def old_method(self):  # Just deleted!
```

### Deprecation Timeline

**Follow minimum timeline**:

1. **Version N**: Feature works normally
2. **Version N+1**: Add deprecation warning
3. **Version N+2**: Keep deprecated feature (still warns)
4. **Version N+3 (MAJOR)**: Remove deprecated feature

```python
# v3.0: Feature introduced
def list_indexes() -> list[str]:
    ...

# v3.1: New method added, old deprecated
def list_indexes() -> list[str]:
    warnings.warn("Use list_index_names instead", DeprecationWarning)
    return self.list_index_names()

def list_index_names() -> list[str]:  # New method
    ...

# v3.2: Still works (deprecated)
def list_indexes() -> list[str]:
    warnings.warn("Use list_index_names instead", DeprecationWarning)
    return self.list_index_names()

# v4.0: Removed
# list_indexes() is gone, only list_index_names() exists
```

### Deprecation Documentation

**Document deprecations clearly**:

```python
def old_method(self) -> str:
    """
    Old method for backward compatibility.

    .. deprecated:: 3.1.0
        Use :meth:`new_method` instead.
        This method will be removed in version 4.0.0.

    Returns:
        str: The result
    """
    warnings.warn(
        "old_method is deprecated since 3.1.0, "
        "use new_method instead. "
        "Will be removed in 4.0.0",
        DeprecationWarning,
        stacklevel=2
    )
    return self.new_method()
```

## Migration Guide

### Changelog

**Maintain detailed changelog**:

```markdown
# Changelog

## [3.2.0] - 2024-02-04

### Added
- Added `filter` parameter to `list_indexes()` method
- New `get_index_stats()` method for index statistics

### Changed
- Improved performance of vector serialization (10x faster)

### Deprecated
- `old_list_indexes()` - Use `list_indexes()` instead

### Fixed
- Fixed race condition in connection pool
- Fixed memory leak in async client

## [3.1.0] - 2024-01-15

### Added
- Support for async/await with `AsyncClient`
- New `batch_upsert()` method for bulk operations

### Breaking Changes (if MAJOR version)
- Removed `Client.old_method()` (deprecated in 2.5.0)
- Changed return type of `list_indexes()` from `list[str]` to `list[Index]`
```

### Migration Scripts

**Provide automated migration tools**:

```python
# pinecone/_internal/migrate/patterns.py
MIGRATION_PATTERNS = {
    "3.0.0": [
        {
            "pattern": r"pc\.list_indexes\(\)",
            "replacement": "[idx.name for idx in pc.list_indexes()]",
            "message": "list_indexes() now returns list[Index], not list[str]"
        },
        {
            "pattern": r"client\.old_method\(",
            "replacement": "client.new_method(",
            "message": "old_method() was removed, use new_method()"
        }
    ]
}
```

### Migration CLI

**Provide CLI tool for migration**:

```bash
# Scan for deprecated patterns
pinecone-migrate scan ./src

# Apply automatic fixes
pinecone-migrate fix ./src --from 2.5.0 --to 3.0.0

# Preview changes
pinecone-migrate fix ./src --dry-run
```

## Version Detection

### Runtime Version Check

**Check SDK version at runtime**:

```python
# pinecone/__init__.py
__version__ = "3.2.1"

def get_version() -> str:
    """Get SDK version."""
    return __version__

# Check version
import pinecone
print(pinecone.__version__)  # "3.2.1"
```

### Compatibility Check

**Check compatibility with backend API**:

```python
class Client:
    def __init__(self, api_key: str):
        self._api_key = api_key
        self._check_compatibility()

    def _check_compatibility(self) -> None:
        """Check SDK compatible with API version."""
        response = self._http.get("/version")
        api_version = response.json()["version"]

        if not self._is_compatible(api_version):
            warnings.warn(
                f"SDK version {__version__} may not be compatible "
                f"with API version {api_version}. "
                f"Consider upgrading the SDK.",
                UserWarning
            )

    def _is_compatible(self, api_version: str) -> bool:
        """Check if SDK compatible with API version."""
        # Check compatibility matrix
        return api_version in SUPPORTED_API_VERSIONS
```

## Version Constants

### Define Version Constants

```python
# pinecone/_internal/version.py

# SDK version
SDK_VERSION = "3.2.1"

# Supported API versions
SUPPORTED_API_VERSIONS = [
    "2024-07",
    "2024-01",
    "2023-10"
]

# Default API version
DEFAULT_API_VERSION = "2024-07"

# Minimum Python version
MIN_PYTHON_VERSION = (3, 10)

# Maximum Python version tested
MAX_PYTHON_VERSION = (3, 12)
```

## Testing Across Versions

### Test Multiple API Versions

```python
import pytest

@pytest.mark.parametrize("api_version", ["2024-07", "2024-01"])
def test_list_indexes_across_versions(api_version):
    """Test list_indexes works with multiple API versions."""
    pc = Pinecone(api_key="test", api_version=api_version)
    indexes = pc.list_indexes()
    assert isinstance(indexes, list)
```

### Test Deprecations

```python
def test_deprecated_method_warns():
    """Deprecated method should warn."""
    client = Client(api_key="test")

    with pytest.warns(DeprecationWarning, match="Use new_method instead"):
        client.old_method()
```

## API Stability Principles

### Stable Interfaces

**What to keep stable**:
- Public class names
- Public method names and signatures
- Return types
- Exception types
- Configuration options

**What can change**:
- Internal implementation
- Private methods/classes
- Performance characteristics
- Internal dependencies

### Adding Features

**Safe changes** (MINOR version):

```python
# ✅ Good: Added optional parameter
# v3.1
def create_index(name: str, dimension: int) -> Index:
    ...

# v3.2 (MINOR)
def create_index(
    name: str,
    dimension: int,
    metric: str = "cosine"  # New optional parameter
) -> Index:
    ...

# ❌ Bad: Changed required parameter (MAJOR)
# v3.1
def create_index(name: str, dimension: int) -> Index:
    ...

# v4.0 (MAJOR)
def create_index(config: IndexConfig) -> Index:  # Breaking change
    ...
```

## Anti-Patterns

### ❌ Breaking Changes in MINOR/PATCH

```python
# Bad: Breaking change in PATCH version
# v3.2.0
def list_indexes() -> list[str]:
    ...

# v3.2.1 (Should be 4.0.0!)
def list_indexes() -> list[Index]:  # Breaking change!
    ...
```

### ❌ No Deprecation Warning

```python
# Bad: Removing feature without warning
# v3.1
def old_method():
    ...

# v3.2
# old_method just removed!
```

**Solution**: Deprecate first.

```python
# Good: Deprecate in MINOR, remove in MAJOR
# v3.1
def old_method():
    warnings.warn("Use new_method", DeprecationWarning)
    return new_method()

# v4.0
# Now safe to remove old_method
```

### ❌ Silent Behavior Changes

```python
# Bad: Changed behavior without version bump
# v3.2.0
def list_indexes():
    return sorted(indexes)  # Sorted

# v3.2.0 (same version!)
def list_indexes():
    return indexes  # No longer sorted!
```

**Solution**: Document behavior, make explicit.

```python
# Good: Behavior is documented
def list_indexes(sorted: bool = False) -> list[str]:
    """
    List all indexes.

    Args:
        sorted: If True, return sorted list. Default False.
    """
    indexes = self._fetch_indexes()
    return sorted(indexes) if sorted else indexes
```

## Version Documentation

### Document Version Requirements

```python
# README.md
## Requirements

- Python 3.10+
- Pinecone API key

## Installation

```bash
pip install pinecone-client>=3.0.0,<4.0.0
```

### Document Breaking Changes

```markdown
## Upgrading to 3.0

### Breaking Changes

1. **list_indexes() return type changed**
   ```python
   # Before (2.x)
   names: list[str] = pc.list_indexes()

   # After (3.x)
   indexes: list[Index] = pc.list_indexes()
   names: list[str] = [idx.name for idx in pc.list_indexes()]
   ```

2. **Removed old_method()**
   ```python
   # Before (2.x)
   client.old_method()

   # After (3.x)
   client.new_method()
   ```
```

## References

- See [api-stability.md](./api-stability.md) for stability principles
- See [architecture.md](./architecture.md) for adapter pattern usage
- See [errors.md](./errors.md) for exception versioning
- See [testing.md](./testing.md) for version testing strategies
