Make schemas carry precise operational intent
How to study this lesson
Read one section, trace the worked example, and try the knowledge check. Bookmark saves a shortcut; notes appear in My notebook. Mark lesson complete is your own assessment and does not mark its lab passed.
By the end, you can
- Specify structural and domain validation separately.
- Identify ambiguous optional fields and units.
- Plan contract evolution without silent reinterpretation.
From a Python function to a public promise
A Python signature is a useful starting point for a tool contract, but it rarely expresses everything the caller needs. reserve(sku, quantity) leaves unanswered whether quantity means units or cases, whether zero releases a reservation, whether negative values are meaningful, and whether a missing SKU can be inferred. A model will often produce syntactically plausible arguments despite these ambiguities. Clarifying the contract is therefore part of reliable tool use, not merely documentation polish.
For this module, quantity is an integer number of individual units from one through twenty. SKU is an exact catalog identifier. Unknown fields are rejected because an accidental warehouse field must not create the impression that warehouse selection was honored. The server never substitutes a nearby product automatically.
Structure and meaning need different checks
JSON Schema describes properties such as types, required members, enumerations, and numeric bounds. Its required keyword concerns presence, not whether a business object exists. A structurally valid SKU can still be absent from the catalog. A numeric quantity can satisfy its bounds while exceeding the caller's reservation quota. These later checks belong to the domain and authorization layers.
Consider {"sku":"A","quantity":true}. Python treats booleans as a subclass of integers, so a casual isinstance(value, int) check accepts it. A JSON-facing quantity contract should reject a boolean. The example uses exact type checks for its deliberately small contract. It is an educational validator for two fields, not an implementation of the entire JSON Schema standard.
A worked validation table
Evaluate four requests in order. Quantity 3 for SKU A passes structure. Quantity 0 fails the lower bound. Quantity "3" fails type validation, even though converting it seems easy. Quantity 3 accompanied by discount_code fails the closed-field policy. If the adapter silently coerces or drops fields, the caller loses the ability to know which request the server actually executed.
Coercion can be appropriate at a user interface, where a person sees the interpreted value. It is riskier inside an unattended tool boundary. When normalization is part of the contract, state it precisely and return the normalized representation. For example, trim surrounding SKU whitespace only if identifiers cannot legitimately contain it. Never let convenience rules change identifiers invisibly.
Optionality has consequences
Optional fields need clear absence semantics. An omitted expires_at might mean use a documented default, inherit a policy, or never expire. Those behaviors are materially different. null can mean something else again, such as explicitly clearing an existing value. Describe omission, nullability, and defaults independently. A schema default can document an expected value without guaranteeing that every validator inserts it.
Evolving a contract requires the same care. Adding an optional output field is often easier for tolerant clients than changing the meaning of an existing field. Adding an enum value can still break exhaustive client code. Version a contract when interpretation changes, and keep fixtures representing old consumers. Compatibility is a relationship between producers and consumers, not a property inferred from a version number alone.
Errors should help repair one request
Return a stable error category and a field path, such as quantity: expected integer from 1 through 20. Avoid reflecting arbitrary untrusted text or entire payloads into logs. For a failed request, validate enough to give useful feedback while bounding work on huge nested objects. Limit payload size before expensive validation, then apply structural checks, then enforce domain rules and authorization at the appropriate boundary.
The lesson's validator returns a list so callers can repair several independent problems together. A production JSON Schema library provides broader correctness than expanding this hand-written subset indefinitely. Keep the small function as a learning tool and as an independent oracle for a narrow fixture set. Its value is showing where each constraint enters the decision, including the easy-to-miss boolean case.
Work through the code
The input is a dictionary matching a closed two-field contract. The function collects structural errors; it does not check stock, credentials, or the complete JSON Schema vocabulary. Change the maximum and update both documentation and fixtures together.
def validate(payload):
if type(payload) is not dict:
return ["payload: expected object"]
errors = []
if set(payload) != {"sku", "quantity"}:
errors.append("fields: expected sku and quantity only")
if type(payload.get("sku")) is not str or not payload["sku"]:
errors.append("sku: expected nonempty string")
quantity = payload.get("quantity")
if type(quantity) is not int or not 1 <= quantity <= 20:
errors.append("quantity: expected integer from 1 through 20")
return errors
cases = [
{"sku": "A", "quantity": 3},
{"sku": "A", "quantity": True},
{"sku": "A", "quantity": 0},
]
for case in cases:
print(validate(case))
[] ['quantity: expected integer from 1 through 20'] ['quantity: expected integer from 1 through 20']
Run Python snippets locally with the prerequisites named above. The module coding lab runs directly in your browser.
Pause and reason
An update tool accepts an optional nullable manager_id. Define three distinct behaviors for omission, null, and a nonempty ID, then give one compatibility risk.
Check your understanding
A quantity field must be a JSON integer from 1 through 20. Why is `isinstance(True, int)` insufficient?
Your notes
Explain the mechanism in your own words. Add a failure you want to test.