Herhangi bir Rust projesini alın, bir Cargo.toml bulursunuz. Bir GitHub reposunu klonlayın, YAML dolu bir .github/workflows/ klasörü bulursunuz. Her iki format da yüzeysel düzeyde aynı işi yapıyor — insanların düzenlediği yapılandırılmış yapılandırmayı depolama — ancak çok farklı dengeler kuruyorlar. YAML'ın bir değeri sessizce tahrip ettiğini yaşadıysanız veya Rust'ın paket manifestesi için neden YAML yerine TOML'u seçtiğini merak ettiyseniz, bu makale tam size göre.

Temel Fark: Açık vs Örtük

TOML ve YAML arasındaki temel ayrım, ayrıştırıcının ne kadar tahmin yapmasına izin verildiğiyle ilgilidir. TOML açıktır: her değerin belirsizliksiz bir tipi vardır. Dizeler her zaman tırnak içindedir. Boolean'lar tam olarak true veya false'dur. Tarih/saatler kendi sözdizimiyle birinci sınıf değerlerdir. Örtük tip zorlaması yoktur — ayrıştırıcı akıllı olmaya çalışmaz.

YAML ise öte yöne eğilir. Kullanışlı olmaya çalışır: çoğu dize etrafında tırnak gerekmez ve ayrıştırıcı değerin görünümünden tipleri çıkarsar. Bu çıkarsama insanları ısıran şeydir. YAML 1.1 spesifikasyonu (hâlâ birçok araç tarafından kullanılan) yes, no, on, off, true ve false'un tümünü boolean olarak ele alır. 1.0 gibi tırnak işaretleri olmayan sürüm dizelerini ondalık sayı olarak ele alır. Ve bir de Norveç Sorunu var.

YAML'ın Ünlü Tuzakları

Norveç Sorunu, DevOps çevrelerinde bir tür meme haline geldi. YAML 1.1'de, ülke kodu NO boolean false olarak ayrıştırılır. Bu nedenle, ISO ülke kodlarını ayarlara eşleyen bir yapılandırma, Norveç'in girişini sessizce bir boolean'a dönüştürürdü. YAML 1.2 spesifikasyonu bunu düzeltti, ancak yaygın olarak kullanılan birçok ayrıştırıcı — yakın zamana kadar PyYAML'ın varsayılan modu dahil — hâlâ YAML 1.1'i hedefliyor. Güvenmeden önce araçlarınızın hangi spesifikasyon sürümünü gerçekten uyguladığını kontrol edin.

Norveç Sorunu pratikte: YAML 1.1'de, tırnak işaretsiz NO, Yes, on ve off'un tümü boolean olur. Düzeltme basittir — dizelerinizi tırnak içine alın — ancak sorun sessizdir. Yapılandırmanız hatasız yüklenir ve bir dize beklediğiniz yerde bir boolean alırsınız.
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"
  • Sekizli sabit değerler. YAML 1.1'de, 010 10 değil 8 olarak (sekizli) ayrıştırılır. Bu, 0755 gibi dosya izin değerleri için önemlidir.
  • Tab vs boşluk. YAML, girinti için sekme karakterlerini yasaklar. Sekme kullanmak üzere yapılandırılmış bir editörden kod yapıştırın ve şifreli bir ayrıştırma hatası alırsınız — ya da daha da kötüsü, sessiz hizasızlık.
  • Örtük null'lar. Değeri olmayan bir anahtar null olur. Elle düzenlerken yanlışlıkla oluşturmak kolaydır.
  • Girinti duyarlılığı. Bir boşluk fazla ve bir değer sessizce bir üstden diğerine geçer. Hata yok, sadece yanlış veri.

TOML: Açık Tiplerin Gerçekte Nasıl Göründüğü

TOML'un tip sistemi TOML v1.0 spesifikasyonunda belirtilmiştir. Dizeler tırnak içinde olmalıdır (tek veya çift). Boolean'lar true veya false'dur ve başka bir şey değildir. Tam sayılar tam sayıdır. Ondalıklılar ondalıklıdır. Ve tarih/saatler — JSON'un ve YAML'ın yerel tip olarak sahip olmadığı bir şey — TOML'da birinci sınıf değerlerdir.

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

