Deterministic Core, Expressive Edge
A lot of engineers I talk to don’t like AI. Not because it can’t write code — it can. Not because it isn’t useful — it is. What bothers them is deeper than that: it’s non-deterministic. The same input doesn’t always produce the same output, the behavior is fuzzy, the edge cases don’t line up. For engineers who grew up worshipping reproducibility — and for a lot of them, that worship is the whole point — this feels like the system is breaking the rules.
I’m on the other side of it, and I have been the whole time. I picked JavaScript as my primary language on purpose. I liked that it was interpreted, loosely typed, forgiving — a language that let you move first and tighten later. The rigor crowd rolled their eyes at that for years. Now the script has flipped: the engineers who were most at home in strict, deterministic systems are the ones most uncomfortable with this shift, and the people who were always comfortable with a little mess are suddenly in their element.
That’s worth sitting with, because it points at what’s actually happening. This isn’t determinism being replaced. It’s a new layer being added on top of it, and for the first time, we have a clean way to wire the two together.
The Old Stack Was Deterministic by Necessity
For most of software history, we didn’t have a choice. If you wanted a system to behave correctly, you encoded every rule up front — every branch, every condition, every edge case. The machine didn’t understand context, it understood instructions. So we wrote instructions for everything.
That’s the world our tools came from: APIs with fixed schemas, SDKs with typed contracts, CLIs with exact flags, databases with rigid shapes, workflows expressed in YAML. Each one is a promise that the same call produces the same result, and that predictability is what made modern software composable in the first place. You could build on top of someone else’s system because you knew what it would do.
That layer isn’t going anywhere. You don’t want your payment processor to be “mostly correct.” You don’t want your auth system to interpret the user’s vibe. The deterministic core is the part that has to keep working. The shift isn’t that it’s being replaced — it’s that we’re finally able to put something on top of it.
The Expressive Layer Is the New Part
For the first time, we have tools that can take messy, open-ended input — natural language, images, partial context — and do something useful with it. That’s not a different API with slightly different semantics. That’s a new kind of primitive. LLMs are the engine. MCPs let those engines plug into real systems with defined contracts. Skills package up repeatable capabilities the model can pull from. Together they form a layer that didn’t exist before — one that deals in intent rather than syntax.
It’s worth being precise about what this layer is good at — interpreting ambiguous input, choosing between reasonable paths, generating structure from unstructured context, routing, filtering, summarizing, extracting — and what it’s bad at: guaranteeing exactly-once behavior, holding a hard invariant, doing anything you want to be reproducible down to the byte. Which is why it doesn’t replace the deterministic core. It sits above it. The deterministic layer ensures correctness. The expressive layer ensures relevance.
The Gap in the Middle
There’s a real problem where those two layers meet. The expressive layer produces language; the deterministic layer expects arguments. If you hand a raw LLM response to a typed API, you’ll be parsing your way out of a JSON-looking string, handling malformed quotes, retrying when a required field goes missing, and writing defensive code for every value that might show up as null or "null" or "unknown" or the literal word unknown.
This is the real complaint engineers have when they say AI feels sloppy. It’s not that the model is wrong — it’s that the seam between the expressive layer and the deterministic layer is leaky. Close the seam and most of the sloppiness goes away.
Structured Outputs Are the Bridge
Anthropic’s API supports structured outputs , and once you start using them it’s hard to build anything serious without them. The shape is simple: you hand the model a JSON schema, and the response is guaranteed to conform to it. No malformed JSON, no missing fields, no post-hoc parsing. The constraint is enforced during generation, not patched up after.
That’s a small API feature with a big architectural consequence. It means the expressive layer can produce output that’s safe to feed directly into the deterministic layer. The fuzzy thing on top and the strict thing on the bottom finally speak the same language at the boundary. If I want to turn a sentence like “John Smith wants an Enterprise demo Tuesday at 2pm, his email is john@example.com” into a row in my CRM, I can define a ContactInfo schema and get back exactly those fields, typed and validated, ready for the next step. No retry loop, no schema drift, no if typeof x === 'string' branches guarding against the model’s mood that day. The input stays expressive. The output stays deterministic.
This is also why tool use in agent systems works as well as it does now. A tool call is a structured output — the model picks the tool and fills in the arguments, but the shape of the call is enforced. The expressive layer decides what to do; the deterministic layer defines what’s allowed.
Drawing the Line
If you take this stack seriously, a lot of system design collapses into one question: where does determinism stop and expressiveness start? Core business logic, transactions, permissions, invariants — those stay on the deterministic side. That’s not AI skepticism, that’s good engineering. You don’t want those behaviors to “mostly” happen. Interpretation, routing, UI generation, extraction, classification — those move to the expressive side. Trying to encode those deterministically is what gave us the static, brittle systems we’re all tired of maintaining: forms with fifty fields because you had to anticipate every case, filters that never quite match what the user meant, CODEOWNERS files that went stale the week they shipped.
The structured-output boundary is what lets the two sides stay cleanly separated. Without it, the expressive layer bleeds into the deterministic core and everything gets mushy. With it, you have a real contract at the seam. The engineers who struggle with AI aren’t wrong to worry about non-determinism — they’re just placing it in the wrong layer. You don’t let it float free through the system. You put it where it belongs, shape its output with a schema, and hand off to code that behaves the way code is supposed to.
What This Stack Makes Possible
Once you have a clean seam between the two layers, the kind of software you can build starts to change. You stop hardcoding forms and start generating them from intent. You stop writing every branch of a workflow and start describing the goal. You stop assigning static ownership and start computing it when it matters. You stop shipping features designed for the average user and start generating an interface for the person in front of you. None of that is possible with a purely deterministic stack, and none of it is trustworthy with a purely expressive one.
It’s the combination that’s new. Deterministic systems gave us control. Expressive systems give us adaptability. Structured outputs are what let those two properties coexist in the same program without tearing each other apart. Core stays strict. Edges get expressive. The schema is where they meet.