Most structured-output tutorials show you a single clean example and stop, leaving you to discover the hard parts in production. This is the opposite: an ordered build sequence you can follow start to finish, where each step exists because skipping it causes a specific, predictable failure later. Do them in order. The order is not arbitrary.
The goal is a pipeline that takes some input, sends it to a model, and reliably returns validated, typed data your application can trust—even when the model occasionally misbehaves. By the end you will have a structure you can adapt to extraction, classification, or tool-calling tasks without rethinking the fundamentals each time.
We assume you already understand the basic vocabulary. If terms like JSON mode and schema are unfamiliar, start with the beginner's guide and come back. This piece is about doing, not defining.
Step 1: Define the Schema First
Before you write a single line of model-calling code, write the schema. The schema is the contract every other step depends on, so it comes first.
Decide exactly what fields you need, what type each is, and which are required versus optional. Resist the urge to ask for "everything the model knows"—each field you add is another thing that can go wrong and another token you pay for. Ask for the minimum your application actually uses.
Make the Schema the Single Source
Write the schema in a form your code can use directly—a Pydantic model in Python, a Zod schema in TypeScript, or raw JSON Schema. The point is that the same definition drives both the instruction you send the model and the validation you run on the response. If you hand-write two separate copies, they will drift apart and produce confusing bugs.
Step 2: Write Field Descriptions, Not Just Types
A schema that says category: string tells the model the type but not the meaning. Add a description to each field explaining what belongs there and how to handle edge cases.
For example, a category field might describe: "One of: billing, technical, account. Use technical for anything about product functionality. If unclear, use account." Those descriptions ride along into the model's context and dramatically improve accuracy. They are prompt engineering hiding inside your schema, and skipping them is the most common reason "correct" schemas still produce wrong values.
Step 3: Turn On the Strongest Enforcement Available
Now wire up the model call. Check what your provider offers and use the strictest mode it supports:
- If it offers strict schema enforcement (structured outputs), use that and pass your schema directly.
- If it only offers JSON mode, use that and lean harder on validation in step 5.
- If you are using an open model, reach for a constrained-decoding library that accepts your schema.
The tooling survey covers which providers and libraries support which level, but the principle is constant: take the strongest guarantee you can get, then verify anyway.
Step 4: Parse Defensively
Even with enforcement, write your parsing code as though the response might be malformed. Wrap the parse in error handling rather than assuming success.
try {
data = JSON.parse(response)
} catch (e) {
// route to the retry path, do not crash
}This costs almost nothing and turns a class of crashes into recoverable events. The first time enforcement fails—and over enough volume it will—this is what keeps your service running.
Step 5: Validate Against the Schema and Your Rules
Parsing succeeds means you have valid JSON. It does not mean you have correct data. Run two checks now.
Structural Validation
Pass the parsed object through your schema validator. This confirms the fields exist and have the right types. With strict enforcement upstream this rarely fails, but it is cheap insurance and it catches the cases where enforcement was unavailable.
Semantic Validation
This is where your business logic lives. Is the date in a plausible range? Is the dollar amount positive? Is the chosen category actually one your downstream system handles? Schema enforcement cannot know these rules—only your code can. The common mistakes article shows how many production bugs trace back to skipping this exact step.
Step 6: Build the Retry Loop
When validation fails, you need a recovery path, not a thrown exception that bubbles up to the user. Build a loop with a bounded number of attempts.
On failure, re-call the model with the original request plus a note describing what was wrong: "The previous response set discount to 150, but it must be between 0 and 100. Try again." Feeding the specific error back gives the model what it needs to self-correct, and most validation failures resolve on the second attempt.
Define a Final Fallback
Decide what happens when retries are exhausted. Options include falling back to a more capable model, returning a safe default, or routing the case to a human queue. The wrong answer is to crash or to silently drop the record. Pick a fallback deliberately.
Step 7: Log and Measure
Once the pipeline runs, instrument it. Log every validation failure and every retry. Over a week these logs tell you which fields the model struggles with, which descriptions need rewriting, and whether a cheaper model would suffice. Structured output quality is a thing you tune over time, and you cannot tune what you do not measure. The best practices guide expands on what to watch.
Putting the Sequence Together
The seven steps form a loop you can wrap any task in: schema, descriptions, enforcement, defensive parse, validation, retries, measurement. Built once, this scaffold handles extraction jobs, classification jobs, and tool-calling jobs with only the schema changing between them.
The discipline that separates a demo from a dependable service is doing all seven, every time, rather than the two or three that feel sufficient when the model is behaving. The steps you are tempted to skip are precisely the ones that absorb the model's bad days.
Frequently Asked Questions
Can I skip validation if I use strict schema enforcement?
You can skip structural validation in low-stakes cases, but never skip semantic validation. Enforcement guarantees the JSON has the right fields and types; it cannot know your business rules. A value can be a valid number and still be wrong for your domain, and only your own validation catches that.
How many retries should I allow before giving up?
Two or three is a sensible default. Most fixable failures resolve on the first retry once you feed the error back to the model. Beyond three attempts you are usually hitting a genuine limitation—the input is ambiguous or the task is too hard—and you should fall back to a stronger model or a human rather than burning tokens.
Where exactly do field descriptions go?
Inside the schema itself, attached to each field. In JSON Schema this is the description keyword; in Pydantic it is the field's description argument; in Zod it is the describe method. They travel with the schema into the model's context, which is why writing them is part of step two rather than a separate prompt-tweaking phase.
What if my provider only offers JSON mode, not schema enforcement?
You can still build the full pipeline; you simply lean harder on steps five and six. JSON mode guarantees parseable output, so your defensive parse rarely triggers, but structural validation now does real work because the provider is not enforcing shape. Pair it with strong field descriptions and a solid retry loop.
Should the schema live in my code or in the prompt?
In your code, as the single source of truth, and then derived into the prompt. Writing the schema as a typed object lets your validator and your model instruction stay in sync automatically. Pasting a hand-written schema into the prompt as a string invites the two copies to drift apart over time.
Key Takeaways
- Build in order: schema, field descriptions, enforcement, defensive parse, validation, retries, measurement—each step prevents a specific later failure.
- Define the schema first as a single source that drives both the model instruction and your validator.
- Field descriptions are prompt engineering inside the schema; they fix most "correct schema, wrong value" problems.
- Always run semantic validation for business rules that schema enforcement cannot know.
- A bounded retry loop that feeds the error back to the model resolves most failures on the second attempt; define a deliberate fallback for the rest.