Yaml #
YAML (YAML Ain’t Markup Language) adalah format serialisasi data yang dirancang agar mudah dibaca manusia (human-readable). YAML sangat populer untuk:
- File konfigurasi aplikasi
- DevOps (Docker Compose, Kubernetes, GitHub Actions)
- Infrastructure as Code
- CI/CD pipeline
- Configuration-driven systems
Dalam ekosistem Golang, YAML sering digunakan untuk:
- Konfigurasi aplikasi production
- Environment management
- Feature flags
- Mapping config ke struct strongly-typed
Artikel ini membahas secara mendalam:
- Konsep dasar YAML
- Library YAML di Go
- Encoding & Decoding
- Struct tags dan advanced mapping
- Custom marshal & unmarshal
- Strict mode & validation
- Multi-document YAML
- Anchor, alias, dan merge key
- Dynamic config handling
- Performance consideration
- Best practice production
Mengenal YAML #
Contoh YAML:
server:
host: localhost
port: 8080
database:
driver: postgres
host: db
port: 5432
ssl: false
Karakteristik utama:
- Indentation-based (spasi, bukan tab)
- Mendukung nested object
- Mendukung list
- Mendukung komentar (#)
- Mendukung multiple document
Library YAML di Golang #
Library paling umum digunakan:
gopkg.in/yaml.v3
Install:
go get gopkg.in/yaml.v3
Kenapa v3?
- Mendukung Node API
- Support strict decoding
- Lebih stabil untuk production
Basic Decoding (Unmarshal) #
import "gopkg.in/yaml.v3"
type Config struct {
Server struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
} `yaml:"server"`
}
var cfg Config
err := yaml.Unmarshal(data, &cfg)
⚠ Harus pointer.
Basic Encoding (Marshal) #
b, err := yaml.Marshal(cfg)
YAML akan di-generate otomatis berdasarkan struct.
Struct Tag YAML #
Contoh:
type Database struct {
Driver string `yaml:"driver"`
Host string `yaml:"host"`
Port int `yaml:"port"`
SSL bool `yaml:"ssl,omitempty"`
}
Opsi penting:
omitempty-(ignore field)
Berbeda dengan JSON: YAML tidak case-sensitive secara default, tetapi tetap disarankan eksplisit menggunakan tag.
Strict Mode (UnmarshalStrict) #
Secara default, unknown field diabaikan.
Untuk strict mode:
decoder := yaml.NewDecoder(reader)
decoder.KnownFields(true)
err := decoder.Decode(&cfg)
Ini penting agar:
- Salah ketik config terdeteksi
- Tidak ada silent failure
Best practice: aktifkan KnownFields(true) untuk production.
Multi Document YAML #
YAML mendukung multiple document:
---
app: service-a
---
app: service-b
Handling di Go:
decoder := yaml.NewDecoder(file)
for {
var doc map[string]interface{}
err := decoder.Decode(&doc)
if err == io.EOF {
break
}
}
Digunakan di Kubernetes manifest.
YAML Anchor & Alias #
Contoh:
default: &default
timeout: 30
retries: 3
serviceA:
<<: *default
url: http://a
YAML v3 akan resolve merge key secara otomatis saat Unmarshal.
Namun hati-hati:
- Tidak semua library support penuh
- Debug bisa membingungkan
Handling Nested & Slice #
servers:
- host: a
port: 8080
- host: b
port: 8081
Struct:
type Server struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
}
type Config struct {
Servers []Server `yaml:"servers"`
}
Zero Value vs Missing Field #
Masalah umum pada config update.
Solusi: gunakan pointer.
type Config struct {
Timeout *int `yaml:"timeout"`
}
Jika nil → tidak diset Jika non-nil → override
Custom Unmarshal #
Untuk validasi atau transformasi.
func (c *Config) UnmarshalYAML(value *yaml.Node) error {
type rawConfig Config
var aux rawConfig
if err := value.Decode(&aux); err != nil {
return err
}
if aux.Server.Port <= 0 {
return fmt.Errorf("invalid port")
}
*c = Config(aux)
return nil
}
Digunakan untuk:
- Validasi
- Default value injection
- Transformasi format
Default Value Pattern #
YAML tidak punya default value.
Pattern umum:
cfg := Config{
Server: Server{
Port: 8080,
},
}
yaml.Unmarshal(data, &cfg)
Nilai yang tidak ada akan tetap default.
Dynamic YAML (map[string]interface{}) #
var data map[string]interface{}
yaml.Unmarshal(b, &data)
Masalah:
- Type assertion kompleks
- Tidak type-safe
- Sulit maintain
Gunakan hanya jika schema benar-benar dinamis.
YAML vs JSON dalam Config #
Kenapa YAML populer untuk config?
- Lebih readable
- Mendukung komentar
- Indentation natural
Kenapa JSON kadang lebih aman?
- Tidak ambigu
- Parser lebih ketat
- Lebih cepat
Tradeoff:
YAML lebih fleksibel tapi lebih kompleks.
Security Concern #
Beberapa risiko:
- Large file DOS
- Resource exhaustion
- Unexpected type coercion
Mitigasi:
- Batasi ukuran file
- Gunakan strict mode
- Validasi setelah decode
Performance Consideration #
YAML parsing lebih lambat dari JSON karena:
- Lebih kompleks grammar
- Indentation parsing
- Support anchor & merge
Untuk high-throughput system:
- Hindari parsing YAML per request
- Parse sekali saat startup
- Simpan di memory
Production Config Pattern #
Best practice umum:
- Load YAML saat startup
- Inject default value
- Validate config
- Freeze config (read-only)
- Jangan reload tanpa kontrol
Contoh pattern:
Load → Merge Env → Validate → Start App
Sering digabung dengan:
- ENV override
- Secret manager
- CLI flag
Integrasi dengan ENV #
Pattern umum:
- Load YAML
- Override dengan environment variable
Contoh:
APP_SERVER_PORT=9090
Mapping manual atau menggunakan library tambahan.
Common Mistakes #
- Menggunakan tab (harus spasi)
- Tidak strict mode
- Tidak validasi setelah decode
- Menaruh logic kompleks di config
- Reload config tanpa concurrency safety
- Menggunakan dynamic map berlebihan
Penutup #
YAML di Golang sangat powerful untuk configuration-driven system, namun memiliki kompleksitas lebih tinggi dibanding JSON.
Menguasai YAML handling berarti memahami:
- Mapping struct
- Strict decoding
- Default injection
- Custom unmarshal
- Multi-document
- Anchor & merge
- Security consideration
Untuk production-grade system:
Gunakan YAML sebagai configuration layer, bukan data transport layer.
Parse sekali saat startup, validasi ketat, dan jangan biarkan konfigurasi menjadi sumber bug tersembunyi.