Em Go, a qualidade do código não é apenas uma boa prática: é um valor cultural. A linguagem foi projetada para ser simples e legível, e as ferramentas oficiais e da comunidade reforçam isso. Nesta aula, você vai dominar três ferramentas essenciais: go vet, que analisa o código em busca de suspeitas de erros; gofmt, que padroniza a formatação; e golangci-lint, uma suíte completa de linters que eleva a qualidade do seu projeto a um nível profissional.

Vamos começar com os conceitos e depois mergulhar em exemplos práticos, mostrando como usar cada ferramenta no terminal, como interpretar suas saídas e como integrá-las ao seu fluxo de trabalho, seja localmente ou em CI/CD.

go vet

go vet é uma ferramenta oficial da linguagem que examina o código-fonte e relata construções suspeitas, como chamadas de funções com argumentos incorretos, problemas em operações de formatação, ou uso indevido de ponteiros. Ela não é um linter de estilo; seu foco é encontrar bugs reais que podem passar despercebidos em testes.

Para usar, basta executar go vet ./... no diretório do seu módulo. Ele analisa todos os pacotes do diretório atual e subdiretórios. Vamos ver um exemplo de código com problemas que o go vet detecta:

package main

import "fmt"

func main() {
    // Erro: %d espera um inteiro, mas recebemos uma string
    fmt.Printf("%d", "olá")

    // Erro: copiar uma struct com sync.Mutex (go vet detecta)
    type Resource struct { mu sync.Mutex; value int }
    r1 := Resource{value: 1}
    r2 := r1
    _ = r2
}

Se você rodar go vet nesse código, verá um aviso como: fmt.Printf format %d has arg "olá" of wrong type string. Isso mostra como a ferramenta ajuda a evitar erros comuns.

Além disso, go vet é executado automaticamente como parte do go test e do go build (desde a versão 1.10). Isso significa que você não precisa rodá-lo separadamente em muitos fluxos, mas é sempre bom fazer uma verificação explícita.

gofmt

gofmt é o formatador oficial de código Go. Ele garante que todos os programas Go tenham a mesma formatação, eliminando discussões sobre indentação, alinhamento e espaçamento. A comunidade Go adotou o estilo do gofmt como padrão, e é praticamente obrigatório em qualquer projeto.

Para formatar um arquivo, use gofmt -w arquivo.go. A opção -w escreve o resultado de volta no arquivo. Você também pode usar -d para mostrar as diferenças sem modificar o arquivo, e -e para reportar todos os erros (não apenas os primeiros 10).

Aqui está um exemplo de código mal formatado:

package main

import "fmt"

func main(){
fmt.Println("olá")
}

Após rodar gofmt -w main.go, o resultado será:

package main

import "fmt"

func main() {
	fmt.Println("olá")
}

Note que o gofmt também corrige a indentação, os espaços e até mesmo quebras de linha. Muitos editores e IDEs têm suporte a gofmt on save, o que garante que seu código esteja sempre formatado.

Uma alternativa mais moderna é go fmt, que é um comando que aplica gofmt a todos os arquivos do pacote atual. No entanto, gofmt é mais flexível e é a ferramenta subjacente.

golangci-lint (visão geral)

golangci-lint é uma ferramenta da comunidade que agrega dezenas de linters em um único binário. Ela combina análises estáticas, detecção de erros comuns, problemas de estilo e até mesmo questões de desempenho. É amplamente usada em projetos open source e em ambientes corporativos.

Para instalar, você pode baixar o binário da página de releases ou usar go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest. Depois de instalado, você pode rodar golangci-lint run no diretório do seu projeto.

Um exemplo de código que o golangci-lint pode detectar:

package main

import "fmt"

func main() {
	var x int
	fmt.Println(x)  // x é sempre zero, mas não é um erro

	// Exemplo de problema: errcheck - erro não tratado
	f, _ := os.Open("arquivo.txt")
	defer f.Close()
}

O linter errcheck reclamaria que o erro de os.Open não está sendo tratado. Outros linters populares incluem golint (estilo), govet (que já vimos), staticcheck (análise mais profunda) e gosec (segurança).

