Abre cualquier proyecto de Rust y encontrarás un Cargo.toml. Clona un repositorio de GitHub y encontrarás una carpeta .github/workflows/ llena de YAML. Ambos formatos hacen el mismo trabajo superficial — almacenar configuración estructurada que los humanos editan — pero hacen compromisos muy diferentes. Si alguna vez el YAML ha alterado silenciosamente un valor, o te has preguntado por qué Rust eligió TOML en lugar de YAML para su manifiesto de paquetes, este es el artículo para ti.

La diferencia fundamental: explícito vs implícito

La división fundamental entre TOML y YAML trata sobre cuánto puede adivinar el analizador. TOML es explícito: cada valor tiene un tipo inequívoco. Las cadenas siempre van entre comillas. Los booleanos son exactamente true o false. Los datetimes son valores de primera clase con su propia sintaxis. No hay coerción de tipos implícita — el analizador no intenta ser listo.

YAML se inclina en la otra dirección. Intenta ser conveniente: no necesitas comillas alrededor de la mayoría de las cadenas, y el analizador infiere los tipos a partir de la apariencia del valor. Esa inferencia es lo que pilla a la gente. La especificación YAML 1.1 (aún usada por muchas herramientas) trata yes, no, on, off, true y false como booleanos. Interpreta cadenas de versión sin comillas como 1.0 como flotantes. Y luego está el Problema de Noruega.

Las famosas trampas de YAML

El Problema de Noruega se convirtió en algo así como un meme en los círculos DevOps. En YAML 1.1, el código de país NO se analiza como el booleano false. Así que una configuración que mapea códigos ISO de países a ajustes convertiría silenciosamente la entrada de Noruega en un booleano. La especificación YAML 1.2 lo corrigió, pero muchos analizadores ampliamente usados — incluido el modo predeterminado de PyYAML hasta hace poco — aún apuntan a YAML 1.1. Verifica qué versión de especificación implementa realmente tu toolchain antes de confiar en él.

El Problema de Noruega en la práctica: En YAML 1.1, NO, Yes, on y off sin comillas se convierten todos en booleanos. La solución es simple — poner las cadenas entre comillas — pero el problema es que es silencioso. Tu configuración carga sin errores y obtienes un booleano donde esperabas una cadena.
yaml
# YAML 1.1 implicit type coercion — all of these silently become booleans:
countries:
  norway: NO        # → false  ← The Norway Problem
  sweden: SE        # → "SE"   (fine, not in the boolean list)
  enabled: yes      # → true
  disabled: no      # → false
  feature_flag: on  # → true
  another: off      # → false

# Version strings can become numbers:
python_version: 3.10   # → float 3.1 (trailing zero dropped)
api_version: 1.0       # → float 1.0

# Safe: quote anything that could be ambiguous
python_version: "3.10"
country: "NO"
enabled: "yes"
  • Literales octales. En YAML 1.1, 010 se analiza como 8 (octal), no 10. Esto importa para valores de permisos de archivo como 0755.
  • Tabulación vs espacio. YAML prohíbe caracteres de tabulación para la sangría. Pega código de un editor configurado para usar tabulaciones y obtendrás un error de análisis críptico — o peor, un desalineamiento silencioso.
  • Nulos implícitos. Una clave sin valor se convierte en null. Fácil de crear accidentalmente al editar a mano.
  • Sensibilidad a la sangría. Un espacio extra y un valor se desplaza silenciosamente de un padre a otro. Sin error, solo datos incorrectos.

TOML: cómo se ven realmente los tipos explícitos

El sistema de tipos de TOML está detallado en la especificación TOML v1.0. Las cadenas deben ir entre comillas (simples o dobles). Los booleanos son true o false y nada más. Los enteros son enteros. Los flotantes son flotantes. Y los datetimes — algo que ni JSON ni YAML tienen como tipo nativo — son valores de primera clase en TOML.

toml
# TOML types are always unambiguous
name = "my-app"          # string — must be quoted
version = "1.0.0"        # string — quotes make it clear this is not a float
port = 8080              # integer
debug = false            # boolean — only true/false, nothing else
threshold = 0.95         # float

