Golang

Artigo 42 — Documentação com godoc e boas práticas de comentário Já leu

10 min de leitura

Artigo 42 — Documentação com godoc e boas práticas de comentário
Artigo 42 — Documentação com godoc e boas práticas de comentário Documentação como parte do código Em Go, documentação não é um arquivo separado nem uma

Artigo 42 — Documentação com godoc e boas práticas de comentário

Curso: Dominando Go em 1 Ano Prof. Ricardo Matos Módulo 7 — Ferramentas, CLI e Qualidade


Documentação como parte do código

Em Go, documentação não é um arquivo separado nem uma ferramenta externa — é o próprio código comentado. O godoc extrai comentários diretamente do código fonte e os transforma em documentação navegável, disponível tanto na linha de comando quanto no navegador.

Essa abordagem tem uma consequência poderosa: documentação que não está no código não existe para o godoc. Isso incentiva manter a documentação próxima do que ela descreve — reduzindo a probabilidade de ficar desatualizada.


As regras do godoc

O godoc segue convenções simples e estritas. Comentários de documentação são aqueles imediatamente antes de uma declaração, sem linha em branco entre o comentário e o identificador:

// Package financeiro fornece tipos e operações para gestão financeira,
// incluindo contas bancárias, transferências e relatórios de movimentação.
//
// Uso básico:
//
//  conta, err := financeiro.NovaConta("Ricardo", 1000.00)
//  if err != nil {
//      log.Fatal(err)
//  }
//  conta.Depositar(500.00)
//
// Para mais exemplos, consulte os arquivos _test.go do pacote.
package financeiro

Regra 1: o comentário começa com o nome do identificador. Isso permite que o godoc gere índices e o texto faça sentido quando lido isoladamente.

Regra 2: sem linha em branco entre o comentário e a declaração. Uma linha em branco quebra a associação — o godoc não vai reconhecer o comentário como documentação daquele identificador.

Regra 3: use frases completas. O primeiro parágrafo é exibido nos índices como sumário.


Documentando funções e métodos

// NovaConta cria e retorna uma nova conta bancária para o titular informado.
// O depósito inicial deve ser maior ou igual a zero; valores negativos retornam erro.
//
// O titular é obrigatório — uma string vazia retorna ErrTitularVazio.
//
// Exemplo:
//
//  conta, err := NovaConta("Ana Silva", 500.00)
//  if err != nil {
//      // tratar erro
//  }
func NovaConta(titular string, depositoInicial float64) (*Conta, error) {
    if titular == "" {
        return nil, ErrTitularVazio
    }
    if depositoInicial < 0 {
        return nil, fmt.Errorf("depósito inicial não pode ser negativo: %.2f", depositoInicial)
    }
    return &Conta{
        titular: titular,
        saldo:   depositoInicial,
    }, nil
}

// Depositar adiciona o valor informado ao saldo da conta.
// Retorna ErrValorInvalido se o valor for menor ou igual a zero.
func (c *Conta) Depositar(valor float64) error {
    if valor <= 0 {
        return ErrValorInvalido
    }
    c.mu.Lock()
    defer c.mu.Unlock()
    c.saldo += valor
    return nil
}

// Saldo retorna o saldo atual da conta.
// Esta operação é thread-safe.
func (c *Conta) Saldo() float64 {
    c.mu.RLock()
    defer c.mu.RUnlock()
    return c.saldo
}

Documentando tipos e constantes

// Conta representa uma conta bancária com operações de depósito, saque e transferência.
// Conta é segura para uso concorrente.
type Conta struct {
    titular string
    saldo   float64
    mu      sync.RWMutex
}

// StatusConta representa o estado atual de uma conta bancária.
type StatusConta int

