Don't let dicts spoil your code

Role of dicts in Python and when to use them

  • Many agree dicts are fine for their “native” role: dynamic key–value storage, lookups, and small, ad‑hoc structures (e.g., notebooks, quick scripts).
  • Several argue they become “cancerous” when used as long‑lived, semi‑structured records passed across many layers. Then key presence, spelling, and defaults become hard to track, and refactors are risky.
  • Others counter that dicts are central to Python’s design and that replacing them wholesale with classes is overkill and loses useful operations (comprehensions, merging, rich “algebra” on mappings).

Types, duck typing, and refactoring

  • One camp stresses that duck typing and untyped dicts make refactors fragile; field renames or schema changes surface only in tests or production.
  • Static or “strong” typing (including TypedDicts, dataclasses, Pydantic models) lets tools flag all breakages at change time and enables automatic refactors.
  • Skeptics argue Python is fundamentally dynamic, and heavy typing pushes it toward being a language it isn’t.

API boundaries, validation, and data modeling

  • Strong agreement that external API data should be validated/sanitized early, with clear contracts and allowlists.
  • Some advocate “functional core, imperative shell”: convert JSON/dicts at the boundary into well‑typed value objects, then keep internal logic type‑safe and null‑free.
  • Others warn that aggressively dropping unknown fields can break APIs that expect full objects back; passing through unknown fields or configuring libs (e.g., Pydantic extra fields) is sometimes necessary.
  • There’s debate over “parse don’t validate”: some see it as clarifying and safe; others worry it explodes the number of types and tightly couples code to schemas.

Alternatives to raw dicts in Python

  • Mentioned options: dataclasses, TypedDict, namedtuples, Pydantic models, msgspec Structs, runtime checkers like beartype and typeguard.
  • Trade‑offs discussed:
    • Dataclasses/Pydantic: semantics, validation, better tooling, but more ceremony and potential performance cost.
    • TypedDict: compile‑time help without runtime conversion, but refactoring tooling lags.

Perspectives from other ecosystems

  • Elixir maps/structs and Clojure maps are cited as friendlier due to immutability and language design.
  • TypeScript shows how strong typing with plain objects works well.
  • Go, Swift (Codable), Rust, and bioinformatics tools illustrate both benefits and pains of strict schemas versus loose JSON/dict handling.