# Datetime is a first-class type — no string parsing needed
created_at = 2024-01-15T09:30:00Z
build_date = 2024-03-20

# Arrays
allowed_hosts = ["localhost", "staging.example.com", "api.example.com"]

# Inline tables
[database]
host = "postgres.internal"
port = 5432
name = "payments_prod"
pool_size = 20

El código de país NO es simplemente una cadena en TOML porque no está entre comillas, pero TOML no infiere tipos a partir de valores similares a cadenas — los valores sin comillas siguen reglas sintácticas estrictas. Para ser una cadena, necesita comillas. TOML simplemente no tiene el mecanismo de coerción implícita que causa las trampas de YAML.

Lado a lado: una configuración de pipeline CI

Aquí hay un workflow de GitHub Actions — este es el territorio natural de YAML. El formato encaja naturalmente porque GitHub Actions espera YAML, el anidamiento basado en sangría refleja la estructura lógica, y los comentarios son esenciales para explicar configuraciones de pasos no obvias.

yaml
# .github/workflows/release.yml
name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  build-and-publish:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      packages: write

    steps:
      - uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

      - name: Build
        run: npm run build --if-present

      - name: Publish to npm
        run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Ahora aquí está el manifiesto de proyecto equivalente en TOML — aquí es donde TOML brilla. Compara una estructura real de Cargo.toml con cómo se vería la misma configuración en YAML:

toml
# Cargo.toml — Rust package manifest
[package]
name = "payments-service"
version = "2.4.1"
edition = "2021"
description = "Payment processing microservice"
license = "MIT"
authors = ["Alice Chen <[email protected]>"]

