Nesta aula, vamos explorar as ferramentas essenciais que todo desenvolvedor Go deve dominar para manter a qualidade do código: go vet, gofmt e golangci-lint. Essas ferramentas automatizam a detecção de problemas, padronizam a formatação e aplicam boas práticas, permitindo que você se concentre na lógica do programa em vez de se preocupar com detalhes mecânicos.

Embora o compilador go build já capture muitos erros, ele não detecta problemas sutis de lógica, suspeitas de código ou inconsistências de estilo. É aí que entram as ferramentas de análise estática e formatação. Ao incorporá-las ao seu fluxo de trabalho, você reduz drasticamente a ocorrência de bugs e melhora a legibilidade do código, facilitando a manutenção e a colaboração em equipe.

go vet

O comando go vet é uma ferramenta de análise estática embutida no ecossistema Go. Ela examina o código-fonte em busca de construções suspeitas que podem indicar bugs reais ou potenciais. Ao contrário do compilador, que verifica a sintaxe e os tipos, o go vet procura padrões problemáticos que geralmente escapam da compilação, como chamadas incorretas de fmt.Printf, uso indevido de copy, ou condições que são sempre verdadeiras ou falsas.

Para usar, basta executar go vet ./... no diretório do módulo. Ele percorrerá todos os pacotes e reportará quaisquer problemas encontrados. É uma prática recomendada rodar go vet antes de cada commit ou em um pipeline de CI/CD. A ferramenta é altamente personalizável: você pode habilitar ou desabilitar verificadores específicos com as flags -vet (para go test) ou usar variantes como go vet -vettool para ferramentas externas.

Exemplo de código que o go vet detectaria:

package main

import "fmt"

func main() {
    // Erro: fmt.Printf espera um verbo de formatação, mas o argumento é uma string
    fmt.Printf("Olá, %s", 42)
}

Ao rodar go vet, você verá uma mensagem como fmt.Printf format %s has arg 42 of wrong type int. Isso impede que você cometa erros de formatação que passariam despercebidos na compilação.

gofmt

O gofmt é o formatador oficial de código Go. Ele aplica um estilo consistente de indentação, espaçamento, alinhamento e quebras de linha, seguindo as convenções da comunidade. A grande vantagem é que ele elimina discussões sobre estilo: todo código Go, quando formatado com gofmt, fica idêntico em termos de estilo, o que facilita a leitura e a revisão por outros desenvolvedores.

Para formatar um arquivo, use gofmt -w arquivo.go para sobrescrever o arquivo com a versão formatada. Se preferir apenas visualizar a saída, use gofmt arquivo.go. Em projetos, é comum usar gofmt -l . para listar os arquivos que não estão formatados, permitindo identificar rapidamente o que precisa ser ajustado.

Exemplo de código não formatado:

package main

import "fmt"

func main(){
    fmt.Println("Olá, mundo!")
}

Após executar gofmt -w main.go, o arquivo se torna:

package main

import "fmt"

func main() {
	fmt.Println("Olá, mundo!")
}

Note a indentação com tabulação e o espaço entre main() e a chave. O gofmt também pode ser usado em conjunto com editores de texto, que geralmente o executam automaticamente ao salvar.

golangci-lint (visão geral)

O golangci-lint é uma ferramenta de linting agregada que reúne mais de 100 linters diferentes em um único binário. Ele combina a análise estática do go vet com linters de estilo, complexidade, segurança e desempenho, oferecendo uma solução completa para qualidade de código. É amplamente adotado em projetos open source e em empresas devido à sua flexibilidade e velocidade.

A instalação é simples: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest. Depois, você pode executar golangci-lint run no diretório do seu projeto. Ele analisará todos os arquivos e reportará problemas com a categoria do linter, como errcheck, govet, staticcheck, entre outros. Você pode configurar quais linters habilitar através de um arquivo .golangci.yml, ajustando o comportamento às necessidades do seu time.

Exemplo de configuração básica (.golangci.yml):

linters:
  enable:
    - errcheck
    - govet
    - staticcheck
    - gosimple
    - ineffassign

Com essa configuração, o golangci-lint verificará erros não tratados, problemas de vet, e código ineficiente. Ele também oferece uma opção de autocorreção com --fix, que aplica correções seguras automaticamente, como remover imports não utilizados.

