Por que seu código Go falha e como corrigir isso sem dor de cabeça
\n\nVocê escreveu um código Go que considera perfeito. A compilação foi um sucesso, os testes estão verdes... mas em produção tudo vai pelos ares. Familiar? Bem-vindo ao mundo real do desenvolvimento, onde bugs não são erros de iniciantes, mas parte do crescimento profissional. Hoje vamos abordar a depuração de código Go de forma que você não apenas encontre erros, mas previna seu aparecimento durante a escrita.
\n\nGo é uma linguagem que oferece ao desenvolvedor ferramentas poderosas para depuração diretamente "de fábrica". Mas muitos usam apenas fmt.Println e se perguntam por que suas goroutines se comportam como gatos em telhado quente. Vamos corrigir isso de uma vez por todas.
Técnica nº1: Print inteligente — quando log.Fatal não é necessário
\n\nSim, eu sei o que você vai dizer: "Por que eu preciso do delve se posso imprimir uma variável?" E você estará certo em 80% dos casos. Mas o problema não é fmt.Println, e sim como você o usa.
Migrando de Println para structured logging
\n\nEm vez de espalhar dezenas de prints, use o pacote log ou slog (disponível desde Go 1.21):
package main\n\nimport (\n "log/slog"\n "os"\n)\n\nfunc main() {\n logger := slog.New(slog.NewTextHandler(os.Stdout, nil))\n \n // Em vez de fmt.Println("erro:", err)\n if err := doSomething(); err != nil {\n logger.Error("operação falhou", \n "error", err,\n "module", "data_processor",\n "attempt", 3,\n )\n }\n}\n\n\nEssa abordagem fornece logs estruturados, fáceis de analisar, filtrar e interpretar. Em produção, isso evitará ligações noturnas.
\n\nDica: compilação condicional para logs de debug
\n\nPara não poluir a produção, use build tags:
\n\n//go:build debug\n\npackage main\n\nvar debug = true\n\n\n//go:build !debug\n\npackage main\n\nvar debug = false\n\n\nExecute com a flag -tags debug — obtém logs detalhados. Sem a flag — produção limpa.
Técnica nº2: Delve — seu detetive particular para Go
\n\nSe a depuração com print é uma bicicleta, o Delve é uma Tesla. Este depurador foi criado especificamente para Go e entende goroutines, interfaces e até generics. A instalação é simples:
\n\ngo install github.com/go-delve/delve/cmd/dlv@latest\n\n\nPontos de parada que sabem pensar
\n\nDelve permite definir não apenas breakpoints, mas pontos condicionais:
\n\n(dlv) break main.go:25 if userID == 42\n(dlv) continue\n\n\nO depurador só parará quando userID for igual a 42. Imagine quanto tempo isso economiza ao depurar loops.
Inspeção de goroutines
\n\nO mais temido em Go são deadlocks e race conditions. Delve mostra todas as goroutines ativas:
\n\n(dlv) goroutines\n Goroutine 1 - User: ./main.go:10 main.main (0x109a5b6)\n Goroutine 2 - User: ./main.go:15 main.worker (0x109a6c8)\n* Goroutine 3 - User: ./main.go:22 main.worker (0x109a6c8)\n\n\nO asterisco indica a goroutine atual. Alterne entre elas:
\n\n(dlv) goroutine 2\n(dlv) stack\n\n\nTrabalhando com memória: vendo o que há dentro
\n\nO comando locals mostra todas as variáveis locais, e print exibe seus valores com tipos:
(dlv) locals\ndata = []int len: 100000, cap: 100000\nresult = map[string]int ["error": 5, "success": 99995]\n(dlv) print data[0:5]\n[]int len: 5, cap: 100000, [42, 17, 89, 3, 256]\n\n\nTécnica nº3: Profiling com pprof — encontrando gargalos
\n\nSeu código funciona, mas é lento? pprof é o profiler embutido do Go. Ele mostra onde o programa "engasga" e onde consome memória.
\n\n