BriteCore SDK v2.0.0 Roadmap

Last updated: July 16, 2026 Status: Planning & Implementation

Overview

v2.0.0 introduces breaking changes focused on moving away from implicit global state, improving type safety, standardizing error handling, and providing better extensibility. These changes align with modern Python SDK best practices.


Breaking Changes & Migration Path

Phase 1: Client Lifecycle Redesign

Goal: Move away from module-global implicit client state; make explicit client instances the default API.

Current State (legacy)

# Implicit global client - hard to test, unclear dependencies
from britecore_sdk.api.api_calls import retrieve_quote
quote = retrieve_quote(quote_number="Q123")

# Or explicit but cumbersome
from britecore_sdk.api.api_calls import use_api_client, retrieve_quote
client = BritecoreAPIClient("site").init_client()
with use_api_client(client):
    quote = retrieve_quote(quote_number="Q123")

v2.0.0 Target

# Explicit client is now primary pattern
from britecore_sdk import BritecoreAPIClient
from britecore_sdk.api.api_calls.v2 import quotes

client = BritecoreAPIClient("site").init_client()
quote = quotes.retrieve_quote(quote_number="Q123", client=client)

# Or with context manager (preferred for cleanup)
with BritecoreAPIClient("site").init_client() as client:
    quote = quotes.retrieve_quote(quote_number="Q123", client=client)

Implementation Strategy

  1. Add optional client parameter to all endpoint wrappers

  2. Fall back to module-level client if not provided (for backwards compatibility during transition)

  3. Deprecate implicit client in a minor release (after initial rollout)

  4. Remove implicit client fallback in v2.0.0

  5. Update all examples to use explicit client pattern

  6. Add type hints for client parameter (remove Unpack[RequestParameters] anti-pattern)

Acceptance Criteria

  • [ ] All v2 endpoint wrappers accept optional client: BritecoreAPIClient parameter

  • [ ] Wrappers prefer explicit client over module-level client

  • [ ] Examples use explicit client pattern throughout

  • [ ] Migration guide covers the transition


Phase 2: Typed Response Models

Goal: Reduce Any returns; provide typed response models (or typed envelopes) for sync + async parity.

Current State (legacy)

# Returns `Any` - no IDE autocomplete, runtime surprises
result = retrieve_quote(quote_number="Q123")
quote_premium = result.get("premium")  # Could be anything!

# Types are unclear in async too
result = await aretrieve_quote(quote_number="Q123")  # Same Any return

v2.0.0 Target

# Typed response models with autocomplete
from britecore_sdk.api.responses import QuoteResponse
from britecore_sdk.api.api_calls.v2 import quotes

quote: QuoteResponse = quotes.retrieve_quote(quote_number="Q123", client=client)
quote_premium = quote.premium  # Type-safe, IDE autocomplete

# Async has parity
quote: QuoteResponse = await quotes.aretrieve_quote(quote_number="Q123", client=client)

Response Model Structure

# britecore_sdk/api/responses/__init__.py
from dataclasses import dataclass
from typing import Any, Optional

@dataclass
class ResponseEnvelope:
    """Base envelope for all API responses."""
    success: bool
    data: Any  # The actual typed model
    message: Optional[str] = None
    messages: Optional[list[str]] = None
    request_id: str = ""  # Extracted from response headers
    status_code: int = 200

@dataclass
class QuoteResponse:
    """Typed response for quote retrieval."""
    quote_id: str
    quote_number: str
    status: str
    premium: float
    term_days: int
    effective_date: str
    # ... other fields

    @classmethod
    def from_api(cls, data: dict) -> "QuoteResponse":
        """Parse API response dict into typed model."""
        return cls(
            quote_id=data.get("id"),
            quote_number=data.get("quoteNumber"),
            # ...
        )

Implementation Strategy

  1. Define response models for each domain (quotes, policies, contacts, etc.)

  2. Use dataclass or pydantic BaseModel for runtime validation (opt-in)

  3. Create factory methods (.from_api()) to hydrate models from API responses

  4. Update all wrappers to return typed models instead of raw dicts/Any

  5. Maintain backwards compatibility shim (legacy paths continue returning dict/Any)

  6. Provide both ResponseEnvelope and direct typed models for flexibility

