The moment you want a model’s output consumed by code rather than read by a person, you need it in a fixed shape. This is where a lot of otherwise working prototypes fall over in production, because the failure rate that is tolerable when a human is watching is not tolerable in a batch job.
Ask precisely
Vague requests produce varied output. “Return JSON” gets you JSON wrapped in an explanation, or in a code fence, or with a preamble.
Give the exact schema with field names and types. State that the response must be the object alone, with no surrounding text and no code fence. Provide one example of a correct response.
That combination fixes most of the problem before you write any handling code.
Use the constrained mode if there is one
Most current model interfaces offer a structured output or tool-calling mode where you supply a schema and the output is constrained to match it. Where this exists, use it. It converts a prompting problem into an interface problem and removes an entire class of failure.
This is the single highest-value thing in this post. Check whether your model provider or local runtime supports it before building parsing workarounds.
Validate, always
Even with a constrained mode, validate against the schema on receipt. Not because the mode is unreliable, but because validation is where you catch the interesting failures: correct shape, wrong content. An enum value outside the allowed set. A date that parsed but is in 1970. A number where you expected a positive integer.
Schema validation is cheap and it is the boundary between the non-deterministic part of your system and the deterministic part. Put it there explicitly.
Retry once, with the error
When validation fails, send the output back with the specific validation error and ask for a correction. This recovers most failures.
Bound it. One retry, maybe two. A model that has failed twice on the same input is not going to succeed on the fifth attempt, and an unbounded retry loop is how a batch job becomes an incident. Fail loudly, log the input, move on.
Design the schema for the model, not for your database
Flat structures work better than deeply nested ones. Explicit enums work better than free strings. Field names that describe their content in plain language work better than abbreviations.
If your database wants a different shape, transform it afterwards. Optimising the model-facing schema for the model and converting to storage shape in code is easier than fighting a nested structure through a prompt.
Include an explicit uncertainty path
If a field might not be determinable from the input, give the schema a way to say so: a null, an “unknown” enum value, a confidence field.
Without an escape route, a model asked for a value it cannot determine will produce one. With it, you get an honest signal you can route to a human. This one design decision does more for output quality than most prompt engineering.
What good looks like
A schema, a constrained generation mode where available, validation on receipt, one bounded retry with the error, an explicit unknown path, and logging of every failure with its input.
That is perhaps thirty lines of code around the model call, and it is the difference between something that works when you are watching and something that runs on a schedule while you are asleep.