Integração

Para aproveitar ao máximo essas ferramentas, é essencial integrá-las ao seu fluxo de trabalho diário. A maioria dos editores de código (VS Code, GoLand, Vim) possui suporte nativo ou plugins que executam gofmt e go vet automaticamente ao salvar. Isso garante que o código esteja sempre formatado e livre de problemas comuns antes mesmo de você executar os comandos manualmente.

Em ambientes de CI/CD, você pode adicionar etapas para rodar go vet e golangci-lint em cada pull request. Isso cria uma barreira de qualidade que impede a introdução de código problemático no repositório principal. Exemplo de pipeline em GitHub Actions:

name: CI
on: [push, pull_request]
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up Go
        uses: actions/setup-go@v4
        with:
          go-version: '1.22'
      - name: Run go vet
        run: go vet ./...
      - name: Run golangci-lint
        uses: golangci/golangci-lint-action@v3

Essa integração garante que todo código submetido seja verificado automaticamente, reduzindo a carga de revisão manual e melhorando a confiabilidade do projeto.

Boas práticas e observações finais

Além de usar essas ferramentas, é importante adotar uma cultura de qualidade no time. Defina um padrão de formatação e linting no início do projeto e documente as regras. Incentive todos a rodar os comandos localmente antes de enviar código. Lembre-se de que gofmt não é opcional: é o padrão oficial da linguagem, e desviar dele gera ruído desnecessário.

Para go vet, tenha em mente que ele não detecta todos os erros lógicos; é uma ferramenta complementar aos testes. Portanto, combine-o com testes unitários e de integração robustos. O golangci-lint, por sua vez, pode ser customizado para se adequar ao seu projeto, mas evite habilitar linters excessivamente restritivos que gerem muitos falsos positivos. Comece com o conjunto padrão e adicione linters conforme a necessidade.

Finalmente, mantenha suas ferramentas atualizadas. Novas versões do Go trazem melhorias no go vet e no gofmt, e o golangci-lint é atualizado frequentemente com novos linters e correções. Acompanhe os releases e ajuste sua configuração conforme o ecossistema evolui.

Referências

Exercícios

  1. Explique a diferença entre go vet e go build em termos de análise de código.

    ✓ Resposta: go build compila o código e verifica erros de sintaxe e tipos, enquanto go vet realiza uma análise estática mais profunda, procurando por padrões suspeitos que podem indicar bugs lógicos, como chamadas incorretas de funções ou condições tautológicas. Enquanto go build garante que o programa compile, go vet ajuda a prevenir erros que não impedem a compilação, mas podem causar comportamento incorreto em tempo de execução.
  2. Qual é o comando para formatar todos os arquivos .go do diretório atual e subdiretórios?

    ✓ Resposta: O comando é gofmt -w . (ou gofmt -w ./... em módulos) para formatar todos os arquivos .go no diretório atual e subdiretórios, sobrescrevendo-os com a versão formatada.
  3. Como você habilitaria apenas os linters govet e errcheck no golangci-lint?

    ✓ Resposta: Crie um arquivo .golangci.yml com o seguinte conteúdo:
    linters:
      enable:
        - govet
        - errcheck
    
    Depois execute golangci-lint run.
  4. Qual a importância de integrar essas ferramentas no CI/CD?

    ✓ Resposta: Integrar go vet, gofmt e golangci-lint no pipeline de CI/CD garante que todo código submetido seja automaticamente verificado quanto a problemas de estilo, erros comuns e más práticas. Isso promove consistência, detecta problemas cedo e reduz a carga de revisão manual, aumentando a qualidade geral do software.
  5. Escreva um pequeno trecho de código Go que contenha um erro que go vet detectaria e explique qual é o erro.

    ✓ Resposta: Exemplo:
    package main
    
    import "fmt"
    
    func main() {
        x := 10
        fmt.Printf("%d", x) // correto
        fmt.Printf("%s", x) // erro: %s espera string, mas x é int
    }
    
    O erro é que o verbo de formatação %s é usado para um inteiro, o que go vet sinaliza como uma chamada inválida de fmt.Printf.