A markdown file sitting between JSON, YAML, and XML — the format that won the prompt war

// tools in this post Markdown Markdown Git Git GitHub GitHub

This is part of the Prompt-as-DSL series, and it’s the post about the format war nobody announced.

Every structured prompt you write today is markdown. Headings, bullets, bold, code fences, the occasional table. That wasn’t inevitable. We had JSON. We had YAML. We had XML, which the model vendors themselves leaned on for years. Any of them could have become the way humans talk to language models.

Markdown Markdown won. And it didn’t win by accident.

The Job The Format Has To Do

Talking to a model is a strange requirement. The text has to do two jobs at once, and they usually fight each other.

It has to be readable by a human, because a person writes it, edits it, reviews it in a diff, and argues about it in a pull request. And it has to carry enough structure for a machine, because the whole point is that headings declare scope, bullets declare sets, and fences declare “this is data, not instruction” — the five primitives that make a prompt behave like code instead of a wish.

Most formats are good at exactly one of those jobs. The winner had to be decent at both, in the same file, with no second copy.

So measure the candidates against that bar.

JSON Is For Machines

JSON is a beautiful machine format. Unambiguous, parseable, every language ships a library for it. If two programs need to exchange data, JSON is a fine answer.

It is miserable to write by hand.

{
  "task": "Clean the CSV provided below",
  "rules": [
    "Drop rows where email is empty or malformed",
    "Normalize date to ISO 8601",
    "De-duplicate on email, keep most recent"
  ],
  "output": "A single CSV block, no commentary"
}

Count the syntax tax. Quotes on every key. Quotes on every string. Commas between items but not after the last one — get that wrong and the whole thing fails to parse. No comments allowed, ever, which means you can’t leave a note next to a rule explaining why it’s there. And the moment a rule contains a quote or a newline, you’re escaping characters by hand.

JSON wants to be emitted by a program and consumed by a program. A human in the middle is friction. You feel it on every line.

YAML Is Whitespace On A Tightrope

YAML fixed JSON’s readability. No braces, no quote-everything, comments allowed. It reads almost like an outline, which is why config files and CI pipelines moved to it.

The catch is that YAML makes whitespace load-bearing.

rules:
  - drop empty emails
   - normalize dates   # one extra space — silently broken

Indent by the wrong number of spaces and the meaning changes or the parse breaks. Mix a tab in with spaces and it breaks. Write a value like no or 3.10 and YAML helpfully turns it into a boolean or a number you didn’t ask for. The format has a famous list of gotchas precisely because it tries to infer your intent and sometimes guesses wrong.

For a config file you write once and lint forever, that’s a tolerable trade. For a prompt you’re editing constantly, in a chat box, in a markdown file, in a comment — a format where an invisible character changes the result is the wrong tool. You don’t want the syntax to have opinions.

XML Is Heavy

XML carries the most structure of the bunch, and the model vendors lean on it for a reason — <example>, <thinking>, <document> tags scope blocks cleanly and survive long contexts well. Tags are genuinely useful, and good prompts still use a handful of them.

But XML as the whole format is exhausting to write.

<rules>
  <rule>Drop rows where email is empty or malformed</rule>
  <rule>Normalize date to ISO 8601</rule>
</rules>

Every element opens and closes. The content-to-syntax ratio is bad — you type the tag name twice and wrap every value. For one or two scoped blocks inside a larger document, that cost is worth it. For an entire prompt, you’re spending more keystrokes on angle brackets than on instructions. Heavy structure you don’t need is just overhead.

Where Markdown Sits

Now the same prompt in markdown:

## Task
Clean the CSV provided below.

## Rules
- Drop rows where `email` is empty or malformed
- Normalize `date` to ISO 8601
- De-duplicate on `email` — keep the most recent row

## Output
A single CSV block. No commentary.

Read it. There is almost no syntax tax. A ## for a heading. A - for a list item. Backticks for a literal. That’s the entire cost, and every one of those marks is something a human would reach for anyway to make plain text legible.

That’s the trick. Markdown’s syntax is the formatting you’d already apply to make text readable to a person. The structure a machine needs and the structure a human wants are the same marks. You’re not paying a tax to satisfy the parser — the parser reads the exact thing that makes the document clear to you.

Here’s how the four stack up:

FormatHuman-readableCarries structureSyntax taxFragile?
JSONPoorYesHighYes — one comma
YAMLGoodYesLowYes — whitespace
XMLFairYes (heavy)HighForgiving
MarkdownExcellentEnoughMinimalVery forgiving

That last column is doing quiet work. Markdown degrades gracefully. Forget to close a bold tag and you get a stray asterisk, not a parse failure. Indent a bullet inconsistently and the meaning survives. A model reads slightly-broken markdown fine, the way you read a typo without stopping. None of the other three forgive you like that.

Why This Wasn’t An Accident

It’s tempting to call this a fluke — markdown happened to be lying around, so it got used. I don’t buy it.

Markdown won because the constraint selected for it. The thing we needed was a format a human could write fluently and a machine could act on, with the lowest possible friction in between, that didn’t punish small mistakes. Run every candidate through that filter and markdown is the one that comes out the other side. JSON loses on readability. YAML loses on fragility. XML loses on weight. Markdown loses on none of them — it just isn’t the best at any single axis, which is exactly why it wins the combined job.

There’s a deeper reason too. Markdown was designed from the start to be read as plain text even when nothing renders it. John Gruber’s original goal was a format that looked fine in a raw editor. That design goal — readable with zero tooling — is the same property that makes it perfect for a model, which reads the raw characters and never sees a rendered page. The format was accidentally built for an audience that didn’t exist yet.

And once a critical mass of prompts, CLAUDE.md files, system prompts, and MCP instructions were all markdown, the network effect locked it in. The format you can paste anywhere — a chat box, a code comment, a .md file, a GitHub issue — and have it stay legible is the format that spreads. Markdown goes everywhere unchanged. That portability was the final nail.

The Takeaway

The format that talks to machines didn’t have to be friendly to humans. It happened to be, because the job demanded both at once and only one candidate cleared the bar on both.

Write your prompts in markdown. Not because it’s trendy — because it’s the format that costs you the least to write, the least to edit, and the least to get wrong, while still carrying the structure the model needs. Every other option makes you choose between the human and the machine. Markdown refuses to make you choose. That’s the whole reason it won.

If you’d rather hand the prompts, the agents, and the systems behind them to people who think about format this carefully, tell us what you’re building.