skip to content

When should an LLM return Markdown instead of JSON, and why not mix both?

level: juniorimportance: should knowfreq 52%

answer

  1. who reads the output?
  2. humans read structure, parsers read types
  3. prose around JSON breaks the parse
  4. make the summary a field
  5. one format per response

basics

~20 s

Ask for Markdown when a human reads the output directly, and JSON when code consumes it. Mixing them breaks parsing, since prose around an object stops the whole response from parsing. Put human-facing text inside a JSON field instead.

solid answer

~50 s

Pick the format by consumer. A human reading a chat reply or a generated report wants Markdown — headings, lists, tables, emphasis — because those carry structure for a reader. A program wants JSON (or another machine format) because it needs unambiguous field boundaries and types, and Markdown gives it neither: a heading is not a key, and a table cell containing a comma or a pipe is a parsing accident waiting to happen. The trouble comes from asking for both at once. "Return the JSON object and then a short summary" produces a response your parser cannot load, because the surrounding prose is not JSON. If you need both, make the prose a **field**: `{"summary": "...", "items": [...]}`. Your code parses the object, then renders `summary` as Markdown for the human. One machine-readable envelope, human-readable content inside it.

go deeper

for a junior

Decide by consumer: Markdown for a person, JSON for code. Never ask for both in one response, and put any human-readable sentence inside a JSON field instead of around the object.

for a middle

Explain why a mixed response fails — a JSON document is the entire string, so any preamble or trailing prose makes it unparseable — and why brace-matching extraction is a fragile substitute for asking for one format.

for a senior

Show that you design the envelope: a machine-readable container with human text carried as fields, so the UI layer renders and the pipeline parses without a text-splitting heuristic anyone has to maintain.

for a principal

Own the interface convention across teams — one response format per endpoint, human copy as content rather than wrapper — so downstream consumers, logging and evaluation all read the same shape instead of each inventing an extraction rule.

## Two different jobs Output format is a question about the consumer, not about taste. Markdown exists to convey structure to a *reader*: headings group, lists enumerate, bold emphasizes, and a table lays values out visually. JSON exists to convey structure to a *parser*: keys are unambiguous, values are typed, nesting is explicit, and the grammar is closed. Each is bad at the other's job. Markdown has no types, no required fields, no way to distinguish a missing value from an empty one, and no escaping story for the delimiter characters you rely on. JSON is miserable to read and worse to skim. So the decision rule is short. Output that a person reads directly — a chat answer, a drafted email, a report section, an explanation — should be Markdown. Output that a program reads — anything you will index, store in a column, branch on, or feed to another service — should be JSON or another machine format. ## Why the mixed response bites "Return the JSON and then explain your reasoning" is one of the most common first-week mistakes. The response looks fine on screen, and `json.loads()` on it raises immediately, because a JSON document is the *whole* string. The same applies to the ```json fence that models like to add: those backticks are three characters your parser has no theory for. The usual reactions are both wrong. Stripping the prose with a regex or hunting for the first `{` and the last `}` builds a fragile ad-hoc parser that breaks the first time a string value contains a brace. Asking the model more insistently not to add commentary reduces the rate but does not eliminate it, because an instruction is a bias rather than a constraint. ## The right shape: prose inside the envelope Make the machine format the outer container and put every human-facing string inside a field. ``` {"summary": "Three dishes contain peanuts.", "items": [ ... ]} ``` Now one parse gets you everything. Your code reads `items`, and your UI renders `summary` — as Markdown, if you like, since Markdown inside a JSON string is perfectly legal and just needs the usual escaping, which the serializer handles for you. This also lets you evolve: adding a `confidence` or `warnings` field later is a schema change, not a re-derivation of some text-splitting heuristic. ## Markdown as the requested format When the human *is* the consumer, ask for Markdown specifically rather than leaving it implicit, and say what structure you want: "Reply in Markdown with a level-2 heading per section and a bulleted list of findings under each." Models honour that well, and it saves you from post-processing walls of text. Be aware that Markdown itself needs care in one direction: if the model is quoting code or user content, ask it to use fenced blocks, or your renderer will interpret stray characters as formatting. ## What about Markdown tables for data? People reach for a Markdown table when the data is tabular and they want it readable. It is fine when a person reads the table. It is a poor transport format: pipes inside cell values break the row, alignment rows are noise your parser must skip, empty cells are indistinguishable from missing columns, and everything is a string. If you need both a readable table and the underlying data, generate JSON and render the table yourself — deterministic rendering is free, and unreliable parsing is not. ## A note on other machine formats JSON is the default because schema tooling, structured-output modes and validators all speak it. Alternatives have narrow niches: a single bare enum value or number is simpler as plain text when that is genuinely all you need; XML-style tags are sometimes easier for a model to keep balanced across very long outputs and are trivially extractable; CSV is compact for wide uniform rows but has the same escaping hazards as Markdown tables. Whatever you pick, the rule holds — one format per response, chosen by who reads it, with human text carried as content rather than wrapped around the payload.

  • You need both a machine-readable result and a sentence explaining it. What do you ask for?
    A single JSON object with the explanation as a string field, for example a `summary` key alongside the structured fields. Your code does one parse and hands the summary to the UI, which can render it as Markdown since Markdown inside a JSON string is just text. Nothing has to be split out of prose, and adding fields later is a schema change rather than a parser rewrite.
  • Is a Markdown table ever the right format for data your code consumes?
    Rarely. A pipe character inside a cell breaks the row, empty cells are indistinguishable from missing columns, everything arrives as an untyped string, and alignment rows are noise. If a human needs a table, return JSON and render the table yourself — deterministic rendering costs nothing, while parsing a generated table is a permanent source of bugs.
  • Why not just extract the JSON with a regex when the model adds commentary?
    Because you have written an ad-hoc parser with no grammar. Finding the first brace and the last brace breaks the moment a string value contains one, and it will fail silently by extracting a truncated object. It is acceptable as a narrow tolerance for a stray code fence, not as the strategy for handling arbitrary surrounding prose.

Markdown is the printed report and JSON is the database row. You do not staple a paragraph to the front of a database row and expect the importer to cope.

saying these in an interview costs you the question

  • Asking for a JSON object plus a trailing explanation
  • Parsing Markdown tables as if they were structured data
  • Assuming a program can read Markdown formatting as fields
  • Splicing JSON out of prose with brace-matching heuristics
  • Treating a ```json fence as part of valid JSON

context