# YAML

## Hva det er

YAML, på disk en fil som slutter på `.yaml` eller `.yml`, er et tekstformat for strukturerte data
som markerer strukturen med innrykk, kolon og bindestreker i stedet for klammer og anførselstegn, og
det er derfor det er det vanlige valget for konfigurasjon som folk skriver og gjennomgår for hånd.
Navnet er et rekursivt akronym, «YAML Ain’t Markup Language». RFC 9512, publisert i februar 2024,
registrerte medietypen `application/yaml` og pekte ut `.yaml` som den foretrukne endelsen.

Under den lette tegnsettingen ligger en liten datamodell. Det finnes avbildninger (_mappings_), som
kobler nøkler til verdier, sekvenser, som er ordnede lister, og skalarer: strenger, tall, boolske
verdier og null. Rundt den modellen legger språket til det en person som redigerer en fil, vil ha:
kommentarer som starter med `#`, blokkskalarer som beholder linjeskift med `|` eller bretter dem
sammen med `>`, flere dokumenter i én fil skilt med `---`, ankere og aliaser (`&name` og `*name`)
som lar én verdi gjenbrukes et annet sted, og tagger som navngir en type direkte.

Det første YAML-rammeverket ble skrevet i Perl i 2001. Versjon 1.1 av spesifikasjonen fulgte i 2005,
og versjon 1.2 i 2009, med hovedvekt på å gjøre YAML til et strengt supersett av JSON og med mange
av de eldre reglene for implisitt typing fjernet. Revisjon 1.2.2, publisert i oktober 2021, rettet
feil og klargjorde ordlyden uten å endre språket. Mye programvare leser likevel fortsatt etter de
eldre reglene: dokumentasjonen til Ansible påpeker at PyYAML og libyaml implementerer YAML 1.1.

Dermed står YAML mellom to naboer. JSON er en streng delmengde av det, kortfattet og entydig, laget
for at programmer skal utveksle data. TOML er et flatere konfigurasjonsformat der hver streng står i
anførselstegn, så typen til en verdi aldri blir gjettet. YAML tilbyr mer enn begge, og den ekstra
friheten er nettopp der problemene kommer fra.

## Hvorfor det er viktig

Det egentlige valget er hvilket format en fil skal få når folk skal vedlikeholde den i årevis:
beskrivelser av produksjonssetting, definisjoner av CI-løp, serverkonfigurasjon. Playbooks i Ansible
er YAML, og det samme er konfigurasjonsfilene til mange andre infrastrukturverktøy, så for mye av
det arbeidet er beslutningen allerede tatt lenger opp i kjeden, og det som gjenstår å avgjøre, er
hvor strengt filene skal skrives.

Argumentet for YAML er lesbarhet. Den som gjennomgår en endring, kan lese en diff av det uten å
telle klammer, en kommentar kan forklare hvorfor en verdi er som den er, og et skript eller et
sertifikat over flere linjer får plass i en blokkskalar uten escaping. Mot det står at mellomrom
bærer mening, så en nøkkel som er rykket inn to mellomrom for langt, kan havne i en annen avbildning
mens filen fortsatt lar seg tolke. Tabulatorer er helt forbudt som innrykk. Verdier uten
anførselstegn får type etter hvordan de staves, og reglene for stavingen er ulike i 1.1 og 1.2.
Maler legger en syntaks til oppå: i Ansible må en verdi som begynner med `{{`, stå i anførselstegn,
ellers tar tolkeren den for en avbildning.

Det som avgjør valget, er hvordan filen skal sjekkes. YAML med en linter og en vane med å sette
strenger i anførselstegn er et behagelig format for konfigurasjon et team redigerer sammen. YAML
uten noen av delene er et format der en gyldig fil kan si noe annet enn det forfatteren mente. For
data som sendes mellom programmer, er JSON det tryggere standardvalget, og for en kort
innstillingsfil med få nivåer av nesting er TOML enklere.

## Slik ser det ut i praksis

Selskapet driver sitt eget driftsmiljø, med en tilstedeværelse på nettet, en git-forge, en database,
sikkerhetskopier, e-posttransport og DNS, og
[hele miljøet er beskrevet i 19 Ansible-playbooks og 34 roller på 12 065 linjer YAML](/nb/portfolio/built-the-companys-infrastructure-as-code-108/).
Sammensetningen er data og ikke flagg: hvilke roller en vert kjører, er deklarert i variablene til
nivågruppen den ligger i, og det er nok til at klargjøring uten argumenter bringer hver vert til
tilstanden YAML-en beskriver. Det er YAML brukt som språket for deklarert tilstand, og at det
lykkes, blir sjekket og ikke antatt, fordi en konvergert vert må rapportere null endringer. Siden
oppgir også kostnaden: hver endring går tregere enn det ville gått å redigere en fil på serveren, og
fordi en konvergering som bare blir halvveis gjennomført, er verre enn en som nekter, kjører det nå
en preflight-play foran den.

