Docs Geradas por Máquina, Confiáveis por Contrato: Um Modelo de Verificação para Documentação Automática com IA
Você já publicou uma documentação gerada por IA que descrevia um comportamento que o código nunca teve. Não é hipótese — é quase certeza estatística para quem usa geração automática de docs há mais de alguns meses. O modelo lê a assinatura de uma função, infere a intenção a partir do nome dos parâmetros, e escreve um parágrafo fluente, gramaticalmente perfeito, sobre um comportamento que existiu numa versão anterior, foi alterado num refactor silencioso, e nunca foi revisado por ninguém porque “a IA já documentou”.
O problema não é a geração. É a ausência de um contrato de verificação entre o código-fonte e o texto gerado. Enquanto a comunidade debate qual ferramenta escreve a prosa mais elegante, o risco real está em produção: documentação que parece autoritativa e está errada é pior do que nenhuma documentação, porque ela substitui a leitura do código pela confiança na prosa.
O ponto cego que a maioria dos pipelines de documentação ignora
Já existe um artigo aqui no blog tratando de “documentação como efeito colateral” e de como transformar geração de docs em parte do pipeline. Este texto assume que você já passou dessa fase — já tem um pipeline, já gera docstrings, README’s ou páginas de referência automaticamente. A pergunta que resta, e que raramente é respondida na prática, é: como você sabe que a documentação gerada ainda é verdadeira?
Você vai encontrar aqui um modelo de verificação de três camadas — sintática, semântica e comportamental — para tratar documentação gerada por IA como um artefato que precisa de teste, assim como código precisa de teste. Sem esse modelo, você não tem “documentação automática”, tem “prosa automática com aparência de documentação”.
💡 Dica do Mestre: a distinção entre “documentação descritiva” e “documentação prescritiva” é central aqui. Descritiva relata o que o código faz agora; prescritiva define o que o código deve fazer. IA generativa é excelente na primeira e perigosa na segunda, porque tende a “corrigir” silenciosamente comportamentos que interpreta como bugs, documentando a intenção idealizada em vez do comportamento real. Veja a comparação de ferramentas de geração de código com foco em segurança em Melhores ferramentas de IA para programação segura em 2026, que trata exatamente desse tipo de desvio entre intenção documentada e comportamento real.
Por que docstrings geradas por LLM degradam com o tempo (e por que isso é diferente de código legado)
Código legado degrada porque acumula complexidade acidental. Documentação gerada por IA degrada por um motivo estrutural diferente: ela é uma fotografia de contexto, tirada no momento da geração, sem vínculo semântico persistente com o código que descreve. Um comentário escrito por um humano carrega intenção e memória institucional — mesmo desatualizado, ele reflete uma decisão consciente. Uma docstring gerada por IA reflete apenas o que o modelo inferiu a partir do texto visível naquele instante.
Isso cria três modos de falha específicos:
- Drift silencioso: o código muda, a doc não é regenerada, e ninguém percebe porque a doc continua “fazendo sentido” — apenas descreve a versão anterior.
- Alucinação de contrato: o modelo descreve exceções, retornos ou efeitos colaterais que nunca existiram, porque são padrões comuns em código semelhante visto no treinamento.
- Falso consenso de revisão: o time aprova o PR porque “a doc está lá e parece boa”, sem checar se ela reflete o diff.
O antídoto não é gerar melhor. É verificar de forma automatizada, assim como você não confia em código sem suíte de testes — mesmo que ele “pareça” correto.
Camada 1 — Verificação sintática: a documentação aponta para o que existe?
É a camada mais simples e, ainda assim, a mais negligenciada. Trata-se de garantir que toda referência na documentação — nomes de funções, parâmetros, tipos, exceções citadas — exista de fato no código-fonte atual. Ferramentas de linting de documentação fazem isso de forma estática.
Em Python, um exemplo com pydocstyle combinado a uma checagem de assinatura via AST:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import ast import inspect def verify_docstring_params(func): """Verifica se todos os parâmetros citados na docstring existem na assinatura real.""" sig = inspect.signature(func) real_params = set(sig.parameters.keys()) doc = inspect.getdoc(func) or "" documented_params = set() for line in doc.splitlines(): line = line.strip() if line.startswith(":param"): # formato reStructuredText: :param nome: descrição name = line.split(":")[1].replace("param", "").strip() documented_params.add(name) missing_in_doc = real_params - documented_params - {"self", "cls"} stale_in_doc = documented_params - real_params if missing_in_doc: raise ValueError(f"Parâmetros sem documentação: {missing_in_doc}") if stale_in_doc: raise ValueError(f"Documentação cita parâmetros inexistentes: {stale_in_doc}") return True |
Esse tipo de checagem, rodado em CI a cada PR, elimina a classe mais barata e mais comum de erro: doc que fala de um parâmetro removido três refactors atrás. Não resolve alucinação semântica, mas é o piso mínimo — se a doc gerada não passa nem por essa checagem sintática, ela não deveria nem chegar a revisão humana.
Camada 2 — Verificação semântica: a documentação descreve o comportamento certo?
Aqui a coisa fica interessante e é onde a maioria dos times para. Verificação semântica significa usar um segundo agente de IA — idealmente um modelo diferente do que gerou a documentação — para auditar a doc contra o código-fonte, não para regenerá-la.
A técnica é conhecida como “critic model” ou “verificador adversarial”: você não pede para o modelo escrever, pede para ele apontar divergências.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import anthropic client = anthropic.Anthropic() def audit_documentation(source_code: str, generated_doc: str) -> str: prompt = f"""Você é um auditor técnico rigoroso. Sua tarefa é APENAS apontar divergências factuais entre o código-fonte abaixo e a documentação gerada. Não sugira melhorias de estilo. Não reescreva a documentação. Liste cada divergência com: linha do código, trecho da doc, e por que diverge. Se não houver divergências, responda apenas "SEM DIVERGÊNCIAS". CÓDIGO-FONTE: {source_code} DOCUMENTAÇÃO GERADA: {generated_doc} """ response = client.messages.create( model="claude-opus-4-5-20251101", max_tokens=2048, messages=[{"role": "user", "content": prompt}] ) return response.content[0].text |
O ponto-chave de arquitetura aqui é a separação de responsabilidades entre gerador e auditor. Se você usa o mesmo modelo, com o mesmo contexto, para gerar e depois “revisar” a própria geração, está pedindo para o viés de confirmação se auto-validar — o modelo tende a concordar consigo mesmo. Trocar de modelo (por exemplo, gerar com um modelo e auditar com outro, ou pelo menos resetar completamente o contexto) reduz esse viés, embora não elimine.
Esse padrão de “segundo agente cético” é o mesmo discutido no artigo sobre code review automatizado deste blog — a lógica de que um aprovador de IA pode estar te enganando se ele for complacente por design se aplica integralmente à documentação.
Camada 3 — Verificação comportamental: a documentação sobrevive à execução?
É a camada mais cara de implementar e a que gera mais valor. Consiste em extrair exemplos de código citados na própria documentação e executá-los como testes reais, num pipeline de CI. Se a doc afirma que uma função “lança ValueError quando o input é negativo”, esse comportamento vira um teste automatizado gerado a partir da própria doc.
Em Python, isso é literalmente nativo via doctest:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
def calcular_desconto(preco: float, percentual: float) -> float: """Calcula o preço final aplicando um desconto percentual. >>> calcular_desconto(100.0, 10) 90.0 >>> calcular_desconto(100.0, -5) Traceback (most recent call last): ... ValueError: percentual não pode ser negativo """ if percentual < 0: raise ValueError("percentual não pode ser negativo") return preco * (1 - percentual / 100) if __name__ == "__main__": import doctest doctest.testmod(verbose=True) |
Quando a IA gera a docstring, force-a a gerar exemplos no formato doctest (ou equivalente na sua linguagem — Rust tem isso nativo via cargo test --doc, Go tem Example functions testáveis). Isso transforma a documentação em algo que quebra visivelmente no CI quando o comportamento muda, em vez de ficar mentindo silenciosamente em produção.
|
1 2 3 4 |
python -m doctest calcular_desconto.py -v |
Em Go, o padrão de “Example functions” cumpre exatamente esse papel — documentação que é, ao mesmo tempo, teste executável:
|
1 2 3 4 5 6 7 8 |
func ExampleCalcularDesconto() { resultado := CalcularDesconto(100.0, 10) fmt.Println(resultado) // Output: 90 } |
Se você trabalha em uma linguagem sem esse suporte nativo (como Delphi/Object Pascal, por exemplo), a alternativa é construir um script de extração que varre comentários XMLDoc em busca de blocos de exemplo e os compila isoladamente como parte da suíte de testes — mais trabalho de infraestrutura, mas o princípio é o mesmo: nenhum exemplo documentado deve existir fora de um contexto executável.
Arquitetura de pipeline: onde cada camada entra no fluxo
Um erro comum é tentar rodar as três camadas em todo commit, o que é caro e lento. A arquitetura que funciona na prática distribui as camadas por gatilho:
- Pre-commit / pre-push: apenas verificação sintática (rápida, sem chamadas de API, roda local).
- Pull Request (CI): verificação sintática + semântica, rodando apenas nos arquivos alterados no diff — não no repositório inteiro, por custo de tokens e tempo.
- Pipeline noturno / release: verificação comportamental completa, executando todos os exemplos documentados como testes, incluindo os que não mudaram, para pegar regressões introduzidas por dependências externas.
Um exemplo de job de CI (GitHub Actions) que roda a camada semântica apenas sobre arquivos modificados:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
name: audit-docs on: pull_request: paths: - '**/*.py' jobs: audit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Get changed files id: changed run: | echo "files=$(git diff --name-only origin/main...HEAD | grep '\.py$' | tr '\n' ' ')" >> $GITHUB_OUTPUT - name: Run doc audit run: python scripts/audit_docs.py ${{ steps.changed.outputs.files }} |
💡 Dica do Mestre: resista à tentação de rodar o auditor semântico contra a base de código inteira a cada PR. O custo de tokens cresce linearmente com o tamanho do repositório e a maior parte da análise é redundante. Restrinja ao diff e trate a auditoria completa como uma tarefa de manutenção periódica, não de gate de merge. Para comparar o custo-benefício de diferentes ferramentas de geração e análise de código com IA, vale conferir o panorama em Melhor Software de Geração de Código por IA — G2.
O caso difícil: documentação de arquitetura e decisões (ADRs) — onde a IA não deveria escrever sozinha
Tudo o que foi discutido até aqui se aplica bem a documentação de nível de função e módulo — descrever comportamento observável é uma tarefa tratável por verificação automatizada porque existe um “gabarito” objetivo: o próprio código em execução. Documentação de arquitetura (Architecture Decision Records, diagramas de contexto, racionais de design) é categoricamente diferente: não existe execução que valide se o racional documentado é verdadeiro, porque o racional é uma afirmação sobre intenção, não sobre comportamento.
Aqui, o uso correto de IA não é gerar o ADR, é gerar o rascunho estrutural a partir de artefatos existentes — PRs, discussões de issue, commits — e forçar um humano a preencher a seção “consequências” e “alternativas consideradas”, que são exatamente as partes que exigem julgamento e não podem ser inferidas de forma confiável a partir de código.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def draft_adr_from_pr(pr_diff: str, pr_description: str) -> str: """Gera apenas o esqueleto de um ADR — nunca as seções de julgamento.""" prompt = f"""A partir do diff e da descrição de PR abaixo, gere um esqueleto de ADR (Architecture Decision Record) com as seções: Contexto, Decisão (resumida objetivamente, sem avaliação de mérito). NÃO preencha 'Consequências' nem 'Alternativas Consideradas' — deixe marcadas como [PENDENTE DE REVISÃO HUMANA]. DIFF: {pr_diff} DESCRIÇÃO: {pr_description} """ # chamada ao modelo omitida por brevidade return prompt |
Essa distinção — o que a IA pode verificar objetivamente versus o que exige julgamento humano — é a linha divisória mais importante deste artigo. Times que ignoram essa fronteira acabam com ADRs “completos” que documentam decisões que ninguém de fato tomou conscientemente, apenas o modelo inferiu como plausíveis.
Métricas de saúde da documentação: o que medir de verdade
Se você vai investir em um pipeline de verificação, meça o que importa. Três métricas têm valor prático maior do que “cobertura de documentação” (percentual de funções documentadas), que é uma métrica de vaidade — 100% de cobertura com 40% de conteúdo desatualizado é pior do que 60% de cobertura confiável.
- Taxa de divergência semântica por release: quantas divergências a camada 2 encontrou proporcionalmente ao volume de mudanças — indica se o processo de atualização de docs está acompanhando o ritmo do código.
- Idade média da última verificação comportamental: há quanto tempo cada exemplo documentado foi de fato executado com sucesso — não quando foi escrito, quando foi validado pela última vez.
- Taxa de rejeição do auditor: quantas docs geradas pelo primeiro agente são rejeitadas pelo segundo agente antes de chegar a revisão humana — um número crescente aqui é sinal de que o prompt de geração precisa de ajuste, não de que o auditor está “sendo chato”.
💡 Dica do Mestre: ferramentas de geração de código voltadas a produtividade tendem a enfatizar velocidade de geração como métrica de sucesso — o que é um incentivo perigoso quando aplicado a documentação. Vale ler com espírito crítico o levantamento em 10 ferramentas de IA para programação mais inteligente e perguntar, para cada ferramenta citada, não “quão rápido ela gera” mas “como ela permite verificar o que gerou”.
Quando NÃO vale a pena automatizar a verificação
Honestidade técnica exige reconhecer o custo. Construir as três camadas de verificação é investimento de infraestrutura não trivial — scripts de auditoria, pipelines de CI dedicados, orçamento de tokens para o segundo agente. Para projetos pequenos, times de duas ou três pessoas, ou código com ciclo de vida curto (protótipos, provas de conceito, código descartável), esse investimento não se paga. Nesses contextos, a alternativa pragmática é mais simples: gerar documentação com IA, mas tratar toda ela como rascunho — nunca mergear sem revisão humana linha a linha, o que efetivamente substitui a camada semântica por um revisor humano mais lento, mas suficiente na escala do projeto.
O ponto de inflexão em que vale investir na arquitetura completa é quando a base de código atinge um tamanho em que revisão humana linha a linha de toda documentação deixa de ser viável — tipicamente sistemas com múltiplos times contribuindo no mesmo repositório, ou bibliotecas com API pública consumida por terceiros que nunca verão o código-fonte.
Participe da Comunidade Dev’s AI
Discussões como essa — sobre arquitetura de verificação, não apenas sobre qual ferramenta gera texto mais bonito — são o tipo de conversa que acontece na Comunidade Dev’s AI. Se você já passou da fase de “gerar documentação com IA” e está enfrentando o problema real de mantê-la confiável em produção, entre para trocar pipelines, scripts de auditoria e casos reais de drift semântico com outros desenvolvedores sêniores enfrentando o mesmo desafio.
Conclusão
Documentação gerada por IA sem verificação não é um atalho — é uma dívida técnica disfarçada de produtividade. O valor real da automação não está na velocidade de geração, que já é resolvida por praticamente qualquer ferramenta do mercado, como mostram os panoramas em Focalx e nas comparações setoriais citadas ao longo deste artigo. O valor está em construir o contrato de verificação que transforma prosa gerada em artefato confiável: sintaxe checada em pre-commit, semântica auditada por um segundo agente em PR, e comportamento validado por execução real em pipeline de release.
Trate cada docstring, cada ADR, cada exemplo de código na sua documentação exatamente como trataria uma linha de produção: sem teste, não existe garantia — só a aparência confortável de que existe.