Why are we templating YAML? (2019)

Engineers argue that templating YAML for infrastructure and Kubernetes configs has become an anti-pattern: once-simple declarative files have accreted loops, conditionals, and vendor-specific DSLs, turning into fragile “yaml-as-programming-language” systems that are hard to validate, debug, and secure. Many prefer generating JSON/YAML from real programming languages (TypeScript, Python, Ruby) or from purpose-built config languages (Jsonnet, CUE, Dhall, Nix), often combined with tools like CDK, Pulumi, or Tanka, so that logic lives in tested code while the cluster still receives plain manifests. Underneath is a broader concern that every new config format and template engine repeats the same mistake—reinventing a constrained language instead of embracing existing ones and proper tooling for types, schemas, and composition.

Why YAML is used and then templated

  • Commenters agree YAML dominates largely by inertia and familiarity: examples and tooling are everywhere, especially in Kubernetes and CI.
  • People start with “just a simple YAML,” then add a few variables, then conditionals/loops, and end up with a de‑facto programming language.
  • Copy‑pasting snippets from docs and blogs is a big reason people stick with raw/templated YAML rather than higher‑level tools.

Critiques of YAML itself

  • Many see YAML as deceptively “human‑friendly”: indentation is fragile, context is easy to lose in long files, and the spec is complex.
  • The type system and implicit conversions (e.g. no → false / “Norway” problem, 1.1 vs 1.2 differences) are recurring complaints.
  • Templated YAML is described as “stringly‑typed programming”: hard to validate, debug, and reason about, with vendor‑specific semantics.
  • Some defend YAML for small to moderately complex configs and for non‑programmers (e.g. front‑matter, simple docker‑compose), especially with schema validation and linters.

Templating vs generating with real languages

  • Strong current: stop inventing half‑baked template DSLs; use a real language (TypeScript, Python, Ruby, Go, etc.) to build data structures and emit JSON/YAML.
  • Benefits cited: reuse of libraries and tooling, type checking, unit tests, easier refactoring, clearer separation between “dumb data” and logic.
  • Counter‑arguments:
    • Letting arbitrary languages run in CI/IaC increases attack surface and complexity.
    • Config should be declarative and restricted; full Turing‑complete power invites spaghetti and makes policy/validation harder.

Alternative config languages / systems

  • Frequently mentioned: Jsonnet, Dhall, CUE, Nix, Nickel, Starlark‑based tools (ytt, Bazel/Starlark, Kurtosis), CDK8s, AWS CDK, Pulumi, Tanka, Kustomize.
  • Opinions diverge: some love Jsonnet/CUE/Nix/Dhall for functional, total, or type‑safe configuration; others find them too niche, hard to learn, or missing libraries.
  • Several suggest “config as code with types,” plus a simple, validated data format as the compiled artifact (often JSON, optionally rendered to YAML).

Kubernetes, Helm, CI, and operators

  • Helm charts are widely called painful: text substitution, indentation juggling, poor error messages, and need to re‑expose Kubernetes features via values.yaml.
  • Some prefer Kustomize or plain manifests; others argue operators or higher‑level CDKs are a better abstraction than ever more YAML templating.
  • For CI (GitHub Actions, etc.), many recommend minimal YAML that just execs real scripts, avoiding complex logic in config files.

Deeper configuration philosophy

  • Repeated theme: “all config drifts toward Turing completeness.”
  • Debate centers on where logic should live, how much power config should have, and how to keep systems observable, testable, and maintainable as complexity grows.