fewwords
Blog · 2026-04-24

Why YAML. An engineering argument against DSLs, GUIs, and AI-writes-AI.

I tried to build fewwords with a proper DSL. Every platform engineer I showed it to asked the same question. I do not have a rebuttal anymore. Here is why.

AV
Abhishek Vyas · Founder, fewwords
2026-04-24 · 7 min read

Early on I tried to build fewwords with a proper DSL. Small typed language. Recursive-descent parser I could fit on a whiteboard. Nice error messages. Python-shaped enough that every engineer I showed it to understood the syntax in 30 seconds.

Every platform engineer I showed it to asked the same question: why isn’t this just YAML?

I was going to write a polemic about how YAML is the wrong tool for trajectory contracts. I don’t have one anymore. Here’s why.

What the DSL would have been good at

A DSL gives you the ability to check the program before it runs. You can enforce types at the syntactic level. You can report errors at a column index with a caret and a little diagram. You can evolve the language cleanly because versioning is explicit.

These are real advantages. If your user is a compiler engineer writing agent contracts as their primary job, a DSL wins. That was not the user.

What I learned talking to the user

The user is a platform engineer or a staff engineer who owns their team’s agent infra. They’re not spending their week authoring contracts. They’re spending their week reviewing agent PRs from junior engineers who added a tool or changed a prompt. A contract, for them, is an artefact they want to read inside a pull request.

This single constraint killed the DSL.

Why YAML wins at the review surface

Three reasons.

GitHub already knows how to render YAML. The moment your contract is a YAML file, every PR that touches it shows up as a clean diff in the standard review UI, with syntax highlighting. No plugin. No special viewer. The DSL would have needed a bespoke rendering step or a plugin that 90% of reviewers don’t install.

A contract is mostly declarative. Almost everything you want to say about an agent trajectory is declarative — a list of banned tools, a set of required-prior-work edges, a schema for an output. The 10% that isn’t declarative (a Python expression on tool args, an LTL formula) is best expressed as a raw string embedded in the declarative structure. YAML handles this natively.

The syntax cost is zero. Every engineer in the target persona has written a Kubernetes manifest. YAML’s sharp edges (Norway, dates, scalars vs. flow) are a cost, but it’s a cost already amortised across their career. Introducing a second syntax for a single product is a real tax.

Why not a GUI

I considered a GUI. “Point and click to build your contract.” It looked great in screenshots.

It failed the first smell test: a contract has to live in version control, has to diff cleanly, has to be reviewable asynchronously by a reviewer who is not in your GUI. A GUI-authored contract that serialises to YAML is just YAML with worse authorship ergonomics for anyone who isn’t the author. A GUI is plausibly a bolt-on later. As the primary authoring surface it fails the team workflow.

Why not let an LLM write the contract

Everybody asks this. The pitch writes itself: “Paste your trace, our LLM generates the contract.” I keep declining to ship it.

Not because it doesn’t work — it kind of does. Because the people I’m selling to do not want an LLM-authored contract in the hot path of their agent. The whole point of a trajectory contract is that it’s a deterministic, auditable artefact written by a human who took responsibility. “An LLM wrote this rule” re-introduces the non-determinism the contract was designed to remove.

There is a middle ground. trajeval init suggests a starter contract from observed traces. The author edits from there. The suggestion is scaffold, not law. That’s in the tool today.

The sharp edges

YAML has real sharp edges and pretending otherwise is insulting. The most common one: accidental string-to-type coercion. allowed_tools: [no] becomes [False] because YAML 1.1 thinks no is a boolean. fewwords’s loader refuses YAML 1.1 and validates every loaded value against a Pydantic schema, which catches this class of bug on day one. The error messages are deliberately specific.

The second sharp edge is anchors and merge keys. We don’t use them. Contracts are flat. If you find yourself wanting YAML anchors in a contract, that’s a signal to split the contract into two contracts that compose at the Gate.

What the engine does

The YAML is the UX. The engine is not the YAML. Underneath, a contract compiles to three kinds of checks:

  • A Python expression, evaluated in a sandboxed namespace against the current node, for ad-hoc argument or output predicates.
  • A JSON Schema validator, for structural checks on tool inputs and outputs.
  • A Büchi automaton, compiled from the ordering properties, for linear-temporal-logic checks over the trace.

The author doesn’t see these. They write YAML. The engine compiles.

The best UX for an artefact that lives in a repo, is reviewed in a PR, and is owned by a platform engineer who isn’t full-time writing contracts, is YAML. The engineering work is in the engine, not the syntax.

We pay the YAML tax so the author doesn’t pay the DSL tax, because the author’s time is worth more than ours.


Got a production trace that deserves this treatment? Send it. I answer every email at abhishekvyas02032001@gmail.com. Free dossier for the first five teams.

More from the blog