articulos·7 min read

El Problema de Noruega y Otras Trampas de YAML que JSON No Tiene

En YAML el código de país NO se convierte en false, on es un booleano y 1:20 puede ser una hora o el número 80. Un repaso honesto a las trampas del formato que se comió la configuración de DevOps.

DevToolsHub Team
·
El Problema de Noruega y Otras Trampas de YAML que JSON No Tiene

El país que desapareció de la configuración

Cuenta la leyenda —documentada, además— de alguien que mantenía una lista de países soportados en su fichero de configuración:

paises:
  - GB
  - IE
  - FR
  - DE
  - NO
  - FI
  - SE

Todo funcionaba perfectamente, excepto un detalle: los usuarios de Noruega no existían para el sistema. Porque en YAML 1.1, NO —el código ISO de Noruega— se parsea como false. La lista decía [GB, IE, FR, DE, false, FI, SE] y nadie lo notó hasta que los noruegos se quejaron.

Bienvenido al "Norway problem", la más famosa de las trampas de un formato que decidió que la comodidad de escribir era más importante que la previsibilidad de leer. Y como todo lo que toca Kubernetes, Docker Compose, GitHub Actions, Ansible y media infraestructura moderna está escrito en YAML, estas trampas te van a encontrar tarde o temprano.


Primero, lo justo: por qué YAML ganó

Es fácil burlarse de YAML, pero ganó por razones reales. Comparado con JSON tiene comentarios, que en un fichero de configuración son oro puro. No necesita comillas en casi nada. Las listas con guiones y los mapas con indentación se leen de un vistazo. Y permite anclas para no repetir bloques.

Para un docker-compose.yml que escribes a mano una vez al mes, todo eso es genuinamente agradable. El problema no es que YAML sea cómodo de escribir — es lo que pasa después, cuando otra cosa tiene que interpretar lo que escribiste.


La bomba de relojería: tipos implícitos

YAML 1.1 tiene una regla llamada "resolución de tags implícitos": el parser mira tu valor sin comillas e intenta adivinar el tipo. Y adivina con más entusiasmo que criterio.

Booleanos: once formas de decir verdadero

En YAML 1.1 todos estos son booleanos:

verdadero: [true, True, TRUE, yes, Yes, YES, on, On, ON, y, Y]
falso:     [false, False, FALSE, no, No, NO, off, Off, OFF, n, N]

Sí, on y off también. Lo que nos lleva a la perla de GitHub Actions:

# Este fichero empieza literalmente así:
on:
  push:
    branches: [main]

Si parseas un workflow de GitHub Actions con PyYAML (que implementa YAML 1.1), la clave on se convierte en True y tu diccionario tiene una llave booleana en lugar del string "on". La documentación de GHA tuvo que añadir notas avisando de esto. Piénsalo: el trigger más importante de CI/CD tiene el nombre de una palabra reservada accidental.

Números: el sexagesimal que nadie pidió

version: 3.10      # float 3.1 — tu Python 3.10 acaba de ser 3.1
port: 8080         # int, bien
reintento: 1:20    # sexagesimal en YAML 1.1: 60 + 20 = 80. Ochenta.
hex: 0xDEADBEEF    # int
grande: 1_000_000  # int con guiones bajos, depende del parser

Ese 1:20 es mi favorito personal: en YAML 1.1, los números en formato mm:ss se interpretan como sexagesimal. Alguien escribió un timeout de "un minuto veinte" y el sistema recibió el número 80. Funciona, sí. Hasta que lo parseas con otra librería y deja de funcionar.

Null: vacío de cinco formas distintas

a: ~
b: null
c: Null
d: NULL
e:                 # esto también es null

Cinco maneras de decir "nada". Y combinado con lo anterior: value: NONE no es null — es el string "NONE" — pero value: None... depende de tu parser. Divertido en producción.


La lotería de los parsers: 1.1 contra 1.2

YAML 1.2 (2009, hace ya sus años) corrigió gran parte de esto: los booleanos son solo true/false, el sexagesimal desapareció, NO vuelve a ser un string. Problema resuelto.

Excepto que no todo el mundo implementa YAML 1.2. Y aquí está el verdadero peligro: el mismo fichero produce resultados distintos según quién lo lea.