Acceptance Criteria

  • [ ] Response models defined for all major domains (quotes, policies, contacts, etc.)

  • [ ] All v2 wrappers return typed models (not Any or dict)

  • [ ] Async wrappers return same types as sync equivalents

  • [ ] Response models support .from_api() factory pattern

  • [x] Pydantic validation available (opt-in via britecore_sdk[typed-config] extra; see britecore_sdk.settings.get_typed_settings() for the settings view)

  • [ ] Full pydantic response models for API payloads (not yet implemented)

  • [ ] Type stubs (.pyi) generated or provided for IDE support


Phase 3: Standardized Error Model

Goal: Standardize exceptions with status_code, error_code, request_id, and raw payload attached.

Current State (legacy)

# Error details scattered across exception message
try:
    retrieve_quote(quote_number="Q123")
except AuthenticationError as e:
    print(str(e))  # Message only, no structured data
    # No access to HTTP status, error code, or request ID

v2.0.0 Target

# Rich exception context
from britecore_sdk.exceptions import NotFoundError, ValidationError

try:
    retrieve_quote(quote_number="INVALID")
except NotFoundError as e:
    print(e.status_code)      # 404
    print(e.error_code)       # "quote_not_found"
    print(e.request_id)       # "abc123def456"
    print(e.detail)           # "Quote INVALID does not exist"
    print(e.raw_payload)      # Full server response dict

except ValidationError as e:
    print(e.status_code)      # 400
    print(e.error_code)       # "invalid_field"
    print(e.validation_errors) # {"premium": ["Must be positive"]}

Exception Hierarchy (v2.0.0)

class BritecoreError(Exception):
    """Base exception - now with structured metadata."""
    status_code: int
    error_code: Optional[str]
    request_id: Optional[str]
    detail: str
    raw_payload: dict

    def __init__(
        self,
        detail: str,
        status_code: int = 500,
        error_code: Optional[str] = None,
        request_id: Optional[str] = None,
        raw_payload: Optional[dict] = None,
    ):
        self.detail = detail
        self.status_code = status_code
        self.error_code = error_code
        self.request_id = request_id
        self.raw_payload = raw_payload or {}
        super().__init__(detail)

# Flat aliases still available
class AuthenticationError(BritecoreError):
    pass

class NotFoundError(BritecoreError):
    pass

class ValidationError(BritecoreError):
    validation_errors: dict

Implementation Strategy

  1. Enhance BritecoreError base class with status_code, error_code, request_id, raw_payload

  2. Update process_result() to extract and attach metadata to exceptions

  3. Extract request_id from response headers (X-Request-ID or similar)

  4. Parse server error payloads to extract structured error details

  5. Preserve backwards compatibility - existing str(exception) still works

  6. Document exception handling patterns in TROUBLESHOOTING.md

Acceptance Criteria

  • [ ] All exceptions have status_code, error_code, request_id attributes

  • [ ] process_result() populates exception metadata from API responses

  • [ ] Request ID extracted and attached to exceptions + logged

  • [ ] Validation errors available as .validation_errors dict

  • [ ] Raw payload preserved for debugging

  • [ ] Backwards compatible - str(exception) produces readable message


Phase 4: Transport Boundary & Middleware

Goal: Introduce pluggable transport/middleware hooks for retry, logging, tracing, custom headers.

Current State (legacy)

# Limited customization - stuck with built-in retry/rate limit logic
client = BritecoreAPIClient("site").init_client()
# No way to inject custom logging, tracing, headers without subclassing

v2.0.0 Target

# Rich middleware/hook system
from britecore_sdk.transport import Middleware, RequestContext, ResponseContext

class TracingMiddleware(Middleware):
    def on_request(self, ctx: RequestContext) -> RequestContext:
        """Before request is sent."""
        ctx.headers["X-Trace-ID"] = self.tracer.start_span()
        return ctx

    def on_response(self, ctx: ResponseContext) -> ResponseContext:
        """After response received."""
        self.tracer.end_span(status=ctx.status_code)
        return ctx

    def on_error(self, error: Exception, ctx: RequestContext) -> Exception:
        """On request error."""
        self.tracer.record_error(error)
        return error

class CustomRetryMiddleware(Middleware):
    def on_response(self, ctx: ResponseContext) -> ResponseContext:
        if ctx.status_code == 429:
            # Custom backoff logic
            backoff = calculate_backoff(ctx.response_data)
            raise RetryableError(backoff_seconds=backoff)
        return ctx

