Boas práticas de código em Go: formatação, comentários, documentação e lint básico

Capítulo 17

Tempo estimado de leitura: 9 minutos

+ Exercício

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 *Config

Convenções úteis

  • Interfaces pequenas costumam terminar com -er quando fazem sentido: Reader, Writer, Closer.
  • Evite gagueira (stuttering): se o pacote é httpclient, não crie type HTTPClientClient.
  • Use nomes consistentes para erros e variáveis: err para erro, ctx para contexto, req/resp para 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 config

Documentaçã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.

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

Passo a passo: criando um Example

  • Crie um arquivo example_test.go no mesmo pacote.
  • Escreva uma função ExampleXxx que 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.Println em ferramentas simples e exemplos.
  • Use log.Printf/log.Println para 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 ExampleXxx para 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 testdata quando 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 de err.
  • Trocar fmt.Println por log.Printf em caminhos de execução do serviço (mantendo fmt para CLI/exemplos).
  • Procurar e corrigir erros ignorados (busca por _ =, _, _ = e chamadas sem checagem de retorno onde há erro).
  • Revisar defer dentro de loops que abrem recursos, evitando manter recursos abertos por muito tempo.

Agora responda o exercício sobre o conteúdo:

Ao rodar gofmt em um repositório Go, qual prática ajuda a facilitar a revisão quando a formatação gera um diff grande sem alterações de lógica?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Quando o diff fica grande apenas por formatae7e3o, separar em um commit exclusivo de gofmt facilita a revise3o, pois isola mudane7as de estilo das mudane7as de lf3gica.

Próximo capitúlo

Projeto final em Go: aplicação completa com módulos, testes, JSON, I/O e concorrência

Arrow Right Icon
Capa do Ebook gratuito Go (Golang) para Iniciantes: Fundamentos, Concorrência e Estrutura de Projetos
94%

Go (Golang) para Iniciantes: Fundamentos, Concorrência e Estrutura de Projetos

Novo curso

18 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.