NO ülke kodu TOML'da tırnak içine alınmadığı için bir dize olmaz, ancak TOML dize benzeri değerlerden tipleri çıkarsamaz — tırnak içine alınmamış değerler katı sözdizim kurallarını izler. Dize olmak için tırnak gerekir. TOML'da YAML'ın tuzaklarına neden olan örtük zorlama mekanizması yoktur.

Yan Yana: Bir CI Boru Hattı Yapılandırması

İşte bir GitHub Actions iş akışı — bu YAML'ın ev sahasıdır. Format doğal biçimde oturur çünkü GitHub Actions YAML bekler, girinti tabanlı iç içe geçme mantıksal yapıyı yansıtır ve yorumlar açık olmayan adım yapılandırmalarını açıklamak için gereklidir.

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 }}

Şimdi işte eşdeğer proje manifestinin TOML versiyonu — TOML'un parladığı yer burası. Gerçek bir Cargo.toml yapısını aynı yapılandırmanın YAML'da nasıl görüneceğiyle karşılaştırın:

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

TOML'un Zayıflıkları: Tablo Dizisi

TOML'un pürüzlü kenarları da yok değil. Tablo dizileri için sözdizim — [[çift parantezler]] — spesifikasyondaki en kafa karıştırıcı şeylerden biridir. JSON'ın bir nesne dizisi olarak yazacağını nasıl ifade ettiğinizdir ve ilk başta garip okunur.

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" }
# ]}

YAML'da aynı yapı daha doğal okunur — yalnızca girintili özelliklerle bir liste. Tekrarlanan dizi girişlerine sahip derinlemesine iç içe geçmiş veriler için, YAML'ın girinti tabanlı iç içe geçmesi TOML'un [[bölüm]] başlıklarından gerçekten daha az ayrıntılıdır. TOML'un ayrıca YAML'ın çıpa ve takma ad sistemine eşdeğeri yoktur, bu nedenle paylaşılan bir bloğu bir kez tanımlayıp başka yerlerde başvuramazsınız. Birden fazla TOML tablosunda aynı değer kümesini çoğaltırken kendinizi bulursanız, bunları elle kopyalamak zorunda kalırsınız.

Pratikte Her Formatın Kazandığı Yerler

YAML bu ekosistemlerde varsayılan olarak kazanır:

  • GitHub Actions. Tüm iş akışı sözdizimi YAML'dır. Burada seçim şansınız yok ve sorun değil — girinti, iş, adım ve koşulların iç içe yapısıyla iyi uyuşur.
  • Kubernetes. Her manifest — Deployment'lar, Service'ler, ConfigMap'ler, Ingress kuralları — YAML'dır. Kubernetes nesne modeli derinlemesine iç içe geçmiştir ve YAML bunu zarif biçimde işler.
  • Docker Compose. Servis tanımları, ağlar, birimler — hepsi YAML. Bir portun neden açıklandığını veya belirli bir healthcheck aralığının neden kullanıldığını açıklayan yorumlar belgelemenin bir parçasıdır.
  • Ansible. Playbook'lar, roller, değişken dosyaları — YAML boyunca. Yorum desteği, açık olmayan görev parametrelerini açıklamak için aktif olarak kullanılır.

TOML formatı kontrol ettiğinizde kazanır:

  • Rust projeleri. Cargo.toml altın standarttır. Bağımlılık bildirimleri, özellik bayrakları, derleme profilleri — hepsi TOML'da. Açık tipler, "1.0.0" gibi sürüm dizelerinin dize olarak kalmasını sağlar.
  • Python projeleri. pyproject.toml, Python proje meta verisi, derleme yapılandırması ve araç ayarları (Black, isort, mypy, pytest hepsini okur) için standart haline gelmiştir.
  • Belirsizliğin hatalara neden olduğu araç yapılandırması. Yapılandırmanız sürüm dizeleri, ülke kodları veya boolean ya da sayı gibi görünebilecek diğer değerler içeriyorsa, TOML'un açık tipleri ayrıştırma sürprizlerinin tüm bir kategorisini ortadan kaldırır.
  • Düz yapılandırmalar. Yapılandırmanız çoğunlukla bir veya iki iç içe geçme düzeyindeki anahtar-değer çiftlerinden oluştuğunda, TOML YAML'dan daha okunabilir ve JSON'dan daha temizdir.

