P31 · Typing & Robust Code
gradual typing, mypy, error-handling & linting
Make Python robust before it ships: types a checker verifies, error handling designed like a type system, and linting that codifies the team's contract - the second half of correctness after tests, closing the P30/P31 capstone.
A test tells you the code was right for the cases you thought of. A type checker tells you something weaker about every case at once, and the two are worth having together precisely because neither covers the other's blind spot. This pillar is the second half of correctness: making Python robust before it runs, and codifying the standard so it is enforced by a gate rather than relitigated in review.
Typing comes first, and gradual is the operative word — annotations are added
where they pay, and mypy verifies them in CI rather than at the mercy of anyone
remembering. Optional and | None are treated as the useful imposition they
are: they force the missing case into the open, which is where most None
crashes were hiding. Then the parts that are genuinely tricky — generics,
Protocols, ParamSpec for decorators that must preserve a signature — graded by
inspecting the types themselves rather than by whether the code happened to run.
Error handling is treated as design rather than reflex. EAFP over LBYL where Python favours it, an exception hierarchy shaped so callers can catch at the level they actually care about, chaining that preserves the original cause instead of discarding it, and preconditions that reject bad input at the door rather than allowing it to fail somewhere unrecognisable. Linting closes the pillar by making style a contract: a lint rule is an opinion expressed over the syntax tree, ruff runs hundreds of them, and a checked-in config plus a CI gate is what turns "we prefer" into something enforceable. The P30/P31 capstone joins both halves.