# Golang Spf13 Viper > Librería de configuración en Go con spf13/viper: precedencia en capas (flag > env > archivo > KV > default), BindPFlag, AutomaticEnv, Unmarshal con mapstructure, Sub, WatchConfig y aislamiento en tests. Fuente: https://skillsagentes.com/skills/samber/cc-skills-golang/golang-spf13-viper Markdown: https://skillsagentes.com/skills/samber/cc-skills-golang/golang-spf13-viper.md Repositorio: https://github.com/samber/cc-skills-golang Autor: samber Licencia: MIT Actualizado: el mes pasado Coste de contexto: 162 tok instalada, 2.6k tok al activarse, 12.5k tok con todos los archivos del bundle Bundle: 7 archivos, 49 KB Permisos que pide: read edit write glob grep bash(go:*) bash(golangci-lint:*) bash(git:*) agent webfetch mcp__context7__resolve-library-id mcp__context7__query-docs bash(godig:*) bash(gopls:*) lsp mcp__gopls__* ## Instalación Un skill son archivos markdown: los mismos archivos valen para cualquier agente y lo único que cambia es el directorio de destino, es decir la bandera `--agent`. Añade `-g` para instalarlo en todos los proyectos de la máquina. ```bash # Claude Code npx -y skills add samber/cc-skills-golang --skill golang-spf13-viper --agent claude-code # Cursor npx -y skills add samber/cc-skills-golang --skill golang-spf13-viper --agent cursor # Codex npx -y skills add samber/cc-skills-golang --skill golang-spf13-viper --agent codex # Gemini CLI npx -y skills add samber/cc-skills-golang --skill golang-spf13-viper --agent gemini # Windsurf npx -y skills add samber/cc-skills-golang --skill golang-spf13-viper --agent windsurf # Cline npx -y skills add samber/cc-skills-golang --skill golang-spf13-viper --agent cline ``` ## Qué hace - Guía el uso de spf13/viper para resolución de configuración en capas (flag > env > archivo > KV > default) - Explica BindPFlag/BindPFlags para enlazar flags de cobra a viper en init() o PersistentPreRunE - Cubre SetEnvPrefix + SetEnvKeyReplacer + AutomaticEnv para el mapeo correcto de variables de entorno - Detalla Unmarshal/UnmarshalKey con tags mapstructure y el uso de viper.New() para aislar tests - Explica WatchConfig + OnConfigChange para recarga en caliente y sus trampas con fsnotify ## Cuándo usarla - Se usa o adopta spf13/viper en el proyecto - El código importa github.com/spf13/viper ## Cuándo no - Para estructura de comandos CLI con cobra, se remite al skill golang-spf13-cobra - Para arquitectura general de CLI, se remite al skill golang-cli ## Qué la activa - "Ayúdame a configurar viper con precedencia de flags, env y archivo" - "¿Por qué mi variable de entorno MYAPP_DATABASE_HOST no se resuelve en viper?" - "Necesito recargar la configuración en caliente con WatchConfig" - "Cómo aíslo viper en mis tests unitarios" ## Antes de instalar - Requiere el binario `go` y el paquete github.com/spf13/viper instalado (`go get github.com/spf13/viper@latest`). ## Archivos - SKILL.md — 10 KB - evals/evals.json — 21 KB - references/binding-and-env.md — 4 KB - references/sources-and-formats.md — 3 KB - references/testing-and-isolation.md — 4 KB - references/unmarshal.md — 4 KB - references/watch-and-reload.md — 3 KB ## SKILL.md Reproducido tal cual desde samber/cc-skills-golang bajo MIT. Esta sección es el documento original y está en inglés. **Persona:** You are a Go engineer who treats configuration as a layered system. Flag beats env beats file beats default — and you bind every key so all four layers stay reachable through one API. # Using spf13/viper for layered configuration in Go Viper resolves configuration values from multiple sources in a fixed precedence order. It has no user-facing surface — it doesn't define commands or flags. Its job is to answer "what is the value of key X right now?" by walking its source layers from highest to lowest priority. **Official Resources:** - [pkg.go.dev/github.com/spf13/viper](https://pkg.go.dev/github.com/spf13/viper) - [github.com/spf13/viper](https://github.com/spf13/viper) This skill is not exhaustive. Please refer to library documentation and code examples for more information. For Go package docs, symbols, versions, importers, and known vulnerabilities, → See `samber/cc-skills-golang@golang-pkg-go-dev` skill (`godig`) — prefer it over Context7 for Go package facts. To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See `samber/cc-skills-golang@golang-gopls` skill (`gopls`). Context7 remains a fallback for docs not indexed on pkg.go.dev. ```bash go get github.com/spf13/viper@latest ``` ## Viper vs. cobra Cobra owns the command tree — subcommands, flags, arg validation, completions. Viper owns configuration resolution — it answers "what is the value of key X?" by walking its source layers. Viper has no user-facing surface; it is purely a key-value resolver. Use cobra alone for flag-only CLIs; viper alone for config-file daemons; both when you need both, binding flags at `PersistentPreRunE` via `BindPFlag`. → See `samber/cc-skills-golang@golang-spf13-cobra` for the cobra side of this integration. ## The precedence pipeline Viper resolves a key by walking sources in this order (first set value wins): ``` 1. explicit Set() — viper.Set("key", val) highest priority 2. flag — bound pflag.Flag 3. env var — BindEnv / AutomaticEnv 4. config file — ReadInConfig / MergeInConfig 5. KV remote — etcd / Consul 6. default — viper.SetDefault("key", val) lowest priority ``` This pipeline is fixed and cannot be reordered. Understanding it prevents most viper bugs: a key that "should" come from a config file may be shadowed by an env var or a flag with a default value. ## Sources and config files ```go viper.SetConfigName("config") viper.AddConfigPath("$HOME/.myapp") if err := viper.ReadInConfig(); err != nil { var notFound *viper.ConfigFileNotFoundError if !errors.As(err, ¬Found) { return fmt.Errorf("reading config: %w", err) // propagate real errors only } } ``` `ConfigFileNotFoundError` must be handled gracefully — config files are usually optional. An unhandled error from a missing file crashes programs that are perfectly valid when run with only flags or env vars. For supported formats (JSON, TOML, YAML, HCL, INI, properties), `MergeInConfig`, and remote KV, see [sources-and-formats.md](references/sources-and-formats.md). ## Env binding and key replacers This is the highest-bug-density area in viper. All three settings must be wired together — missing any one breaks nested key resolution: ```go // ✓ Good — all three wired together at startup viper.SetEnvPrefix("MYAPP") // prevent collisions: PORT → MYAPP_PORT viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) // database.host → MYAPP_DATABASE_HOST viper.AutomaticEnv() // ✗ Bad — without SetEnvKeyReplacer, viper looks for MYAPP_DATABASE.HOST (dot preserved) ``` For `BindEnv`, `AllowEmptyEnv`, and env-vs-default interaction, see [binding-and-env.md](references/binding-and-env.md). ## Flag binding (the cobra seam) Bind cobra flags to viper in `init()` or `PersistentPreRunE` — never in `RunE` (config loading in `PersistentPreRunE` already ran before `RunE`, so bindings set in `RunE` are missed): ```go func init() { rootCmd.PersistentFlags().Int("port", 8080, "listen port") viper.BindPFlag("port", rootCmd.PersistentFlags().Lookup("port")) // viper.BindPFlags(cmd.Flags()) — bind an entire FlagSet at once } ``` For `AllowEmptyEnv` and flag/env interaction details, see [binding-and-env.md](references/binding-and-env.md). ## Unmarshaling into structs `viper.Unmarshal` maps the resolved configuration into a struct using `mapstructure`: ```go type Config struct { Port int `mapstructure:"port"` Database struct { MaxConn int `mapstructure:"max_conn"` // explicit tag: mapstructure won't convert underscore→camelCase } `mapstructure:"database"` } var cfg Config viper.Unmarshal(&cfg) ``` **Always use `mapstructure` tags** — implicit mapping is fragile for nested structs and underscore-named fields. Prefer `UnmarshalKey("database", &dbCfg)` over `Sub("database").Unmarshal` — it avoids the nil-check `Sub` requires when the key is missing. For `time.Duration` / `net.IP` / slice decoders and custom `DecodeHook` registration, see [unmarshal.md](references/unmarshal.md). ## Sub-trees `viper.Sub("database")` returns a new `*viper.Viper` scoped to the prefix, or **nil** if the key does not exist — always nil-check before calling methods on the result. Prefer `UnmarshalKey("database", &dbCfg)` which avoids the nil risk entirely. ## Hot reload ```go viper.WatchConfig() viper.OnConfigChange(func(e fsnotify.Event) { /* re-apply changed values */ }) ``` `WatchConfig` uses fsnotify and watches inodes. Editors that write atomically via rename (vim, neovim) replace the inode — the callback may not fire. Test hot-reload with `echo >> config.yaml`, not editor saves. For race-safe reload patterns, see [watch-and-reload.md](references/watch-and-reload.md). ## Test isolation **Never use the global viper in tests** — state leaks across test cases. Use `viper.New()` per test so each instance is isolated: ```go v := viper.New() v.SetConfigFile("testdata/config.yaml") require.NoError(t, v.ReadInConfig()) ``` For `t.Setenv` interactions and `Reset()` limitations, see [testing-and-isolation.md](references/testing-and-isolation.md). ## Best Practices 1. **Set prefix + key replacer + AutomaticEnv together** — missing any one causes nested env keys to silently not resolve (`database.host` → `DATABASE.HOST` instead of `DATABASE_HOST`). 2. **Handle `ConfigFileNotFoundError` gracefully** — a missing config file should not crash a service that runs with only flags and env vars. 3. **Always use `mapstructure` tags on config structs** — implicit mapping silently misses nested and underscore-named fields. 4. **Use `viper.New()` in tests, never the global** — the global accumulates state across test runs; per-test instances are isolated. 5. **Bind flags before `Execute()`** — binding in `RunE` is too late; cobra parses flags before `RunE` runs. ## Common Mistakes | Mistake | Why it fails | Fix | | --- | --- | --- | | `AutomaticEnv` without `SetEnvKeyReplacer` | `database.host` looks for `MYAPP_DATABASE.HOST` (dot preserved) — never matches | Add `SetEnvKeyReplacer(strings.NewReplacer(".", "_"))` before `AutomaticEnv` | | No `mapstructure` tags on struct fields | Silently misses nested and underscore-named fields | Add `mapstructure:"key_name"` to every field | | Using global viper in tests | State from one test contaminates the next, causing flaky ordering | Create `viper.New()` per test | | Missing `ConfigFileNotFoundError` check | Missing config file crashes a service that should run on flags/env alone | `errors.As(err, ¬Found)` — only propagate non-not-found errors | ## Further Reading - [sources-and-formats.md](references/sources-and-formats.md) — supported file formats, multi-path search, MergeInConfig, remote KV (etcd/Consul) - [binding-and-env.md](references/binding-and-env.md) — BindEnv, AutomaticEnv, SetEnvPrefix, SetEnvKeyReplacer, AllowEmptyEnv, timing rules - [unmarshal.md](references/unmarshal.md) — Unmarshal, UnmarshalKey, mapstructure tags, custom DecodeHooks (Duration, IP, slice) - [watch-and-reload.md](references/watch-and-reload.md) — WatchConfig, OnConfigChange, fsnotify caveats, atomic-rename trap, race-safe patterns - [testing-and-isolation.md](references/testing-and-isolation.md) — viper.New() per test, t.Setenv interactions, Reset() limitations, snapshot/restore ## Cross-References - → See `samber/cc-skills-golang@golang-cli` skill for general CLI architecture — project layout, exit codes, signal handling, cobra+viper integration - → See `samber/cc-skills-golang@golang-spf13-cobra` skill for the cobra side of this integration (flag definition and binding) - → See `samber/cc-skills-golang@golang-testing` skill for general Go testing patterns If you encounter a bug or unexpected behavior in spf13/viper, open an issue at . ## Dónde encaja - Categoría: [Herramientas para desarrolladores](https://skillsagentes.com/categorias/herramientas-desarrollo.md) — Skills que cambian cómo tu agente escribe, revisa y despliega código. - Creador: [samber](https://skillsagentes.com/creators/samber.md) — 0 skills en el directorio - [Todas las skills](https://skillsagentes.com/skills.md) - [Ranking de instalaciones](https://skillsagentes.com/ranking.md) ## Otras skills del mismo repositorio - [Golang Lint](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-lint.md): Buenas prácticas de linting y configuración de golangci-lint para proyectos Golang: ejecutar linters, configurar .golangci.yml, suprimir avisos con nolint, interpretar salidas y elegir linters. - [Golang How To](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-how-to.md): Orquestador de skills de Golang, siempre activo en cualquier tarea de código, revisión, debug o setup: carga las skills más relevantes de samber/cc-skills-golang, a menudo varias a la vez. - [Golang Benchmark](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-benchmark.md): Benchmarking, profiling y medición de rendimiento en Golang: escribir y comparar benchmarks, perfilar con pprof, analizar con benchstat y detectar regresiones en CI. - [Golang Testing](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-testing.md): Tests de Golang listos para producción: table-driven, suites y mocks con testify, tests paralelos, fuzzing, fixtures, detección de fugas de goroutines con goleak, snapshot testing, cobertura, tests de integración. - [Golang Performance](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-performance.md): Patrones y metodología de optimización de rendimiento en Golang: si hay cuello de botella X, aplica el patrón Y, una vez que profiling o benchmarks ya lo identificaron. ## Skills relacionadas - [Golang Samber Hot](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-samber-hot.md): Caché en memoria en Golang con samber/hot: algoritmos de expulsión (LRU, LFU, TinyLFU, S3FIFO, ARC, TwoQueue, SIEVE, FIFO), TTL, loaders, sharding y métricas Prometheus. - [Golang Samber Lo](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-samber-lo.md): Helpers funcionales para Go con samber/lo: 500+ funciones genéricas type-safe para slices, maps, canales, strings, math, tuplas y concurrencia. - [Golang Samber Mo](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-samber-mo.md): Tipos monádicos para Golang con samber/mo — Option, Result, Either, Future, IO, Task y State para valores nulos seguros, manejo de errores y composición funcional. - [Golang Samber Slog](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-samber-slog.md): Extensiones de logging estructurado para Go con paquetes samber/slog-****: pipelines multi-handler, sampling, formateo de atributos, middleware HTTP y enrutamiento a backends como Datadog, Sentry o Loki. - [Golang Structs Interfaces](https://skillsagentes.com/skills/samber/cc-skills-golang/golang-structs-interfaces.md): Patrones de diseño de structs e interfaces en Golang: composición, embedding, aserciones de tipo, interfaces pequeñas, DI vía interfaces, tags de campo y receptores puntero vs valor. --- Skills Agentes · [Índice de páginas en markdown](https://skillsagentes.com/sitemap.md) · [Inicio](https://skillsagentes.com/index.md)