Structured outputs as application contracts An AI engineer at a fintech startup replaced regex parsing of LLM chat output with JSON Schema-constrained structured outputs, treating the schema as an application contract that the model must satisfy or explicitly refuse. The engineer reports the loan-approval pipeline's error rate fell from 12% to under 1% after the switch, with refusals handled as a fallback path, and describes versioning schemas to add fields like "explanation" without breaking older clients. So there I was, an AI engineer at a fintech startup, trying to turn a GPT‑4 chat into a credit‑score suggestion engine. The user typed “I just got a promotion, how much should I loan?” and the model spat back a paragraph that looked like poetry: “Congrats Maybe $5‑10k could work, but keep your debt‑to‑income ratio low…” I tried to yank the numbers out with a regex like /\$ \d+ - \d+ k/ . Spoiler: it broke the moment someone said “$5‑10 k” with a thin space or used “5‑10k” without the dollar sign. One mis‑typed hyphen and my downstream service crashed, mis‑classifying a safe applicant as risky. It felt like trying to catch a greased pig with a colander. That’s when I discovered the power of structured outputs as application contracts. Instead of hoping the model “gives me what I need”, I told it exactly what shape the response must have – a JSON object that matches a JSON Schema and a typed TypeScript interface. The prompt became: { "loan range": { "min": int, "max": int }, "confidence": "high" | "medium" | "low" } Now the model can’t accidentally sprinkle a poem in there; it either obeys or says “I’m sorry, I can’t comply”. That refusal is a feature: my code checks if response.refusal … and falls back to a safe default. Why is this better than regex? Regex is a brittle detective that looks for patterns in free text. Anything outside the pattern – extra whitespace, emojis, a different phrasing – sends it into a dead end. A schema validator, on the other hand, parses a proper data structure and instantly tells you which fields are missing or of the wrong type. It’s like checking a passport instead of eyeballing a face. In my edutech side‑project, we needed a list of quiz questions with “question”, “options”, and “answer”. By versioning the schema "$schema": "http://json-schema.org/draft-07/schema ", "title": "QuizV2" , we could roll out a new field “explanation” without breaking older clients. Downstream safety checks – e.g., “no option string longer than 200 chars” – are baked into the schema, so the LLM can’t hand us a monster answer that blows up the UI. A quick case study: after switching to structured outputs and enabling refusals, our fintech loan‑approval pipeline’s error rate dropped from 12% to under 1%. The only time we got a refusal was when the user asked for advice on illegal activity, and we gracefully returned a polite error message. Bottom line: treat the LLM like a junior dev who follows a contract, not a poet who vibes with your mood. Define a JSON Schema, generate typed interfaces, validate, version, and let refusals be your safety net. Got a funny regex story or a schema win? Drop a comment – I’d love to hear how you’re taming your LLMs If you are someone who loves to know the technical work and architecture design I have shared more details based on my experience on this here: https://github.com/SalmonJoy/My guide for building AI systems/blob/main/Structured outputs as application contracts.md https://github.com/SalmonJoy/My guide for building AI systems/blob/main/Structured outputs as application contracts.md