Summary
This module moved from a paragraph of English to typed records that a type checker can verify, and the path between those two points is the method.
Decomposition happens before code. Restate the requirement in one sentence, break it into ordered steps, identify the state that must be tracked, choose structures by access pattern, write pseudocode, and only then implement. Attack the result with edge cases as a systematic list rather than by intuition.
Data structures are chosen by the question you will ask most often. Membership testing belongs in a set, lookup by name belongs in a dict, ordered iteration belongs in a list, and a fixed group of values belongs in a tuple. The gap between an O(n) membership test and an O(1) one is invisible on small data and decisive on large data. Counter, defaultdict, and deque remove code you would otherwise write by hand.
Readable logic is flat. Guard clauses eliminate failure paths one at a time and keep the real work at the leftmost indentation. Truthiness collapses None, empty, and zero into one condition, so use is None whenever those differ. Structural pattern matching handles dispatch on payload shape better than a chain of conditionals.
Records replace dictionaries as soon as a bundle of values has a name. A dataclass gives you checked field names, readable printing, and field-by-field equality for one line of code. frozen=True makes records safe to pass through a pipeline and usable as set members. Provenance and status fields turn a record into something that can answer questions about itself later.
Type hints are design decisions. | None forces callers to handle absence. Literal writes down a closed vocabulary. Pydantic validates at runtime what a dataclass only annotates. Validation happens once, at the boundary, so that every function inside can trust what it receives.
Key takeaways
- If you cannot restate the requirement in one sentence, the next step is a question, not code
- Ordering your steps on paper reveals design decisions that are expensive to discover later
- Choose a structure by the question you will ask it, not by what you used last time
- Membership testing in a list is the most common avoidable performance mistake in Python
- Flat code beats nested code, and guard clauses are how you get it
is Noneis never wrong, and truthiness sometimes is- Give data a type as early as possible and keep it typed
- Frozen records make pipelines debuggable, because nothing can change behind your back
- Provenance is what lets you answer "where did this come from" six months later
- Validate once at the edge so that nothing inside needs to defend itself
Common mistakes to remember
- Writing code before the requirement has been restated in one sentence
- Using a list for membership testing when nothing depends on order
- Passing
[]instead oflisttodefaultdict - Reading a missing key from a
defaultdictand silently creating it - Writing
if not valuewhen zero or empty is a meaningful value - Accessing nested payloads by position rather than by shape
- Building a comprehension for its side effects
- Using a mutable default in a dataclass without
default_factory - Passing dictionaries between functions when the shape is fixed and known
- Assuming a dataclass validates anything at runtime
- Repeating defensive checks in every function instead of validating once at the boundary
- Storing naive datetimes without a timezone