Yeda AI Tips · #034

Stop Asking Nicely For JSON — Define a Schema Instead

Ask an AI for JSON, and you're gambling. Ask for a field nested two levels deep, and it might flatten it. Ask for exactly three keys, and it might add a fourth "helpful" one you never requested. You end up writing defensive parsing code to catch what the model didn't quite give you. None of that is necessary once you stop describing the shape in English and start defining it as data.

The problem with prompting for structure

"Please respond in valid JSON with fields name, email, and orders" reads like a spec, but the model treats it like any other instruction: a strong suggestion, not a contract. In practice this breaks in predictable ways — a nested field gets flattened or renamed, an unrequested description or note key shows up because the model thought it was being helpful, or a field the model was unsure about gets omitted rather than left null. Each of these is a runtime surprise your code has to detect and handle after the fact.

The root cause is that natural-language format instructions are just text tokens mixed in with everything else in the prompt. The model has no mechanism forcing it to obey them — it's pattern-matching on "this looks like a JSON-shaped request," not executing a schema.

Why schema mode actually works

Structured outputs flip the mechanism. Instead of describing the shape in words, you hand the model call a real schema object — a Zod schema, a Pydantic model, or a raw JSON Schema document — through a parameter built for exactly this purpose. The model provider then constrains generation itself, or validates and retries, so the response conforms to that schema before your application code ever sees it. You're not hoping the model understood your description; you're handing it a contract it's mechanically bound to.

This differs from format instructions in three concrete ways:

  1. Types are enforced, not suggested. A field typed as a number can't come back as the string "42".
  2. Required fields can't silently disappear. The enforcement point is the generation/validation step, not a hopeful regex on your end.
  3. Field-level guidance moves into the schema. Instructions like "under 200 characters, general audience" become a description on the schema field itself instead of prompt boilerplate.

How to do it

Most modern AI SDKs expose a "generate structured output" or "generate object" call that takes your normal prompt plus a schema parameter, separate from the prompt text:

schema = {
  name: string,
  email: string,
  orders: [{ id: string, total: number }]
}

result = model.generateObject({
  prompt: "Extract the customer record from this email...",
  schema: schema,
})

# result.object is validated — no parsing, no defensive code

The pattern is the same across providers and libraries: define the schema once, pass it alongside the prompt, and read back a validated object instead of a raw string. Many SDKs also expose a strict mode that hard-constrains generation to the schema rather than validating-and-retrying after the fact — worth turning on if your library supports it.

Where this pays off

Power tricks

Resources

Building an AI feature? Yeda AI designs, audits, and ships production LLM systems.

Talk to us · Read the blog