client = BritecoreAPIClient("site").init_client(
    middleware=[
        TracingMiddleware(tracer=my_tracer),
        CustomRetryMiddleware(),
    ]
)

# Or add middleware post-init
client.add_middleware(LoggingMiddleware(logger=my_logger))

Implementation Strategy

  1. Define Middleware base class with hooks: on_request, on_response, on_error

  2. Define RequestContext and ResponseContext to pass through middleware chain

  3. Refactor do_request() to execute middleware chain

  4. Provide built-in middleware: RetryMiddleware, LoggingMiddleware, RateLimitMiddleware

  5. Support middleware stack - order matters

  6. Document middleware patterns with examples (OpenTelemetry, custom logging, etc.)

Acceptance Criteria

  • [ ] Middleware base class defined with hook methods

  • [ ] RequestContext and ResponseContext types defined

  • [ ] Middleware chain execution in do_request()

  • [ ] Built-in middleware for retry, logging, rate limiting

  • [ ] add_middleware() and remove_middleware() on client

  • [ ] Examples: OpenTelemetry integration, custom logging, header injection

  • [ ] Documentation and tests for middleware system


Phase 5: Pagination Ergonomics

Goal: Add iterator helpers for list endpoints instead of manual page plumbing.

Current State (legacy)

# Manual pagination - error-prone
page = 1
all_quotes = []
while True:
    response = list_quotes(page=page, limit=100, client=client)
    all_quotes.extend(response["data"])
    if len(response["data"]) < 100:
        break
    page += 1

v2.0.0 Target

# Iterator-based pagination - Pythonic
from britecore_sdk.api.api_calls.v2 import quotes

# Option 1: Iterator pattern
for quote in quotes.iter_quotes(limit=100, client=client):
    print(f"Quote: {quote.quote_number}")

# Option 2: Collect all with auto-pagination
all_quotes = list(quotes.iter_quotes(client=client))

# Option 3: Async iterator
async for quote in quotes.aiter_quotes(client=client):
    print(f"Quote: {quote.quote_number}")

Iterator Implementation

from typing import Iterator, AsyncIterator
from britecore_sdk.api.responses import QuoteResponse

def iter_quotes(
    client: BritecoreAPIClient,
    limit: int = 100,
    **kwargs
) -> Iterator[QuoteResponse]:
    """Iterate over all quotes, handling pagination automatically."""
    page = 1
    while True:
        response = list_quotes(page=page, limit=limit, client=client, **kwargs)
        if not response.data:
            break
        for item in response.data:
            yield QuoteResponse.from_api(item)
        if len(response.data) < limit:
            break
        page += 1

async def aiter_quotes(
    client: AsyncBritecoreAPIClient,
    limit: int = 100,
    **kwargs
) -> AsyncIterator[QuoteResponse]:
    """Async iterator for quotes with automatic pagination."""
    page = 1
    while True:
        response = await alist_quotes(page=page, limit=limit, client=client, **kwargs)
        if not response.data:
            break
        for item in response.data:
            yield QuoteResponse.from_api(item)
        if len(response.data) < limit:
            break
        page += 1

Implementation Strategy

  1. Add iter_*() function for each list endpoint

  2. Add aiter_*() async function for each list endpoint

  3. Support limit parameter for page size

  4. Support filtering kwargs (pass through to underlying list function)

  5. Handle is_last_page or similar field in API responses

  6. Document pagination patterns with examples

Acceptance Criteria

  • [ ] Iterator functions added for all list endpoints

  • [ ] Async iterator functions added for all list endpoints

  • [ ] Manual pagination still supported (backwards compatible)

  • [ ] Iterator handles empty results gracefully

  • [ ] Iterator supports filtering parameters

  • [ ] Examples document iterator usage patterns


Phase 6: Legacy Cleanup

Goal: Remove/deprecate v1-only or compatibility shims from core path; keep migration helpers in separate module.

Current State (legacy)

src/britecore_sdk/
├── classes/           # Legacy compatibility layer (deprecated)
├── api/api_calls/
│   ├── v1/           # Old versioned endpoints (still supported for backwards compat)
│   └── v2/           # New endpoints
└── ...