const (
    // StatusAtiva indica que a conta está ativa e operacional.
    StatusAtiva StatusConta = iota

    // StatusBloqueada indica que a conta foi bloqueada temporariamente.
    // Operações de débito são recusadas enquanto a conta estiver bloqueada.
    StatusBloqueada

    // StatusEncerrada indica que a conta foi encerrada definitivamente.
    // Nenhuma operação é permitida em contas encerradas.
    StatusEncerrada
)

// ErrSaldoInsuficiente é retornado quando um saque ou transferência
// excede o saldo disponível na conta.
var ErrSaldoInsuficiente = errors.New("saldo insuficiente")

// ErrTitularVazio é retornado quando o nome do titular é uma string vazia.
var ErrTitularVazio = errors.New("titular não pode ser vazio")

// ErrValorInvalido é retornado quando o valor de uma operação é menor
// ou igual a zero.
var ErrValorInvalido = errors.New("valor deve ser maior que zero")

Formatação especial no godoc

O godoc reconhece alguns padrões de formatação automaticamente:

// Processar executa o pipeline de processamento em três fases:
//
//  1. Validação dos dados de entrada
//  2. Transformação e enriquecimento
//  3. Persistência no banco de dados
//
// Considerações de performance:
//
// A fase de transformação pode ser custosa para entradas grandes.
// Para lotes acima de 10.000 itens, considere usar ProcessarEmLotes.
//
// Exemplos de uso típicos:
//
//  // Processamento simples
//  err := Processar(ctx, dados)
//
//  // Com opções
//  err := Processar(ctx, dados, ComParalelismo(4), ComTimeout(30*time.Second))
//
// Código de exemplo completo em https://pkg.go.dev/meu-pacote#example-Processar
func Processar(ctx context.Context, dados []byte, opts ...Opcao) error {

Blocos de código são indentados com um tab (ou espaços adicionais). O godoc os renderiza como <pre>.

Listas numeradas com o padrão N. são reconhecidas automaticamente desde o Go 1.19.

Links — URLs completas são automaticamente transformadas em hiperlinks.


Exemplos executáveis

Funções de exemplo são verificadas pelo go test e exibidas na documentação:

// arquivo: financeiro/conta_example_test.go
package financeiro_test

import (
    "fmt"
    "log"

    "meu-modulo/financeiro"
)

func ExampleNovaConta() {
    conta, err := financeiro.NovaConta("Ricardo Matos", 1000.00)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Titular: %s\n", conta.Titular())
    fmt.Printf("Saldo: R$ %.2f\n", conta.Saldo())
    // Output:
    // Titular: Ricardo Matos
    // Saldo: R$ 1000.00
}

func ExampleConta_Depositar() {
    conta, _ := financeiro.NovaConta("Ana", 500.00)

    if err := conta.Depositar(250.00); err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Saldo após depósito: R$ %.2f\n", conta.Saldo())
    // Output:
    // Saldo após depósito: R$ 750.00
}

func ExampleConta_Sacar_saldoInsuficiente() {
    conta, _ := financeiro.NovaConta("Carlos", 100.00)

    err := conta.Sacar(200.00)
    fmt.Println(err)
    // Output:
    // saldo insuficiente
}

A convenção de nomenclatura para exemplos: - ExampleFuncao — exemplo para uma função - ExampleTipo_Metodo — exemplo para um método - ExampleTipo_Metodo_sufixo — múltiplos exemplos para o mesmo método


Rodando o godoc localmente

# Instalar godoc
go install golang.org/x/tools/cmd/godoc@latest

# Iniciar servidor local na porta 6060
godoc -http=:6060

Acesse http://localhost:6060/pkg/meu-modulo/ para ver a documentação do seu módulo renderizada.


go doc: documentação na linha de comando

# Documentação de um pacote
go doc fmt

# Documentação de uma função específica
go doc fmt.Printf

# Documentação de um tipo
go doc net/http.Request

# Documentação de um método
go doc net/http.Request.Header

# Documentação completa incluindo não-exportados
go doc -all ./financeiro/

# Exemplo específico
go doc -src fmt.Printf  # mostra o código fonte

pkg.go.dev: documentação pública

Todo módulo publicado no GitHub (ou qualquer VCS público) aparece automaticamente em pkg.go.dev após ser referenciado por um go get. A documentação é gerada automaticamente a partir dos comentários do código.

Para garantir boa apresentação no pkg.go.dev:

// Package meuservico fornece um cliente HTTP para a API de Pagamentos XYZ.
//
// # Instalação
//
//  go get github.com/minha-empresa/meuservico
//
// # Uso rápido
//
//  cliente := meuservico.NovoCliente("minha-api-key")
//  resposta, err := cliente.CriarPagamento(ctx, meuservico.Pagamento{
//      Valor:    100.00,
//      Moeda:    "BRL",
//      Descricao: "Pedido #1001",
//  })
//
// # Autenticação
//
// Todas as requisições exigem uma API key válida, obtida no painel em
// https://app.meuservico.com/api-keys
//
// # Tratamento de erros
//
// Erros da API são retornados como *meuservico.ErroAPI, que contém o
// código HTTP e a mensagem de erro detalhada.
package meuservico

Os cabeçalhos # Seção são suportados desde o Go 1.19 e renderizados como <h3> no pkg.go.dev.


README.md e doc.go

Para pacotes com documentação extensa, é comum separar o comentário do pacote em um arquivo dedicado doc.go:

// doc.go — contém apenas o comentário do pacote e a declaração package

// Package cache implementa uma camada de cache em memória com suporte a
// expiração por TTL, eviction por LRU e métricas de hit/miss.
//
// # Exemplo básico
//
//  c := cache.Novo(cache.Config{
//      Capacidade: 1000,
//      TTL:        5 * time.Minute,
//  })
//
//  c.Set("chave", "valor")
//
//  v, existe := c.Get("chave")
//  if existe {
//      fmt.Println(v)
//  }
//
// # Eviction policy
//
// Quando a capacidade máxima é atingida, o item menos recentemente usado
// (LRU — Least Recently Used) é removido automaticamente.
package cache

Convenções de comentário por tipo

// Comentários de pacote: "Package nome fornece/implementa..."
package autenticacao

// Tipos: "NomeTipo representa/é um/a..."
// Usuario representa um usuário autenticado no sistema.
type Usuario struct { }

// Interfaces: "NomeInterface é implementado por..."
// Autenticador é implementado por qualquer tipo que possa validar credenciais.
type Autenticador interface { }

// Funções construtoras: "NovoTipo cria/retorna..."
// NovoUsuario cria e retorna um novo usuário com o email e senha informados.
func NovoUsuario(email, senha string) (*Usuario, error) { }

// Métodos de leitura: "Nome retorna..."
// Email retorna o endereço de email do usuário.
func (u *Usuario) Email() string { }

// Métodos de escrita: "Nome define/atualiza/registra..."
// DefinirSenha atualiza a senha do usuário com o hash fornecido.
func (u *Usuario) DefinirSenha(hash string) { }

// Métodos booleanos: "Nome relata/verifica se..."
// EstaAtivo relata se o usuário está ativo no sistema.
func (u *Usuario) EstaAtivo() bool { }

// Erros sentinela: "Err... é retornado quando..."
// ErrCredenciaisInvalidas é retornado quando email ou senha não conferem.
var ErrCredenciaisInvalidas = errors.New("credenciais inválidas")

// Constantes: explicar o valor quando não é óbvio
const (
    // SessaoMaxima define o tempo máximo de uma sessão inativa antes da expiração.
    SessaoMaxima = 30 * time.Minute

    // TentativasMaximas define o número de tentativas de login antes do bloqueio.
    TentativasMaximas = 5
)

Deprecated: marcando APIs obsoletas

// Conectar estabelece uma conexão com o servidor.
//
// Deprecated: use ConectarComContext para suporte a cancelamento e timeout.
// Esta função será removida na versão 3.0.
func Conectar(host string) (*Conexao, error) {
    return ConectarComContext(context.Background(), host)
}

// ConectarComContext estabelece uma conexão respeitando o context fornecido,
// permitindo cancelamento e timeout.
func ConectarComContext(ctx context.Context, host string) (*Conexao, error) {
    // implementação atual
}

Ferramentas como staticcheck detectam uso de funções marcadas como Deprecated: e emitem avisos.


Documentação de erros esperados

// Transferir move o valor especificado da conta de origem para a de destino.
//
// Erros possíveis:
//   - ErrSaldoInsuficiente: saldo da conta de origem é menor que o valor
//   - ErrContaBloqueada: conta de origem ou destino está bloqueada
//   - ErrValorInvalido: valor é menor ou igual a zero
//   - ErrMesmasConta: origem e destino são a mesma conta
func (s *Servico) Transferir(ctx context.Context, origemID, destinoID string, valor float64) error {

Checklist de documentação

Antes de publicar ou fazer code review, verifique:

□ O pacote tem comentário começando com "Package nome..."?
□ Toda função/método exportado tem comentário começando com seu nome?
□ Todo tipo exportado tem comentário descritivo?
□ Erros sentinela exportados têm comentário explicando quando são retornados?
□ Funções complexas têm exemplos executáveis?
□ Parâmetros com comportamento não óbvio estão documentados?
□ APIs obsoletas estão marcadas com "Deprecated:"?
□ O comentário faz sentido quando lido isoladamente do código?
□ go doc ./... não exibe "missing documentation" para símbolos públicos?

Resumo do que foi coberto

Este artigo apresentou documentação em Go de forma completa: as regras do godoc — comentários imediatamente antes das declarações começando com o nome do identificador — formatação especial com blocos de código e listas, exemplos executáveis verificados pelo go test, o servidor local do godoc, o comando go doc para consulta na linha de comando, boas práticas de publicação no pkg.go.dev, o arquivo doc.go para documentação extensa, convenções por tipo de identificador, marcação de APIs obsoletas com Deprecated: e um checklist de qualidade. O próximo artigo explora profiling e otimização de performance.


Referências e leituras complementares

  • Go Blog: Go Doc Comments — Guia oficial completo sobre comentários de documentação. https://go.dev/doc/comment

  • pkg.go.dev — Portal de documentação oficial de todos os módulos Go públicos. https://pkg.go.dev

  • Documentação godoc — Referência da ferramenta godoc. https://pkg.go.dev/golang.org/x/tools/cmd/godoc

  • Go Blog: Testable Examples in Go — Como exemplos executáveis funcionam. https://go.dev/blog/examples

  • Go Code Review Comments — Documentation — Convenções de comentário da equipe Go. https://github.com/golang/go/wiki/CodeReviewComments#commentary

  • staticcheck — SA1019 — Detecção de uso de APIs marcadas como Deprecated. https://staticcheck.dev/docs/checks#SA1019


Próximo artigo: Artigo 43 — Profiling e otimização de performance em Go**


you asked

Sim


claude response

Comentários

Mais em Golang

Artigo 47 — Design Patterns em Go: Repository, Factory e Strategy
Artigo 47 — Design Patterns em Go: Repository, Factory e Strategy

Artigo 47 &mdash; Design Patterns em Go: Repository, Factory e Strategy Curs...

Artigo 41 — Linting, formatação e análise estática: gofmt, golangci-lint
Artigo 41 — Linting, formatação e análise estática: gofmt, golangci-lint

Artigo 41 — Linting, formatação e análise estática: gofmt, golangci-lint Qual...

Artigo 31 — Middlewares: logging, autenticação e CORS
Artigo 31 — Middlewares: logging, autenticação e CORS

Artigo 31 — Middlewares: logging, autenticação e CORS Middlewares: comportame...