Pratik Karar Kılavuzu

Çoğu zaman seçim, ekosistem tarafından sizin için yapılır. GitHub Actions YAML'dır. Kubernetes YAML'dır. Rust TOML'dur. Python araçları TOML'dur. Gerçekten özgür bir seçiminiz olduğunda, bunu kılavuz olarak kullanın:

  • YAML kullanın araçlar bunu zorunlu kıldığında — CI/CD platformları, Kubernetes, Helm chartları, Docker Compose, Ansible. Kurala karşı savaşmak kazandığından daha maliyetlidir.
  • YAML kullanın karmaşık bir yapılandırmayı DRY tutmak için çıpalara ve takma adlara ihtiyaç duyduğunuzda — TOML'da eşdeğeri yoktur.
  • TOML kullanın kontrol ettiğiniz proje manifestleri ve araç yapılandırmaları için — paket meta verileri, linter ayarları, derleme yapılandırması.
  • TOML kullanın yapılandırmanız YAML 1.1'in yanlış yorumlayabileceği değerler içerdiğinde — sürüm dizeleri, ülke kodları, boolean veya sayıya benzeyen her şey.
  • YAML dizelerinizi tırnak içine alın değer bir boolean, sayı veya null ile karıştırılabilecekse. Özellikle: sürüm numaraları, ülke kodları, rakamla başlayan her şey ve yes, no, on, off gibi değerler.

Her İki Formatla Çalışma

Üzerinde çalıştığınız bir yapılandırma dosyasını doğrulamanız veya güzel yazdırmanız mı gerekiyor? TOML Biçimleyici TOML dosyalarını işler ve YAML Biçimleyici YAML'ı kapsar. Bir yapılandırmayı bir formattan diğerine taşımanız gerekiyorsa — diyelim ki bunu tercih eden bir proje için YAML tabanlı bir araç yapılandırmasını TOML'a dönüştürmek — hem TOML'dan JSON'a hem de YAML'dan JSON'a dönüştürücüler JSON çıktısı verir, daha sonra hedef formatınıza dönüştürebilirsiniz. Bazen ara adım olarak JSON'dan geçmek en güvenilir yoldur.

Bilmeye değer bir şey: her iki formatın da kendi JSON uyumlu alt kümeleri vardır. Geçerli JSON, geçerli YAML'dır (YAML, JSON'un bir üst kümesidir). TOML'un JSON ile bu ilişkisi yoktur, ancak TOML spesifikasyonu kasıtlı olarak basit tutulur — kısa bir okumadır ki bu, tam YAML 1.2 spesifikasyonu için söylenemez.

Sonuç

TOML ve YAML gerçekten rekabet etmiyor. Tasarım önceliklerine göre farklı nişlere yerleştiler. YAML'ın örtük tipleri ve girinti tabanlı yapısı, onu ekipler tarafından sürdürülen büyük, iç içe geçmiş yapılandırmalar için doğal seçim yapar — Kubernetes manifestlerini ve GitHub Actions iş akışlarını düşünün. TOML'un açık tipleri ve düz bölüm yapısı, onu yanlış okunan bir sürüm dizesinin veya ülke kodunun gerçek bir hataya neden olacağı proje manifestleri ve araç yapılandırması için doğal seçim yapar.

Yanınızda götüreceğiniz tek şey: YAML yazıyorsanız ve boolean, sayı veya null gibi görünebilecek değerleriniz varsa — tırnak içine alın. Bu, YAML ile ilgili yapılandırma hatalarının çoğunu önleyen tek alışkanlıktır. Yapılandırma formatınızı özgürce seçebiliyorsanız ve yeni bir proje başlıyorsanız, TOML'a bir bakın. TOML GitHub deposu iyi örnekler içerir ve bir Cargo.toml veya pyproject.toml yazdıktan sonra, formatın çekiciliği oldukça netleşir.