Como trabalhar com configuração em Go: do caos à ordem
\n\nTodo desenvolvedor Go, mais cedo ou mais tarde, enfrenta a pergunta: "Como armazenar e carregar corretamente as configurações do aplicativo?" O tópico é tão popular que, no Stack Overflow, a pergunta correspondente acumulou milhares de visualizações e dezenas de respostas. E isso não é por acaso: a forma como você organiza a configuração determina o quão fácil será implantar o aplicativo, testá-lo e adaptá-lo a diferentes ambientes.
\n\nNeste artigo, analisaremos as perguntas mais frequentes sobre configuração em Go, veremos exemplos reais de código e desenvolveremos uma abordagem que o salvará de dores de cabeça durante a implantação.
\n\nPor que a configuração é um problema?
\n\nImagine: você escreveu um aplicativo excelente. Ele funciona localmente, os testes passam, os colegas aplaudem. Mas quando chega a hora de implantar em staging e produção, começa o inferno. Bancos de dados diferentes, chaves de API diferentes, níveis de log diferentes. E então você percebe que codificar configurações fixas é uma má ideia.
\n\nGo oferece ferramentas flexíveis para trabalhar com configuração, mas iniciantes frequentemente cometem os mesmos erros: usam variáveis globais, confundem formatos, esquecem das variáveis de ambiente. Vamos entender como fazer corretamente.
\n\nPergunta frequente nº1: JSON ou YAML?
\n\nA primeira pergunta que surge é: "Em qual formato armazenar a configuração?" A resposta depende do contexto, mas existem recomendações gerais.
\n\nJSON — simples e nativo
\n\nGo já sabe trabalhar com JSON nativamente através do pacote encoding/json. É uma excelente escolha para microsserviços e gateways de API.
// config.json\n{\n "port": 8080,\n "database": {\n "host": "localhost",\n "port": 5432,\n "name": "myapp"\n },\n "log_level": "info"\n}\n\n// main.go\npackage main\n\nimport (\n "encoding/json"\n "fmt"\n "os"\n)\n\ntype Config struct {\n Port int `json:"port"`\n Database Database `json:"database"`\n LogLevel string `json:"log_level"`\n}\n\ntype Database struct {\n Host string `json:"host"`\n Port int `json:"port"`\n Name string `json:"name"`\n}\n\nfunc main() {\n file, _ := os.Open("config.json")\n defer file.Close()\n \n var config Config\n json.NewDecoder(file).Decode(&config)\n \n fmt.Printf("Iniciando servidor na porta %d\\n", config.Port)\n fmt.Printf("Conectando ao banco de dados %s:%d/%s\\n", \n config.Database.Host, \n config.Database.Port, \n config.Database.Name)\n}\n\nYAML — legibilidade acima de tudo
\n\nPara configurações complexas com muitos campos aninhados, o YAML é mais conveniente. No entanto, será necessária uma biblioteca de terceiros — por exemplo, gopkg.in/yaml.v3.
// config.yaml\nserver:\n port: 8080\n timeout: 30s\n\ndatabase:\n host: localhost\n port: 5432\n name: myapp\n\nlogging:\n level: info\n format: json\n\npackage main\n\nimport (\n "fmt"\n "gopkg.in/yaml.v3"\n "os"\n)\n\ntype Config struct {\n Server ServerConfig `yaml:"server"`\n Database DatabaseConfig `yaml:"database"`\n Logging LoggingConfig `yaml:"logging"`\n}\n\nfunc main() {\n data, _ := os.ReadFile("config.yaml")\n var config Config\n yaml.Unmarshal(data, &config)\n \n fmt.Printf("Timeout do servidor: %s\\n", config.Server.Timeout)\n}\n\nDica: Use YAML para configurações que serão lidas por humanos e JSON para troca de dados entre máquinas.
\n\nPergunta frequente nº2: E as variáveis de ambiente?
\n\nNo desenvolvimento moderno (especialmente em contêineres e Kubernetes), as variáveis de ambiente se tornaram o padrão de facto. Elas permitem alterar facilmente as configurações sem precisar recompilar o aplicativo.
\n\nGo suporta trabalho com env através de os.Getenv() e os.LookupEnv(). Mas analisar manualmente cada variável é tedioso e