Documentação gerada por LLM é um dos casos onde a capacidade de produção é inversamente proporcional ao valor produzido quando usada incorretamente. Um LLM pode gerar docstrings para cada função de um repositório em minutos — e na maioria dos casos, o resultado é documentação que descreve o que o código faz, informação que um leitor competente já extrai do código. O que está faltando — as decisões de design, as restrições não-óbvias, o porquê de uma escolha que parece estranha à primeira vista — o LLM não consegue produzir porque não existe no texto do código.
Há um princípio de documentação que precede LLMs e que fica ainda mais importante com eles: boa documentação explica o porquê, não o quê. O quê está no código — qualquer leitor vê que a função itera sobre uma lista e filtra por status. O que o leitor não vê: por que esse filtro específico, por que nesse lugar e não em outro, por que esse campo e não aquele, o que aconteceu quando foi tentado de outra forma. Essas são as informações que economizam horas de debugging e de entendimento errado — e são exatamente o que LLMs não conseguem gerar porque não estiveram presentes nas decisões.
Isso não significa que LLMs são inúteis para documentação. Significa que o valor está em lugares específicos: documentar decisões de design que o engenheiro articula explicitamente, gerar documentação de API a partir de contratos bem definidos, manter changelogs a partir de commits, e identificar onde a documentação existente está desatualizada ou inconsistente com o código. O padrão correto é usar LLM para a parte mecânica de documentação e preservar o esforço humano para a parte de valor alto — documentar as decisões que só o engenheiro conhece.
O problema fundamental: LLMs descrevem o quê, não o porquê
Considere uma função com o seguinte comentário gerado por LLM: "Calcula o preço final do pedido aplicando descontos e impostos". O comentário é correto, fluente, e completamente inútil — qualquer pessoa que lê o nome da função CalculateFinalOrderPrice já sabe isso. O comentário que seria valioso: "Imposto é calculado sobre o preço com desconto, não sobre o preço original — isso é requerimento legal (consulte issue #1423 para o contexto do regulador). A ordem de aplicação (desconto primeiro, então imposto) não pode ser invertida sem validação legal."
Esse comentário valioso não pode ser gerado por LLM porque a informação — que a ordem de cálculo é requerimento legal, não escolha de implementação — não existe no código. Ela existe na memória do engenheiro que implementou, no histórico de discussão da issue, no documento legal que o time recebeu. O LLM vê apenas o resultado: desconto aplicado, imposto calculado. A decisão que tornou esse resultado necessário é invisível.
Esse problema escala para todo o código de domínio não-trivial: invariantes de negócio, decisões arquiteturais, workarounds para bugs de dependências externas, comportamentos que parecem errados mas estão corretos por razões não-óbvias. O LLM pode identificar que o código faz algo não-óbvio — uma condição que parece estranha, um número mágico, uma sequência de operações incomum — e pode perguntar ao engenheiro o porquê. Mas a resposta precisa vir do engenheiro.
Antes de gerar documentação com LLM, pergunte: "o que estou documentando é visível para um leitor competente que lê o código?" Se sim, a documentação não agrega valor — o código já é a documentação. O que vale documentar é o que o código não mostra: decisões, restrições, trade-offs, contexto histórico, invariantes de domínio. Essas informações precisam vir do engenheiro, não do LLM.
Onde LLMs agregam valor em documentação
Documentação de API a partir de schema formal. Dado um schema OpenAPI, definição de Protocol Buffer, ou interface tipada, o LLM pode gerar documentação de referência — descrições de campos, exemplos de request/response, descrições de endpoints. Essa documentação é derivável dos contratos formais e é exatamente o tipo de transformação mecânica que LLMs fazem bem. O resultado não precisa de input do engenheiro além do schema, que já existe.
README de módulo a partir da estrutura e testes. Para um módulo que o engenheiro conhece bem, o LLM pode gerar o rascunho do README: o que o módulo faz, como usá-lo, quais são as principais APIs públicas. O engenheiro fornece o contexto de domínio que o LLM não tem (por que esse módulo existe, quais os casos de uso primários, o que está fora do escopo) e o LLM gera a estrutura e os exemplos de uso a partir dos testes e do código.
Changelog a partir de commits. "Dado os commits desta semana, gere um changelog no formato convencional agrupando por tipo de mudança" é uma tarefa mecânica e bem adequada para LLM. O resultado é mais consistente do que changelog escrito manualmente por cada desenvolvedor e mais completo do que changelog só do que o time lembrou de mencionar na reunião de sprint.
Identificar onde documentação está desatualizada. "Dado o código atual e a documentação existente, identifique onde a documentação não corresponde ao código" é análise que LLMs fazem razoavelmente bem para inconsistências visíveis — um parâmetro documentado que não existe mais na assinatura, um comportamento descrito que o código implementa diferente, um exemplo que não compila com a versão atual da API. Inconsistências sutis de semântica ainda requerem o humano.
Documentar decisões de design que o engenheiro articula. Esse é o uso de mais valor: o engenheiro escreve um rascunho de ADR ou comentário de design em bullet points, e o LLM transforma em prosa fluente e bem estruturada. O conteúdo — as decisões reais, as alternativas consideradas, as restrições que levaram à escolha — vem do engenheiro. A fluência e a estrutura vêm do LLM. A combinação é mais rápida do que o engenheiro escrevendo tudo e mais substancial do que o LLM gerando sem input de decisões reais.
O risco da documentação que ninguém mantém
Documentação gerada automaticamente tem um risco específico: como é barata de gerar, há incentivo a gerar muito. Documentação que ninguém vai manter fica desatualizada rapidamente — e documentação desatualizada é ativamente prejudicial, porque um leitor que a encontra pode confiar nela e tomar decisões baseadas em informação errada.
A heurística para decidir se a documentação que vai ser gerada vale gerar: alguém vai ler isso? Quem? Quando? Se a documentação for para um módulo interno que só três pessoas do time conhecem e que ninguém de fora vai consultar, o custo de mantê-la atualizada supera o benefício de tê-la. Se for para uma API pública que consumidores externos vão usar, o valor de manter atualizada é alto e o custo de desatualização é ainda mais alto.
Para documentação de API pública, a solução que escala é documentação como código: o schema OpenAPI é a source of truth, gerado automaticamente a partir das anotações no código (ou o código gerado a partir do schema), e a documentação de referência é derivada do schema. Essa abordagem garante consistência automaticamente — o schema está errado ou o código está errado, mas não podem divergir silenciosamente. LLMs ajudam na parte narrativa (descrição de casos de uso, exemplos, guias de início rápido) que não é derivável do schema.
// Engenheiro escreve o contexto em bullet points:
//
// Decisão: usar outbox pattern para garantir que eventos
// são sempre publicados depois de mudança de estado do pedido
//
// - Problema: se publicarmos o evento antes de salvar no banco,
// o banco pode falhar e o evento já saiu — inconsistência
// - Se salvarmos no banco e publicarmos depois, a instância pode
// morrer antes de publicar — evento perdido
// - Tentamos two-phase commit mas era muito complexo e tinha
// problemas de performance com o message broker
// - Outbox: salvar evento na mesma transação do banco,
// um worker separado lê o outbox e publica
// - Trade-off aceito: latência extra de publicação (50-200ms)
// em troca de garantia de entrega
// - Alternativa considerada e rejeitada: saga com compensação
// (complexidade muito alta para o nível de consistência exigido)
//
// Instrução ao LLM:
// "Transforme esses bullet points em um ADR (Architecture
// Decision Record) bem estruturado seguindo o template
// em docs/templates/adr-template.md. Tom técnico, objetivo.
// Não adicione informação além do que está nos bullets.
// Não faça suposições sobre o sistema além do que foi descrito."
// Resultado: ADR fluente com contexto, decisão, alternativas
// e consequências — o engenheiro forneceu o conteúdo,
// o LLM forneceu a estrutura e a fluência.
O engenheiro fornece as decisões reais (o que o LLM não tem como inventar); o LLM transforma em documento estruturado. Essa divisão produz documentação que tem valor real sem o custo de escrever prosa do zero.
# Instrução ao LLM para auditoria de documentação:
"""
Leia o módulo services/pricing.py.
Para cada bloco de código ou decisão que parece não-óbvia,
liste:
1. O que o código faz (o quê — visível no código)
2. Por que faz assim (o porquê — o que está FALTANDO
ser documentado e eu como autor deveria explicar)
Foque em:
- Números mágicos (constantes sem nome ou explicação)
- Condicionais com mais de 2 ramificações
- Ordem de operações que parece arbitrária mas pode não ser
- Tratamentos de caso especial sem comentário
- Uso de biblioteca de forma não-óbvia
Não invente o porquê — liste o que está faltando ser explicado.
Vou responder com o contexto real de cada um."
"""
# Resultado: lista de lugares onde documentação de porquê
# está faltando. O engenheiro responde com o contexto real.
# O LLM transforma as respostas em comentários/docstrings.
# Exemplo de resultado da auditoria:
# L47: FALTANDO — por que multiplica por 0.85 para pedidos
# internacionais? É taxa? É desconto? Regulação?
# L89: FALTANDO — por que o fallback é 'standard' e não 'none'?
# Qual o impacto de trocar esse default?
# L112: FALTANDO — a condição `and customer.tier != 'trial'`
# parece uma exceção especial — qual é a regra de negócio?
Usar LLM para identificar o que está faltando ser documentado é mais valioso do que usar para gerar documentação. O LLM encontra os pontos de confusão; o engenheiro fornece o contexto; o LLM escreve o texto final.
// Instrução ao LLM (com git log colado):
//
// "Dado o git log abaixo (últimas 2 semanas), gere um
// CHANGELOG.md no formato Keep a Changelog (keepachangelog.com).
//
// Agrupe por categoria:
// - Added: funcionalidades novas
// - Changed: mudanças em funcionalidades existentes
// - Fixed: correções de bug
// - Removed: funcionalidades removidas
// - Security: correções de segurança
// - Deprecated: funcionalidades marcadas para remoção
//
// Regras:
// - Ignore commits de merge, bump de versão, e 'fix typo'
// - Para commits ambíguos, prefira a categoria mais conservadora
// - Tom: frases curtas, voz ativa, do ponto de vista do usuário
// ('Adiciona paginação cursor-based em GET /orders' não
// 'Implementa CursorPaginationMiddleware em handlers/orders.go')
// - Se um commit não é claro o suficiente para categorizar,
// liste separadamente para eu revisar"
//
// git log --oneline --no-merges --since="2 weeks ago":
// a1b2c3d feat: add cursor-based pagination to orders endpoint
// d4e5f6g fix: race condition in payment processing
// h7i8j9k refactor: extract discount calculator
// l0m1n2o security: upgrade jwt library to fix CVE-2024-1234
// p3q4r5s chore: bump go version to 1.24
// t6u7v8w fix: typo in error message
// ...
Changelogs gerados por LLM a partir de commits são mais consistentes do que changelogs escritos manualmente e mais completos do que "o que o time lembrou de mencionar". A qualidade dos commits de entrada determina a qualidade do changelog.
Docstrings e comentários: quando gerar e quando não gerar
A questão de quando gerar docstrings automaticamente tem uma resposta que parece contraintuitiva: para código de domínio complexo, docstrings geradas por LLM geralmente não valem o espaço que ocupam. Para APIs públicas expostas a consumidores externos, valem muito.
Para código de domínio complexo, a regra é: um comentário que descreve o quê o código faz é sempre pior do que código que se explica pelo naming. Se o código precisa de comentário para explicar o quê, o código está mal-nomeado. Se o comentário explica o porquê — uma restrição não-óbvia, um workaround para bug de dependência, uma decisão de design — então agrega valor real e não pode ser gerado automaticamente.
Para APIs públicas, a situação é diferente: os consumidores podem não ter acesso ao código, precisam de exemplos de uso, e a consistência da documentação afeta a adoção. Aqui, LLMs geram rascunhos úteis: descrição de parâmetros, valores de retorno, exceções possíveis, exemplos básicos de uso. O engenheiro adiciona os casos de uso específicos, as restrições não-óbvias, e as advertências que os consumidores precisam saber.
O critério prático: gerar docstrings para funções públicas que fazem parte de uma API consumida por outros (outros times, clientes externos, biblioteca open-source). Não gerar para funções privadas internas a um módulo onde o código bem-nomeado é suficiente. Para funções públicas complexas com invariantes não-óbvias, o engenheiro escreve os comentários de porquê; o LLM pode ajudar a estruturar o texto.
Documentação técnica de longa duração
Além de comentários e docstrings, há a documentação técnica de longa duração: arquitetura do sistema, guias de onboarding, runbooks de operação, post-mortems, RFCs. LLMs são úteis em diferentes graus para cada tipo.
Para arquitetura: LLMs podem gerar rascunhos de diagramas C4 em formato Mermaid ou PlantUML a partir de descrição verbal, ajudar a estruturar documentos de arquitetura, e identificar inconsistências entre o que está documentado e o que está no código. A substância — as decisões arquiteturais, os trade-offs, os componentes reais — precisa vir do engenheiro.
Para runbooks de operação: dado o procedimento que o engenheiro descreve, o LLM pode estruturá-lo no formato padrão, adicionar seções que o engenheiro esqueceu (verificação de pré-condições, rollback, notificação de stakeholders), e tornar as instruções mais claras. Runbooks precisam ser testados — não só escritos — e o teste precisa ser feito por humanos.
Para post-mortems blameless: LLMs podem ajudar a estruturar a narrativa do incidente a partir de um log de eventos, identificar padrões de causa raiz em múltiplos post-mortems ("essa categoria de problema apareceu 3 vezes nos últimos 6 meses"), e garantir que o documento segue o formato do time. A análise de causa raiz e os action items precisam vir de quem esteve no incidente — o LLM não estava.
Como praticar
- Auditoria de documentação de porquê. Escolha um módulo do projeto atual com código não-trivial. Use LLM para listar todos os lugares onde há código que parece não-óbvio mas não tem comentário de porquê. Para cada item da lista, escreva o porquê real (você sabe — é o seu código). Use LLM para transformar suas respostas em comentários ou docstrings bem estruturados. Compare o resultado com o que o LLM teria gerado sem seu input de contexto.
- ADR via bullet points. Para uma decisão técnica que você tomou recentemente e não documentou, escreva os bullet points: o problema, as alternativas consideradas, o porquê da escolha, os trade-offs aceitos. Passe os bullets para o LLM e peça um ADR estruturado. Revise se o ADR captura fielmente sua decisão sem inventar informação. Esse exercício calibra o protocolo de "engenheiro fornece contexto, LLM estrutura".
- Identificar documentação desatualizada. Pegue um módulo que passou por mudanças nos últimos três meses e ainda tem documentação escrita antes das mudanças. Use LLM para comparar o código atual com a documentação e identificar inconsistências. Quantas inconsistências o LLM encontrou? Quantas você conhecia? Quantas eram invisíveis sem comparação explícita? Esse número calibra o quanto sua documentação está se deteriorando e com que frequência auditoria de consistência deve ser feita.
Referências para aprofundar
- livro A Philosophy of Software Design — John Ousterhout (2018).
- livro Clean Code — Robert C. Martin (2008).
- artigo Keep a Changelog — Olivier Lacan.
- artigo ADR: Architecture Decision Records — Michael Nygard (2011).
- docs OpenAPI Specification — Description Objects — OpenAPI Initiative.
- artigo Docs as Code — Anne Gentle.
- artigo The Documentation System — Divio (2017).
- artigo Using AI for Technical Writing — Tom Johnson (2024).
- vídeo Documentation-Driven Development — Write the Docs (2022).
- artigo AI-Generated Documentation: Risks and Best Practices — Hamel Husain (2024).
- livro The Google SRE Book — Beyer et al. (2016).
- artigo Conventional Commits — conventionalcommits.org.