Ecosistema Parser habitual Versión
Python PyYAML 1.1 (sí, en 2026)
Ruby Psych 1.1
Go yaml.v3 1.2 core, con matices
JavaScript yaml / js-yaml 1.2
Kubernetes su sig-yaml vía JSON, otra capa más

Resultado: tu config.yaml con deploy: on significa true para el script de Python de tu compañero y "on" para tu herramienta en Node. Mismo fichero, dos realidades. La regla de supervivencia es una sola: en YAML, pon comillas a todo lo que sea un string. "no", "3.10", "on", "1:20". Las comillas son el único antídoto contra la adivinación.


Anclas, aliases y la bomba de los mil millones de risas

YAML tiene un sistema de reutilización elegante: defines un bloque con &nombre y lo reutilizas con *nombre, o lo fusionas en otro mapa con <<: *nombre:

defaults: &defaults
  retries: 3
  timeout: 30

produccion:
  <<: *defaults
  timeout: 60

Bonito y útil. Pero los aliases pueden anidarse, y un parser ingenuo puede expandir esto:

a: &a ["x","x","x","x","x","x","x","x","x","x"]
b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a,*a]
c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b,*b]
# ... unos niveles más y tienes miles de millones de nodos en memoria

Es el "billion laughs attack": un YAML de unos pocos KB que consume toda tu RAM al parsearlo. Los parsers modernos tienen límites, pero es una categoría de DoS que JSON simplemente no puede expresar. Si alguna vez aceptas YAML de usuarios externos —malas noticias si es así—, valida y limita el parser.


Las trampas de escritura que no son de tipos

Aunque escapes de la inferencia de tipos, YAML tiene minas puramente sintácticas:

Tabs prohibidos

YAML usa solo espacios para indentar. Un tab escondido entre espacios rompe el parseo con errores que rara vez apuntan a la línea correcta. Si tu editor mezcla ambos, configúralo para YAML antes de que lo descubras en un apply a las 2 AM.

Las listas al nivel equivocado

servidores:
- web1    # esto es válido: el guion puede ir al nivel de la clave
  - web2  # esto TAMBIÉN es válido en otro estilo

Ambas formas son legales y significan lo mismo, pero mezclarlas en el mismo fichero invita a errores. Y el clásico:

comando: echo: hola      # ERROR: ":" dentro del valor rompe el mapa
comando: "echo: hola"    # bien

Dos puntos seguidos de espacio dentro de un valor sin comillas hacen que el parser crea que hay otra clave. Comillas. Siempre.

Los bloques multilínea que nadie recuerda

literal: |        # preserva saltos de línea
  línea 1
  línea 2
plegado: >        # junta las líneas con espacios
  esto se
  convierte en una línea
sin_salto_final: |-   # | sin el 
 final
con_extra: |+         # | conservando 
 finales

|, >, |-, |+, >-... hay seis combinaciones y nadie se sabe las cuatro últimas de memoria. La buena noticia: el 99% de las veces quieres | o |-.


Mi postura, después de años de esto

YAML es un formato razonable para configuración escrita por humanos, corta y leída por una sola herramienta conocida. docker-compose.yml, un workflow de Actions, un values.yaml de Helm. Ahí cumple.

Es una mala elección para:

  • Intercambio de datos entre sistemas: usa JSON. Tipos explícitos, parsers idénticos en todos lados, cero adivinación. Si necesitas validarlo, escribe un JSON Schema y listo.
  • Configuración grande y compleja: cuando tu YAML necesita anclas, templates de Helm encima y tres capas de merge, ya no es configuración — es un lenguaje de programación mal diseñado. TOML para lo simple, HCL/CUE/Dhall para lo complejo.
  • Cualquier cosa que parsee más de un lenguaje: la lotería 1.1 vs 1.2 te morderá.

Depura antes de desplegar

La forma más rápida de saber qué está entendiendo realmente el parser es convertir tu YAML a JSON y mirar el resultado: nuestro conversor YAML ⇄ JSON te muestra al instante si tu NO se convirtió en false, si tu on: es ahora true:, o si 1:20 salió como 80. Pega tu config, mira el JSON, y si ves un booleano donde debería haber un string — ya sabes dónde poner las comillas.

YAML no es el villano que pintan los hilos de odio en internet. Es solo un formato que confía demasiado en que sabes lo que haces. Y ahora ya lo sabes.

#yaml#json#devops#configuración#opinión

Related articles