Por que boas práticas importam no dia a dia
Em Go, “boas práticas” não são um conjunto de regras estéticas: elas afetam legibilidade, manutenção, revisão de código e a facilidade de integrar ferramentas automáticas (formatadores, linters e geradores de documentação). O ecossistema de Go favorece padrões consistentes: código formatado automaticamente, nomes simples, comentários que viram documentação e checagens estáticas que pegam erros comuns cedo.
Formatação: gofmt sempre (e sem exceções)
O que é e por que usar
gofmt é o formatador oficial. Ele padroniza espaçamento, quebras, alinhamentos e estilo geral. A regra prática é: se o código não está em gofmt, ele está “fora do padrão” do time e do ecossistema.
Passo a passo prático
- Antes de commitar, rode o formatador no repositório:
gofmt -w . - Para checar se há arquivos não formatados (útil em CI):
test -z "$(gofmt -l .)" - Configure o editor para formatar ao salvar (a maioria dos plugins de Go faz isso automaticamente).
Dicas rápidas
- Evite “formatar manualmente”: deixe o gofmt decidir.
- Se um diff ficou grande “só por formatação”, separe em um commit exclusivo de gofmt para facilitar revisão.
Nomes idiomáticos: curtos, claros e consistentes
Princípios práticos
- Curto, mas significativo: nomes em Go tendem a ser menores, principalmente em escopos pequenos.
- Contexto conta: em métodos, o nome do receiver já dá contexto; evite redundância.
- Exportação é contrato: nomes exportados (iniciando com maiúscula) viram API pública do pacote, então precisam ser estáveis e bem documentados.
Exemplos de melhoria de nomes
// Ruim: redundante e verboso dentro do pacote userstore (por exemplo). func (s *Store) StoreUserInDatabase(u User) error { ... } // Melhor: o contexto do pacote e do receiver já ajudam. func (s *Store) Save(u User) error { ... } // Ruim: abreviações obscuras. var cfgMgr *ConfigurationManager // Melhor: abreviações comuns e claras, ou nome direto. var cfg *ConfigConvenções úteis
- Interfaces pequenas costumam terminar com
-erquando fazem sentido:Reader,Writer,Closer. - Evite gagueira (stuttering): se o pacote é
httpclient, não crietype HTTPClientClient. - Use nomes consistentes para erros e variáveis:
errpara erro,ctxpara contexto,req/resppara request/response.
Comentários no estilo GoDoc (e quando comentar)
O que vira documentação
Em Go, comentários imediatamente antes de um identificador exportado (tipo, função, método, constante, variável) são interpretados como documentação (GoDoc). Isso cria uma disciplina: se é exportado, deve ser documentado.
Regras de ouro
- Comentário de item exportado deve começar com o nome do item.
- Explique o que e por que; evite repetir o óbvio do código.
- Prefira frases completas e objetivas.
Exemplos (bom vs ruim)
// Ruim: não começa com o nome e não diz nada útil. // Faz o parse do arquivo. func Parse(path string) (*Config, error) { ... } // Bom: começa com Parse e descreve comportamento e erros. // Parse carrega e valida um arquivo de configuração no formato TOML. // Retorna erro se o arquivo não existir, não puder ser lido ou se a validação falhar. func Parse(path string) (*Config, error) { ... }Comentários de pacote
Documente o pacote com um comentário no arquivo doc.go (ou no topo de um arquivo do pacote), descrevendo propósito, responsabilidades e exemplos de uso. Isso ajuda quem chega no pacote pela primeira vez.
// Package config fornece carregamento e validação de configurações da aplicação. // // O pacote suporta: // - leitura de arquivo local // - valores padrão // - validação de campos obrigatórios // // Exemplo: // cfg, err := config.Parse("./config.toml") // if err != nil { ... } // fmt.Println(cfg.Port) package configDocumentação exportada e exemplos de uso (Examples)
Quando criar exemplos
Exemplos são especialmente úteis para APIs pequenas e reutilizáveis (funções de parsing, validação, helpers de HTTP, etc.). Em Go, exemplos podem ser escritos como testes de exemplo e também servem como documentação executável.
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
Passo a passo: criando um Example
- Crie um arquivo
example_test.gono mesmo pacote. - Escreva uma função
ExampleXxxque imprime algo determinístico. - Inclua o comentário
// Output:com a saída esperada.
package config import "fmt" func ExampleParse() { cfg, err := Parse("./testdata/app.toml") if err != nil { fmt.Println("erro") return } fmt.Println(cfg.Port) // Output: 8080 }Esse tipo de exemplo ajuda revisores e usuários do pacote a entenderem o uso correto sem ler a implementação.
README técnico mínimo (para repositórios e projetos do curso)
Objetivo do README
Um README técnico mínimo permite que alguém rode, teste e entenda o projeto em poucos minutos. Ele deve ser direto e orientado a tarefas.
Estrutura recomendada
- O que é: 1–2 frases sobre o propósito do projeto.
- Requisitos: versão do Go e dependências externas (se houver).
- Como rodar: comandos exatos (com exemplos de flags/variáveis).
- Como testar: comando(s) de teste.
- Configuração: como configurar variáveis de ambiente/arquivos.
- Estrutura de pastas: visão rápida (opcional, mas útil).
Modelo prático de README (adaptável)
# Projeto do Curso (nome) Serviço em Go para (descrever em 1 linha). ## Requisitos - Go 1.22+ ## Rodar go run ./cmd/app ## Testes go test ./... ## Configuração Variáveis de ambiente: - APP_PORT: porta HTTP (padrão: 8080) Exemplo: APP_PORT=8080 go run ./cmd/app ## Estrutura - cmd/app: entrypoint - internal/...: regras e implementação interna - pkg/...: bibliotecas reutilizáveis (se aplicável)Checagens com go vet e cuidados comuns
Além de formatar e documentar, é importante rodar checagens estáticas para detectar bugs comuns. O go vet analisa padrões suspeitos que passam pelo compilador, mas podem indicar erro lógico.
Passo a passo: rodando vet
- No projeto inteiro:
go vet ./... - Em CI, trate como etapa obrigatória antes de build/release.
Problemas comuns e como evitar
1) Shadowing (sombreamento) de variáveis
Shadowing acontece quando você usa := e cria uma nova variável com o mesmo nome em um escopo interno, sem perceber. Isso pode fazer você checar/retornar o err errado ou manter valores antigos.
// Exemplo de risco: err é recriado dentro do if. err := doA() if cond { result, err := doB() // err novo (shadow) _ = result if err != nil { return err } } // aqui, err é o doA(), não o doB()Preferência prática: quando você já tem a variável, use = em vez de :=.
err := doA() if cond { var result string result, err = doB() // reaproveita err _ = result if err != nil { return err } }Observação: go vet não pega todo shadowing. Uma prática comum é adotar um linter específico para isso (por exemplo, shadow via golangci-lint), mas mesmo sem ele, revisar o uso de := em blocos internos já reduz muito o problema.
2) Uso de fmt vs log
fmt é ótimo para saída formatada (CLI, exemplos, prints pontuais). Para logs de aplicação, prefira log (ou um logger estruturado adotado pelo projeto), porque logs precisam de timestamp, contexto e destino consistente (stdout/stderr, arquivos, etc.).
- Use
fmt.Printf/fmt.Printlnem ferramentas simples e exemplos. - Use
log.Printf/log.Printlnpara eventos de runtime (início do servidor, falhas, retries). - Evite misturar os dois sem critério: isso dificulta observabilidade e testes.
// CLI/exemplo: ok fmt.Println("processando...") // Serviço: melhor como log log.Printf("iniciando servidor na porta %d", port)3) Erros ignorados
Ignorar erro em Go quase sempre vira bug silencioso. Se você realmente quer ignorar, faça isso explicitamente e com justificativa (comentário curto), para deixar claro que foi uma decisão.
// Ruim: erro descartado sem intenção clara. f.Write(data) // Melhor: tratar o erro. if _, err := f.Write(data); err != nil { return err } // Se for intencional (raro), explicite: // Ignoramos o erro porque o buffer é best-effort e falhas não afetam o fluxo principal. _, _ = f.Write(data)4) Defer em loop e vazamento de recursos
Um erro comum é usar defer dentro de loops que abrem recursos, acumulando defers e segurando recursos até o fim da função.
// Risco: muitos arquivos ficam abertos até o final. for _, p := range paths { f, err := os.Open(p) if err != nil { return err } defer f.Close() // ... }Alternativas: fechar explicitamente ao final de cada iteração, ou encapsular a iteração em uma função.
for _, p := range paths { if err := func() error { f, err := os.Open(p) if err != nil { return err } defer f.Close() // ... return nil }(); err != nil { return err } }5) Mensagens e contexto em logs/erros
Mesmo quando o erro já é “bom”, logs e retornos devem carregar contexto suficiente (qual operação falhou, qual identificador, qual arquivo/porta). Evite logs genéricos como “erro ao processar”.
// Melhor: inclui contexto. if err != nil { return fmt.Errorf("carregando config %q: %w", path, err) }Aplicando ao projeto do curso: revisão objetiva (checklist de melhorias)
A seguir, uma lista prática para você aplicar no projeto do curso (estrutura com cmd, internal, pacotes e testes). Ajuste os nomes conforme o seu repositório.
1) Formatação e consistência
- Rodar
gofmt -w .em todo o repositório e garantir que o CI falhe se houver arquivos não formatados. - Evitar alinhamentos “manuais” e espaçamentos fora do padrão.
2) Nomes e APIs exportadas
- Revisar itens exportados: tudo que está com letra maiúscula precisa ser realmente público (API) ou pode virar não exportado.
- Renomear funções/métodos verbosos para nomes mais diretos (
Save,Load,Parse,New), mantendo clareza pelo contexto do pacote. - Padronizar nomes recorrentes:
ctx,err,req,resp,cfg.
3) Comentários GoDoc
- Adicionar comentário GoDoc em todos os tipos/funções exportados, começando pelo nome do identificador.
- Criar documentação de pacote (ex.:
internal/config/doc.go,internal/http/doc.go) descrevendo responsabilidades e limites. - Remover comentários redundantes que apenas repetem o código.
4) Exemplos executáveis
- Adicionar ao menos 1–2
ExampleXxxpara pacotes centrais (ex.: parse de config, criação do cliente, validação), com// Output:determinístico. - Garantir que exemplos não dependam de rede/tempo; usar
testdataquando necessário.
5) README técnico mínimo
- Incluir comandos exatos para rodar:
go run ./cmd/...(com variáveis/flags usadas no projeto). - Incluir comando de testes:
go test ./.... - Documentar configuração (env/arquivo) e valores padrão.
- Adicionar uma seção curta de estrutura de diretórios para orientar leitura.
6) go vet e armadilhas frequentes
- Rodar
go vet ./...e corrigir alertas antes de avançar. - Revisar pontos com
:=em blocos internos para evitar shadowing deerr. - Trocar
fmt.Printlnporlog.Printfem caminhos de execução do serviço (mantendofmtpara CLI/exemplos). - Procurar e corrigir erros ignorados (busca por
_ =,_, _ =e chamadas sem checagem de retorno onde há erro). - Revisar
deferdentro de loops que abrem recursos, evitando manter recursos abertos por muito tempo.