Andrew Mercer

Data Formats Guide: Markdown, YAML, JSON & TOML

A reference for the four text-based formats you'll run into constantly in documentation, config, and tooling — what each is for, how to write it, and where it tends to bite you.


Markdown

What It's For

Markdown is a lightweight markup language for writing formatted text using plain, readable syntax. It compiles to HTML and is the default format for READMEs, documentation sites, GitHub/GitLab issues and PRs, static site generators, and note-taking tools (Obsidian, Notion imports, etc.).

There's no single official spec owned by one body — the practical standard most tooling follows today is CommonMark, with GitHub Flavored Markdown (GFM) layered on top for things like tables and task lists.

Core Syntax

Headings

# H1
## H2
### H3

Emphasis

*italic* or _italic_
**bold** or __bold__
***bold italic***
~~strikethrough~~

Lists

- Unordered item
- Another item
  - Nested item

1. Ordered item
2. Another item
[link text](https://example.com)
![alt text](https://example.com/image.png)

Code

Inline `code` uses backticks.

```python
def hello():
    print("fenced code block, with optional language for syntax highlighting")
```

Blockquotes

> A quoted line.
> Continues here.

Horizontal rule

---

GitHub Flavored Markdown extras

Tables:

| Name    | Role      |
|---------|-----------|
| Andrew  | Platform  |
| Alice   | SRE       |

Task lists:

- [x] Done
- [ ] Not done

Autolinks and strikethrough work as shown above; GFM also auto-links bare URLs and user/repo#123 references on GitHub itself.

Gotchas

  • Flavors differ. CommonMark, GFM, and things like MDX or Pandoc's Markdown all extend the base spec differently — a table or footnote that renders on GitHub may not render on a plain CommonMark parser.
  • Blank lines matter. A list immediately following a paragraph with no blank line between them can fail to render as a list in some parsers.
  • Line breaks require two trailing spaces (or a <br>) — a single newline in source is usually collapsed into a space in the output.
  • Escaping: use \ before literal *, _, `, etc. when you don't want them interpreted (\*not italic\*).
  • Raw HTML is allowed in most Markdown flavors and passes through untouched — useful, but a common source of "why didn't my markdown render" when tags are unclosed.

Tools

  • CommonMark spec/reference — the closest thing to a canonical spec
  • markdownlint — CLI/editor linter for consistent style
  • Pandoc — converts Markdown to/from virtually every document format (docx, PDF, HTML, LaTeX)

YAML

What It's For

YAML ("YAML Ain't Markup Language") is a human-readable data serialization format used for config files, CI/CD pipelines (GitLab CI, GitHub Actions), Kubernetes manifests, Ansible playbooks, and Docker Compose. It's a superset of JSON — any valid JSON is technically valid YAML.

The official spec lives at yaml.org.

Core Syntax

Whitespace matters — indentation defines structure (spaces only, never tabs). Comments start with #.

Scalars:

name: Andrew
role: Platform Engineer
active: true
count: 42
ratio: 3.14
nothing: null      # or ~, or leave blank

Maps nest via indentation:

server:
  host: localhost
  port: 8080
  tls:
    enabled: true

Sequences:

tools:
  - terraform
  - kubernetes
  - elasticsearch

Or inline (flow style, JSON-like): tools: [terraform, kubernetes, elasticsearch]

Multi-line strings:

literal: |
  Preserves
  line breaks exactly.
folded: >
  Folds
  newlines into spaces.

|-/>- strip the trailing newline; |+/>+ keep trailing blank lines.

Quoting: unquoted, single-quoted ('...'), double-quoted ("...", supports escapes). Quote anything that could be misread as another type — "yes", "no", "123", "null".

Data Types

Tag Meaning
!!seq Sequence (list)
!!map Map (dictionary)
!!str String
!!int Integer
!!float Floating-point decimal
!!null Null
!!binary Binary data (base64)
!!omap Ordered map
!!set Unordered set

Force a type without quotes: version: !!str 13 (treats 13 as a string, not an int).

Custom Tags

%TAG ! tag:hostdata:your_tag:
---
record: !YOUR_TAG
  id: 1001
  region: us-east

A tag directive at the top of a document remaps the ! handle to a custom URI, letting tools disambiguate semantic types. Plain parsers ignore tags they don't understand.

Anchors and Aliases (DRY YAML)

defaults: &defaults
  adapter: postgres
  host: localhost

development:
  <<: *defaults
  database: dev_db

test:
  <<: *defaults
  database: test_db
  • &anchor_name defines an anchor
  • *anchor_name references it
  • <<: merges an anchored map into the current map

Common in Docker Compose and .gitlab-ci.yml for shared config/job templates.

Multiple Documents in One File

---
doc: 1
---
doc: 2

--- separates documents; ... optionally marks the end of one. Kubernetes manifests commonly stack resources this way.

Gotchas

  • Tabs are illegal for indentation.
  • on, off, yes, no are booleans in YAML 1.1 but not YAML 1.2 — trips people up in GitHub Actions (on: parsed as boolean true by some tools). Quote if in doubt: "on":.
  • Norway problem: unquoted NO becomes boolean false in YAML 1.1 parsers.
  • Leading zeros can trigger octal interpretation (0755).
  • Trailing whitespace after : before a value can cause subtle parse errors.

Tools

  • yamllint.com — online linter
  • yq (mikefarah/yq)jq for YAML; CLI for reading/writing/transforming
    bash yq '.spec.replicas' deployment.yaml yq -i '.metadata.labels.env = "prod"' deployment.yaml yq -o json eval file.yaml
  • yamllint (Python package) — the standard for CI/pre-commit linting

JSON

What It's For

JSON (JavaScript Object Notation) is a strict, minimal data-interchange format. It's the backbone of REST APIs, is natively parseable in virtually every language, and underlies many other formats (Kubernetes accepts JSON as an alternative to YAML for manifests, for instance). Its strictness is the whole point — there's exactly one way to write valid JSON, which makes it ideal for machine-to-machine communication.

Spec: json.org / RFC 8259.

Core Syntax

{
  "name": "Andrew",
  "active": true,
  "count": 42,
  "ratio": 3.14,
  "nothing": null,
  "tools": ["terraform", "kubernetes", "elasticsearch"],
  "server": {
    "host": "localhost",
    "port": 8080
  }
}

Only two structures: objects ({}, unordered key-value pairs) and arrays ([], ordered lists). Only four primitive types: string (double quotes only), number (no distinction between int/float in the spec), boolean (true/false), and null.

Strict Rules (unlike YAML)

  • Double quotes only — for keys and strings. Single quotes are invalid.
  • No comments. Not in the spec, at all. (Some parsers — like jsonc variants used by VS Code — allow them as an extension, but plain JSON does not.)
  • No trailing commas["a", "b",] is invalid JSON, though many parsers are lenient here.
  • No trailing/leading whitespace requirements, but no tabs-vs-spaces issue either since indentation is cosmetic only (unlike YAML).
  • Keys must be strings — {1: "a"} is invalid; must be {"1": "a"}.

Gotchas

  • No comments means no inline documentation — a common complaint for config files, which is part of why YAML and TOML exist as more human-friendly alternatives for hand-edited config.
  • Large numbers can lose precision — JSON numbers are typically parsed as IEEE 754 doubles, so very large integers (like 64-bit IDs) can silently lose precision unless the parser handles big integers specially.
  • Duplicate keys are technically undefined behavior — most parsers just take the last one, but the spec doesn't mandate this.
  • No native date/time type — dates are just strings, usually ISO 8601 by convention, with no enforcement.

Tools

  • jq — the standard CLI for querying/transforming JSON
    bash cat file.json | jq '.server.port' jq -r '.tools[]' file.json
  • jsonlint.com — online validator
  • Most languages have JSON in their standard library (Python's json, Go's encoding/json, etc.) — no third-party dependency usually needed

TOML

What It's For

TOML (Tom's Obvious, Minimal Language) is a config-file format designed to be easy for humans to read and write, and unambiguous to parse. It's the format behind Rust's Cargo.toml, Python's pyproject.toml, and various other tool configs. Think of it as sitting between JSON's strictness and YAML's flexibility — closer to an INI file with real types.

Spec: toml.io.

Core Syntax

Key-value pairs:

name = "Andrew"
active = true
count = 42
ratio = 3.14

Tables (like sections/objects):

[server]
host = "localhost"
port = 8080

[server.tls]
enabled = true

[server.tls] is equivalent to a nested server.tls object — dotted table names express nesting directly.

Arrays:

tools = ["terraform", "kubernetes", "elasticsearch"]

Array of tables (a list of objects — the thing people often reach for TOML specifically for):

[[servers]]
name = "web1"
ip = "10.0.0.1"

[[servers]]
name = "web2"
ip = "10.0.0.2"

Each [[servers]] block appends a new entry to the servers array.

Inline tables:

point = { x = 1, y = 2 }

Multi-line strings:

description = """
Line one.
Line two.
"""

Dates/times are a native type (unlike JSON/YAML, which treat them as strings):

created = 2026-08-25T14:30:00Z

Comments:

# This is a comment

Gotchas

  • Table order matters for readability but not semantics — once you define [server], you can't re-open it later in the file out of order in a conflicting way; keys must be defined before any subtable that isn't itself.
  • Dotted keys vs. tables can express the same nesting two ways (a.b.c = 1 vs. [a.b]\nc = 1), which can look inconsistent across a hand-edited file.
  • Arrays of tables ([[x]]) trip up newcomers — it's the least JSON/YAML-like construct and the one place TOML syntax genuinely diverges in shape.
  • Less common outside the Rust/Python packaging ecosystem, so tooling support (linters, editor plugins) is thinner than for JSON/YAML.

Tools

  • toml.io — spec and reference, including a "compare with JSON/YAML" cheat sheet
  • Taplo — TOML linter/formatter/LSP (used by rust-analyzer tooling)
  • Most language ecosystems have a native TOML library (Rust's toml crate, Python's tomllib in the standard library since 3.11)

Quick Comparison

Feature Markdown YAML JSON TOML
Purpose Formatted text Config / data Data interchange Config
Comments N/A (it's prose) Yes (#) No Yes (#)
Native dates No No No Yes
Whitespace-significant Some Yes (indentation) No No
Human-editable Yes Yes Awkward Yes
Typical use Docs, READMEs K8s, CI/CD, Ansible APIs, data exchange Cargo.toml, pyproject.toml
in this section
* javascript object notation (json)/
* markdown/
* tom's obvious, minimal language (toml)/
* yet another markup language (yaml)/