v2.0.0 Target

src/britecore_sdk/
├── api/api_calls/
│   ├── v2/           # Current active endpoints (all v2)
│   └── _compat/      # Migration helpers ONLY (moved from v1/)
├── models/           # Response models
├── validators/       # Input validators
├── transport/        # Pluggable transport/middleware
├── responses/        # Response envelopes
└── ...

# Removed or moved:
# - classes/           → Remove entirely, direct to models/validators
# - api_calls/v1/*     → Move useful utilities to _compat/
# - Legacy compatibility shims

What Gets Removed

# REMOVED in v2.0.0:
from britecore_sdk.classes.quote import Quote  # ❌ Use models instead

# REMOVED in v2.0.0:
from britecore_sdk.api.api_calls.v1.custom_ui import ...  # ❌ Moved to compat helpers with migration guide

# STILL WORKS in v2.0.0:
from britecore_sdk.api.api_calls.v2.quotes import retrieve_quote  # ✅

Migration Helpers Module

src/britecore_sdk/api/_compat/
├── __init__.py
├── v1_route_mapping.py   # Maps old v1 imports to new locations
├── migration_guide.md    # User-facing migration instructions
└── examples/
    └── v1_to_v2_migration.py

Implementation Strategy

  1. Audit classes/ module - move useful models to models/, remove rest

  2. Review api_calls/v1/ - preserve supported wrappers when the upstream API still has no v2 equivalent

  3. Move migration shims to api/_compat/ with clear deprecation notices

  4. Update imports in __init__.py to point to new locations

  5. Remove circular import workarounds and other v1-specific hacks

  6. Create archived adoption notes in docs/migrations/

Acceptance Criteria

  • [ ] classes/ module removed or fully deprecated

  • [ ] Supported api_calls/v1 wrappers retained when no v2 SDK equivalent exists

  • [ ] Migration helpers available in _compat/ with clear instructions

  • [ ] No circular imports or compatibility shims in core path

  • [ ] __all__ exports cleaned up

  • [ ] Archived adoption notes provided for the major 2.0.0 design changes


Implementation Timeline

Week 1-2: Foundation

  • Phase 1: Client lifecycle parameter support

  • Phase 3: Enhanced exception model

Week 3-4: Type Safety

  • Phase 2: Response models and typed returns

  • Phase 5: Pagination iterators

Week 5-6: Extensibility

  • Phase 4: Transport middleware system

  • Phase 6: Legacy cleanup


Version Strategy

Historical note: This roadmap captured pre-release planning. The current 2.x policy does not treat all api_calls/v1 wrappers as legacy, and it does not require a blanket SDK-wide v1v2 rewrite.

1.6.0 (Minor - Deprecations)

  • Add deprecation warnings for implicit client usage

  • Add new explicit client pattern alongside old one

  • Response models available but optional

  • Typed exceptions available but not default

  • New middleware system available for early adopters

1.7.0 (Minor - Foundation)

  • Pagination iterators available

  • Adoption/design notes published

  • Deprecations become more vocal

v2.0.0 (Major - Breaking)

  • Remove implicit client fallback

  • Only explicit client API available

  • Response models mandatory

  • Typed exceptions with metadata

  • Transport middleware required pattern

  • Legacy classes/ removed

  • Supported api_calls/v1 wrappers retained where the upstream API or SDK coverage still requires them


Testing Strategy

Each phase should include:

  • Unit tests for new functionality

  • Integration tests with mock API

  • Backwards compatibility tests (where applicable)

  • Migration path tests (old code still works, with warnings)

  • Type checking tests (mypy, pyright)


Documentation Updates

For each phase:

  1. Update CHANGELOG.md with changes

  2. Update DEPRECATION.md if applicable

  3. Add/update docstrings with examples

  4. Create/update migration guide in docs/migrations/

  5. Update README.md and GETTING_STARTED.md

  6. Add examples in examples/


Success Metrics

  • [ ] All phases completed

  • [ ] 100% type hint coverage (no Any in public API)

  • [ ] Zero breaking changes in 1.x releases

  • [ ] Clear adoption guidance for explicit-client patterns and related 2.0.0 design changes

  • [ ] Cleaner, more maintainable codebase

  • [ ] Community feedback positive on v2 design


See Also