[dependencies]
tokio = { version = "1.35", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sqlx = { version = "0.7", features = ["postgres", "runtime-tokio-native-tls"] }
tracing = "0.1"
anyhow = "1.0"

[dev-dependencies]
tokio-test = "0.4"
wiremock = "0.6"

[profile.release]
opt-level = 3
lto = true
codegen-units = 1

Las debilidades de TOML: array de tablas

TOML no está exento de asperezas. La sintaxis para los arrays de tablas — [[double brackets]] — es una de las cosas más confusas de la especificación. Es la forma de expresar lo que JSON escribiría como un array de objetos, y al principio se lee de manera extraña.

toml
# TOML array of tables — [[double brackets]] creates array entries
[[server]]
host = "web-01.example.com"
port = 443
region = "us-east-1"

[[server]]
host = "web-02.example.com"
port = 443
region = "us-west-2"

[[server]]
host = "web-03.example.com"
port = 443
region = "eu-west-1"

# The above is equivalent to this JSON:
# { "server": [
#   { "host": "web-01.example.com", "port": 443, "region": "us-east-1" },
#   { "host": "web-02.example.com", "port": 443, "region": "us-west-2" },
#   { "host": "web-03.example.com", "port": 443, "region": "eu-west-1" }
# ]}

En YAML la misma estructura se lee más naturalmente — solo una lista con propiedades sangradas. Para datos profundamente anidados con entradas de array repetidas, el anidamiento basado en sangría de YAML es genuinamente menos verboso que los encabezados [[section]] de TOML. TOML tampoco tiene equivalente al sistema de anclas y alias de YAML, por lo que no puedes definir un bloque compartido una vez y referenciarlo en otro lugar. Si te encuentras duplicando el mismo conjunto de valores en múltiples tablas TOML, estás obligado a copiarlos manualmente.

Dónde gana cada formato en la práctica

YAML gana por defecto en estos ecosistemas:

  • GitHub Actions. Toda la sintaxis de workflow es YAML. No tienes elección aquí, y está bien — la sangría se corresponde bien con la estructura anidada de jobs, pasos y condiciones.
  • Kubernetes. Cada manifiesto — Deployments, Services, ConfigMaps, reglas Ingress — es YAML. El modelo de objetos de Kubernetes está profundamente anidado, y YAML lo maneja con elegancia.
  • Docker Compose. Definiciones de servicios, redes, volúmenes — todo YAML. Los comentarios explicando por qué se expone un puerto o por qué se usa un intervalo de healthcheck específico son parte de la documentación.
  • Ansible. Playbooks, roles, archivos de variables — YAML en todas partes. El soporte de comentarios se usa activamente para explicar parámetros de tareas no obvios.

TOML gana cuando controlas el formato:

  • Proyectos Rust. Cargo.toml es el estándar de oro. Declaraciones de dependencias, feature flags, perfiles de build — todo en TOML. Los tipos explícitos significan que las cadenas de versión como "1.0.0" permanecen como cadenas.
  • Proyectos Python. pyproject.toml se ha convertido en el estándar para metadatos de proyectos Python, configuración de build y ajustes de herramientas (Black, isort, mypy, pytest todos leen desde él).
  • Configuración de herramientas donde la ambigüedad causa bugs. Si tu configuración contiene cadenas de versión, códigos de país u otros valores que podrían parecer booleanos o números, los tipos explícitos de TOML eliminan toda una categoría de sorpresas de análisis.
  • Configuraciones planas. Cuando tu configuración es principalmente pares clave-valor a uno o dos niveles de anidamiento, TOML es más legible que YAML y más limpio que JSON.

La guía de decisión práctica

La mayor parte del tiempo el ecosistema toma la decisión por ti. GitHub Actions es YAML. Kubernetes es YAML. Rust es TOML. El toolchain de Python es TOML. Cuando realmente tienes libre elección, usa esto como guía:

  • Usa YAML cuando la herramienta lo exige — plataformas CI/CD, Kubernetes, charts Helm, Docker Compose, Ansible. Luchar contra la convención cuesta más de lo que ahorra.
  • Usa YAML cuando necesitas anclas y alias para mantener una configuración compleja DRY — no hay equivalente en TOML.
  • Usa TOML para manifiestos de proyectos y configuración de herramientas que controlas — metadatos de paquetes, ajustes de linters, configuración de build.
  • Usa TOML cuando tu configuración contiene valores que YAML 1.1 podría malinterpretar — cadenas de versión, códigos de país, cualquier cosa que se parezca a un booleano o número.
  • Pon entre comillas tus cadenas YAML siempre que el valor pueda confundirse con un booleano, número o null. Especialmente: números de versión, códigos de país, cualquier cosa que empiece con un dígito, y valores como yes, no, on, off.

Trabajando con ambos formatos

¿Necesitas validar o formatear un archivo de configuración? El Formateador TOML maneja archivos TOML, y el Formateador YAML cubre YAML. Si necesitas migrar una configuración de un formato a otro — por ejemplo, convertir una configuración de herramienta basada en YAML a TOML para un proyecto que lo prefiere — los conversores TOML a JSON y YAML a JSON producen ambos JSON, que luego puedes convertir a tu formato de destino. A veces pasar por JSON como paso intermedio es el camino más fiable.

Algo que vale la pena saber: ambos formatos tienen sus propios subconjuntos compatibles con JSON. El JSON válido es YAML válido (YAML es un superconjunto de JSON). TOML no tiene esa relación con JSON, pero la especificación TOML se mantiene intencionalmente simple — es una lectura corta, lo que no se puede decir de la especificación YAML 1.2 completa.

Conclusión

TOML y YAML no compiten realmente. Se han asentado en nichos diferentes basados en sus prioridades de diseño. Los tipos implícitos y la estructura basada en sangría de YAML lo convierten en la opción natural para configuraciones grandes y anidadas mantenidas por equipos — piensa en manifiestos de Kubernetes y workflows de GitHub Actions. Los tipos explícitos y la estructura de secciones planas de TOML lo convierten en la opción natural para manifiestos de proyectos y configuración de herramientas donde una cadena de versión o código de país mal leído causaría un bug real.

Lo único que debes recordar: si estás escribiendo YAML y tienes valores que parecen booleanos, números o nulos — ponlos entre comillas. Es el único hábito que previene la mayoría de los bugs de configuración relacionados con YAML. Y si estás empezando un nuevo proyecto y puedes elegir libremente tu formato de configuración, TOML vale la pena. El repositorio GitHub de TOML tiene buenos ejemplos, y una vez que has escrito un Cargo.toml o un pyproject.toml, el atractivo del formato queda bastante claro.