What you'll learn: how Python holds structured data (dataclasses, Pydantic models), why only Pydantic checks what it is given, how the capstone's
ProductandRequirementsmodels express rules as field metadata, and how to validate once at the edge.
At a glance
Figure 1. Validation at the boundary
How to read this: all checking happens in the middle box, so the code to its right never asks whether a price is a number.
- A dataclass stores fields and generates a constructor. It does not check input.
- A Pydantic model validates on construction and converts compatible input by default.
- Rules live next to the field:
Field(ge=0),Field(pattern=...), aLiteral. - Validate where data enters (HTTP, files, model replies), then trust the object.
- Report where the problem is, not what was submitted: the value may be private.
Scenario
Composite scenario, not a real company. Lumen Industrial Supply, the fictional distributor, exports its product catalog from an old system as JSON. Last week a row arrived with the price "call us", another with a lowercase SKU, and one with a negative price. The old loader accepted all three, and a sales engineer saw a quote line costing minus 5 euros. All rows and prices here are illustrative.
What you will be able to do
- Choose between a dataclass and a Pydantic model for a piece of data.
- Express a range, a pattern and a closed set of values as field rules.
- Validate untrusted rows and report which rows and fields failed without echoing values.
- Explain lax versus strict validation.
Prerequisites
Python for Java and Node engineers explains why a hint alone is not enforced. For why model replies also need validating, see AI Architect: agents, tools and MCP; this lesson covers the Python mechanics only.
Core ideas
Records, schemas, dataclasses and models
| Question | Java record | TypeScript with Zod | Python dataclass | Pydantic model |
|---|---|---|---|---|
| Stores named fields | Yes | A type, plus a schema | Yes | Yes |
| Generates constructor, equality, text form | Yes | No (plain objects) | Yes | Yes |
| Rejects a wrong type at runtime | The compiler checks your own code; JSON input is a separate step | Only if you call the schema | No | Yes |
| Range and pattern rules | Bean Validation annotations, run by a validator | In the schema | Not provided | In the field |
| Parses JSON text | With a JSON library | parse on the schema |
Not provided | model_validate_json |
Sources:
- A Java record is a data carrier: the compiler generates the constructor, accessors,
equals,hashCodeandtoString. - Jakarta Bean Validation defines constraints such as
@NotNull,@Minand@Pattern. - Zod is TypeScript-first schema validation with static type inference.
@dataclassadds__init__,__repr__and__eq__, and apart from two special markers "nothing in@dataclassexamines the type specified in the variable annotation".
Spot the flaw. A teammate says "the types make this safe: price is a float". Is that true?
Verified: capstone/fde-product-discovery-copilot/lessons/M1-L2/m1_l2_boundary.py
@dataclass(frozen=True)
class PlainProduct:
"""A dataclass stores whatever it is given. Nothing here checks a type or a range."""
sku: str
price_eur: float
in_stock: bool = True
Answer
No. The annotation is never examined, so PlainProduct(sku=12, price_eur="free") builds an object with a number as the SKU and text as the price; test_dataclass_accepts_nonsense runs exactly that. A dataclass suits data you create yourself, not outside input.
Pydantic: validation is the constructor
The Pydantic documentation says: "Initialization of the object will perform all parsing and validation." Three entry points matter:
Model(...)andModel.model_validate(data)take Python values.model_validate_json(text)takes JSON text.model_dump()goes the other way, from the object back to plain values.
Two behaviours surprise Java and Node engineers.
- It converts by default. Pydantic "may cast input data to force it to conform to model field types". A string such as
"123"is valid for anintin lax mode, not in strict mode. Passstrict=Truefor no conversion. - It reports every error at once. One
ValidationErroris raised "regardless of the number of errors found", so you can show all bad fields in one reply.
Its guarantee is narrow: "Pydantic guarantees the types and constraints of the output, not the input data".
The capstone's Product model
Figure 2. The Product model
How to read this: the middle box is the fields of one Python class; PROTOCOL and SPEC are a list and a dictionary inside the product, not tables.
Verified: capstone/fde-product-discovery-copilot/app/models.py