O golangci-lint é altamente configurável. Você pode criar um arquivo .golangci.yml na raiz do projeto para habilitar ou desabilitar linters, definir limites de complexidade e muito mais. Recomendo começar com a configuração padrão e ir ajustando conforme a necessidade.

Integração

Integrar essas ferramentas ao seu fluxo de trabalho é essencial para manter a qualidade do código de forma contínua. Aqui estão algumas práticas:

  • Editor/IDE: Configure seu editor para rodar gofmt ao salvar (a maioria dos editores Go já faz isso por padrão). Para golangci-lint, use a extensão oficial do VS Code ou o suporte nativo do GoLand.
  • Pre-commit hooks: Use ferramentas como pre-commit ou husky para rodar go vet e golangci-lint antes de cada commit. Isso evita que código problemático entre no repositório.
  • CI/CD: Em pipelines como GitHub Actions, GitLab CI ou Jenkins, adicione etapas para executar go vet ./... e golangci-lint run. Assim, qualquer pull request que não passe nas verificações é bloqueado.

Exemplo de passo em um GitHub Action:

- name: Run go vet
  run: go vet ./...

- name: Run golangci-lint
  uses: golangci/golangci-lint-action@v3

Além disso, você pode usar go fmt no CI para garantir que o código está formatado: test -z $(go fmt ./...). Isso falha se houver arquivos não formatados.

Outra integração útil é usar golangci-lint como um hook no seu editor, para que você receba feedback imediato enquanto escreve o código.

Boas práticas e observações finais

Adotar essas ferramentas desde o início do projeto é muito mais fácil do que corrigir um código legado. Mantenha seu código sempre formatado com gofmt, execute go vet regularmente e use golangci-lint como sua rede de segurança. Lembre-se de que essas ferramentas não substituem testes, mas complementam sua estratégia de qualidade.

Outra dica: leia a saída do golangci-lint com atenção. Muitas vezes ela aponta problemas que você não tinha percebido, como uso de variáveis não utilizadas ou códigos mortos. Corrija-os imediatamente para manter o código limpo e sustentável.

Referências

Exercícios

  1. Escreva um programa Go que contenha um erro que o go vet detecte (por exemplo, um formato de printf incorreto). Execute go vet no programa e explique a mensagem de erro.

✓ Resposta: Um exemplo simples:
package main

import "fmt"

func main() {
    fmt.Printf("%d", "erro")
}
Ao rodar go vet, a saída será algo como: fmt.Printf format %d has arg "erro" of wrong type string. O erro indica que o verbo %d espera um valor inteiro, mas foi passada uma string.
  1. Crie um arquivo Go com formatação incorreta (por exemplo, sem indentação adequada). Use gofmt -d para ver as diferenças e depois gofmt -w para formatar. Mostre o antes e o depois.

✓ Resposta: Antes:
package main
func main(){
fmt.Println("oi")
}
Depois de gofmt -w:
package main

func main() {
	fmt.Println("oi")
}
As diferenças incluem a adição de uma linha em branco após o package, espaçamento adequado e indentação com tab.
  1. Instale o golangci-lint e rode no seu projeto atual (ou em um exemplo). Liste os linters que ele executa por padrão e explique o que um deles faz.

✓ Resposta: Após rodar golangci-lint run, você verá a lista de linters ativos na saída, como errcheck, govet, ineffassign, staticcheck, unused, etc. Por exemplo, errcheck verifica se os erros retornados por funções estão sendo tratados, evitando que erros sejam ignorados silenciosamente.
  1. Configure uma integração de CI (ex.: GitHub Actions) para um projeto Go que execute go vet e golangci-lint. Escreva o passo YAML necessário.

✓ Resposta: Exemplo de workflow:
name: CI
on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.22'
      - name: Run go vet
        run: go vet ./...
      - name: Run golangci-lint
        uses: golangci/golangci-lint-action@v3
  1. Pesquise sobre o linter staticcheck (que é incluído no golangci-lint). Dê um exemplo de um problema que ele detecta e como corrigi-lo.

✓ Resposta: O staticcheck detecta, por exemplo, uso de variáveis não utilizadas ou importações desnecessárias. Exemplo:
package main

import "fmt"

func main() {
    x := 10
    fmt.Println("oi")
}
O staticcheck reporta que x é declarado e não usado. A correção é remover a linha x := 10.