Tolv tusen linjer YAML trenger en maskin som leser dem før et menneske gjør det.
[YAML er ett av språkene en commit-port med 22 lintere sjekker](/nb/portfolio/built-a-22-linter-commit-gate-127/),
sammen med HTML, CSS, JavaScript, Python, Markdown og shell. Porten har ikke noe advarselsnivå, så
hver diagnostikk stopper commiten, og undertrykking i selve koden er forbudt uten unntak: en
ignore-kommentar eller et disable-direktiv i filen er ingen fluktvei. For YAML betyr det noe, fordi
de fleste fellene nedenfor er helt gyldig syntaks, og bare en regel som avviser dem, fanger dem før
de når en vert.

## Hvor det går galt

Fellene nedenfor har ett trekk felles: filen lar seg tolke uten feil og betyr noe annet.

**En landkode blir til false.** Etter reglene i YAML 1.1 er `yes`, `no`, `on`, `off`, `y` og `n`
uten anførselstegn boolske verdier, i flere varianter av store og små bokstaver. Kjerneskjemaet i
YAML 1.2 beholder bare `true` og `false`, i tre varianter av store og små bokstaver, og RFC 9512
advarer om at en tolker som følger den eldre syntaksen, kan lese `{insecure: n}` som false og ikke
som bokstaven n. En liste over nordiske land viser problemet:

```yaml
countries:
    - DK
    - SE
    - NO # YAML 1.1 leser dette som false
```

Sett hver streng som kan leses som noe annet, i anførselstegn, skriv boolske verdier bare som `true`
og `false`, og la en linter håndheve begge deler.

**Et versjonsnummer mister et siffer.** Tall uten anførselstegn blir konvertert, så `version: 1.10`
kommer fram som flyttallet `1.1`, og etter reglene i 1.1 markerer en innledende null et oktalt tall,
så en kode skrevet som `010` blir 8. Dokumentasjonen til Ansible sier at en versjon som `"1.0"` skal
stå i anførselstegn, av nettopp denne grunnen. Sett identifikatorer, koder og versjoner i
anførselstegn; de er strenger som tilfeldigvis inneholder sifre.

**En nøkkel havner under feil forelder.** Innrykket avgjør hvilken avbildning en nøkkel tilhører, så
én linje som er forskjøvet to mellomrom, flytter en innstilling et annet sted uten noen feilmelding.
Spesifikasjonen krever også at hver nøkkel i en avbildning er unik, og en gjentatt nøkkel langt nede
i en lang blokk er lett å overse i en gjennomgang, så yamllint har en regel for hver av de to
feilene. Kjør den før filen tas i bruk.

**Å laste inn en fil kjører kode.** Tagger kan navngi typer som hører til ett bestemt
programmeringsspråk, og en laster som respekterer dem, bygger det objektet filen ber om, hva det enn
er. Dokumentasjonen til PyYAML sier at den fulle `yaml.load` er like kraftig som `pickle.load` og
kan kalle hvilken som helst Python-funksjon, og viser til `yaml.safe_load`, som bare gjenkjenner
standardtagger. RFC 9512 ber om at kjøring av kode i deserialiserere er slått av som standard. Bruk
den trygge lasteren for hver fil som ikke kom fra ditt eget repo.

**En kort fil fyller minnet.** Ankere og aliaser gjør et YAML-dokument til en graf heller enn et
tre. Når hvert anker gjentar det forrige to ganger, dobler hver ny linje størrelsen, så tretti korte
linjer beskriver mer enn en milliard strenger når et program går gjennom resultatet som et vanlig
tre. RFC 9512 kaller dette eksponentiell dataekspansjon, angrepet kjent som _billion laughs_, og
svarer med prosessorer som begrenser rekursjonsdybden for ankere. Ta bare imot YAML utenfra gjennom
en slik prosessor, og test grensen mot tolkeren som faktisk er i bruk.

## Les mer

- [YAML 1.2.2 specification](https://yaml.org/spec/1.2.2/) — selve språket, også kjerneskjemaet som
  avgjør hvilken type verdier uten anførselstegn får.
- [RFC 9512: YAML Media Type](https://www.rfc-editor.org/rfc/rfc9512) — registreringen av
  `application/yaml`, med sikkerhetsmerknader om boolske verdier, kjøring av kode og utvidelse av
  aliaser.
- [YAML specification changes](https://yaml.org/spec/1.2.2/ext/changes/) — hva hver revisjon endret,
  fra 1.2.1 til 1.2.2.
- [Ansible: YAML Syntax](https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html)
  — delmengden Ansible bruker, med fellene rundt anførselstegn.
- [yamllint rules](https://yamllint.readthedocs.io/en/stable/rules.html) — sjekkene for
  sannhetsverdier, dupliserte nøkler, oktale tall og innrykk.
- [PyYAML documentation](https://pyyaml.org/wiki/PyYAMLDocumentation) — forskjellen på `load` og
  `safe_load`, med bibliotekets egne ord.

<https://engineer.company/nb/glossary/yaml/>
