Reviewed 6 September 2026

Part II: Practices

Validation and data models

Library Use
Pydantic v2 Validation, coercion and serialization at I/O boundaries: HTTP payloads, config files, external API responses, model output.
dataclasses Stdlib internal value objects. slots=True and frozen=True reduce memory use and prevent mutation.
attrs Similar scope to dataclasses with more features: validators, converters, __init__ customization.
pydantic-settings Loads and validates configuration from environment variables, .env files and secrets directories.
msgspec Alternative serialization and validation library with lower overhead, no coercion by default.

Validate at the process boundary and use plain objects internally:

class CreateJob(BaseModel):          # boundary
    symbol: str
    window: int = Field(gt=0, le=512)

@dataclass(frozen=True, slots=True)  # internal
class Job:
    symbol: str
    window: int

Configuration validated at startup fails immediately on a missing or malformed value rather than at first use:

class Settings(BaseSettings):
    database_url: PostgresDsn
    log_level: Literal["DEBUG", "INFO", "WARNING"] = "INFO"
    model_config = SettingsConfigDict(env_file=".env")