How to combine boundary validation, precise Python types and static analysis without confusing runtime parsing with domain correctness.

Production principle

Validate untrusted data once, convert it into precise application types and run strict static analysis over the code that makes decisions.

Degraded
def create_order(data: dict) -> dict:
    customer_id = data.get("customer_id")
    total = data.get("total", 0)
    return {"id": save(customer_id, total)}
Repaired
class CreateOrderRequest(BaseModel):
    customer_id: UUID
    lines: list[OrderLineRequest] = Field(min_length=1)

@dataclass(frozen=True)
class CreateOrder:
    customer_id: CustomerId
    lines: tuple[OrderLine, ...]

def create_order(command: CreateOrder) -> OrderResult: ...
01

Validate at boundaries

Pydantic is strongest where untrusted JSON becomes an accepted request or where internal results become a documented response. Put size limits, formats and cross-field validation close to that boundary.

Do not pass transport models through every layer. Convert accepted input into application commands and domain value objects so framework configuration, optional request fields and serialization aliases do not spread into business logic.

02

Prefer precise types over annotations everywhere

Annotating a value as str does not distinguish an email address, order ID and currency code. Small value types and enums can encode meaning, while dataclasses make immutable commands explicit.

Avoid Any at integration boundaries. If a third-party library is untyped, isolate it behind a typed adapter and validate its result. A cast should record verified knowledge, not silence a warning the program cannot justify.

03

Turn static analysis into a gate

Run mypy or Pyright in CI with a documented strictness baseline. Ratchet weak modules toward stricter checking rather than introducing thousands of ignored errors at once.

Static analysis cannot prove transaction safety or business correctness. Its evidence is narrower but valuable: consistent call contracts, exhaustiveness, null handling and fewer refactoring surprises. Pair it with runtime validation and behavioural tests.

Review checklist

Evidence to take into review

  • External input is validated before application logic.
  • Transport models do not become domain models by default.
  • Important identifiers and states use precise types.
  • Any and type ignores are isolated and justified.
  • Static analysis runs in CI with a maintained baseline.
Continue the inspection

Explore all engineering notes.

Use PRODUCTION-7 to connect this concern with the other dimensions of a trustworthy backend.

View all articles Get the checklist