1

Fundamentos de Design Docs

Design Docs — ou documentos de design técnico — são artefatos escritos que capturam o pensamento estratégico por trás de uma decisão técnica antes de qualquer linha de código ser escrita. Popularizados por empresas como Google, Meta e Amazon, eles são a espinha dorsal da engenharia de software moderna em escala.

💡
Por que Design Docs existem?

Para forçar clareza de pensamento. O ato de escrever obriga o autor a confrontar ambiguidades, trade-offs e premissas que ficariam ocultas em uma conversa. Como dizia Jeff Bezos na famosa memo da Amazon: "Full sentences are harder to write. They have verbs. The paragraphs have topic sentences. There is no way to write a six-page, narratively-structured memo and not have clear thinking."

Estruturação de um Design Doc

Embora existam muitos formatos, os melhores design docs compartilham uma anatomia comum. Essa estrutura mental ajuda tanto a escrever quanto a ler rapidamente:

🎯
Contexto e Problema

O que motivou este documento? Qual dor existe hoje? Deve ser entendível por qualquer engenheiro da empresa, sem jargão do time.

🎪
Objetivos e Não-Objetivos

O que será resolvido e, crucialmente, o que não será. Escopo explícito previne scope creep.

🏗️
Proposta Técnica

A solução proposta em detalhe — diagramas, APIs, modelos de dados, fluxos. Deve permitir que outro engenheiro implemente.

⚖️
Alternativas Consideradas

Quais caminhos foram avaliados e por que foram descartados. Demonstra rigor e ajuda revisores a questionar premissas.

⚠️
Riscos e Trade-offs

O que pode dar errado, custos operacionais, impactos em performance/segurança, efeitos colaterais previstos.

🗓️
Plano de Execução

Fases, marcos, dependências, critérios de sucesso e estratégia de rollout. Ajuda líderes a entender custo/benefício.

Objetivos e Escopo: a dimensão estratégica

O escopo é a seção mais subestimada de um design doc. Engenheiros juniores tendem a escrever "queremos construir um serviço de autenticação". Engenheiros sêniores escrevem:

Escopo bem escrito

In-scope: autenticação por email/senha para usuários finais do produto X, integração com o provedor OIDC Y, emissão de JWT com TTL de 15min.
Out-of-scope: autenticação social (próximo quarter), 2FA (tracking separado em RFC-142), autenticação de serviços internos (continua usando mTLS).
Non-goals: substituir o sistema legado de sessão para aplicações administrativas — elas manterão cookies de sessão por pelo menos 12 meses.

Ferramentas do Ecossistema

A escolha da ferramenta influencia diretamente a adoção do processo. Organizações maduras padronizam uma combinação de repositório + editor + revisão:

Ferramenta Melhor Para Pontos Fortes Limitações
Google Docs Brainstorming rápido, revisão com stakeholders não-técnicos Comentários inline, controle de versão visual, colaboração em tempo real Fraco com código/diagramas, difícil de indexar, não integra com IDEs
Notion / Confluence Wiki corporativo, documentação permanente Hierarquia, templates, embedding de mídia, buscabilidade Pode virar "onde documentos vão morrer" sem governança forte
Markdown no Git ADRs, RFCs, docs que vivem com o código Versionamento robusto, PRs para revisão, indexável por IAs Curva de aprendizado para não-engenheiros, markup limitado
Linear / Jira Link entre design doc e execução (tickets) Rastreabilidade de decisão → implementação Não é editor ideal, melhor como referência cruzada
2

Design Docs na Era da IA

A chegada dos LLMs transformou fundamentalmente o papel da documentação. Antes, docs eram consumidos apenas por humanos. Hoje, cada design doc também é consumido por agentes de IA que implementam, revisam e operam sistemas. Isso muda tudo.

Documentação como Contexto

No paradigma IA-driven, documentação deixa de ser um artefato "morto" (escrito uma vez, raramente lido) para se tornar o contexto vivo que alimenta todo agente. Quando um desenvolvedor abre o Cursor ou Claude Code para implementar uma feature, o que diferencia uma IA que "acerta de primeira" de uma que produz código genérico é, principalmente, a qualidade do contexto disponível.

🎯
Princípio da Documentação como Contexto

Toda decisão importante deve estar documentada em um arquivo pesquisável pela IA. Se um agente precisa adivinhar convenções, padrões ou decisões anteriores, você tem um gap de contexto. A cura: transformar conhecimento tácito do time em arquivos markdown no repositório (CLAUDE.md, AGENTS.md, docs/*.md).

Do Doc Estático ao Doc Vivo (Living Docs)

📄
Era Pré-IA: Doc Estático

Escrito uma vez antes da implementação. Raramente atualizado. Fica desatualizado em semanas. Valor cai rapidamente após o deploy.

🔁
Era IA: Doc Vivo

Gerado e atualizado automaticamente por pipelines. Cada PR pode acionar regeneração de trechos afetados. IA detecta quando docs divergem do código e sinaliza para atualização.

🤖
Era Agêntica: Doc-First Development

Engenheiro escreve o design doc. Agente lê o doc, faz perguntas de esclarecimento, gera implementação + testes. O doc se torna a fonte da verdade, e o código é um artefato derivado.

Os novos padrões: CLAUDE.md e AGENTS.md

Uma convenção emergente em 2024/2025 é criar arquivos dedicados a agentes de IA na raiz do repositório. Esses arquivos são carregados automaticamente no contexto quando a IA trabalha no projeto:

MARKDOWN
CLAUDE.md (raiz do repositório)
# Contexto para Agentes de IA

## Sobre este projeto
Sistema de pagamentos da empresa X, responsável por processar ~2M transações/dia.
Arquitetura: microserviços em Go (core) e TypeScript (edge/BFF).

## Convenções obrigatórias
- **Nomenclatura**: camelCase em TypeScript, snake_case em Go.
- **Erros**: use o pacote interno `pkg/errors` — NUNCA `errors.New` nativo.
- **Testes**: cobertura mínima 80% em arquivos de domínio. Use table-driven tests em Go.
- **Commits**: Conventional Commits (feat:, fix:, chore:, docs:).

## Arquivos sensíveis (NÃO modificar sem aprovação)
- `internal/crypto/*` — código de criptografia homologado (PCI-DSS)
- `migrations/*` — migrações só podem ser adicionadas, nunca editadas
- `.github/workflows/deploy-prod.yml` — revisão obrigatória do SRE

## Decisões arquiteturais chave
- Mensageria: RabbitMQ (não Kafka) — ver ADR-023 para contexto.
- Database: PostgreSQL 15 com particionamento por merchant_id.
- Cache: Redis Cluster com TTL padrão de 5 minutos.

## Como rodar testes localmente
```bash
make test        # unit tests
make test-int    # integration tests (requer docker-compose up)
make lint        # golangci-lint + eslint
```

## Onde procurar contexto adicional
- `docs/adr/` — Architecture Decision Records
- `docs/runbooks/` — Procedimentos operacionais
- `docs/rfcs/` — Request for Comments em aberto
- `CHANGELOG.md` — Histórico de mudanças
⚠️
Cuidado com over-documentation

Um CLAUDE.md gigantesco (>500 linhas) consome orçamento de contexto e pode diluir atenção do modelo. Mantenha-o como "tabela de conteúdos" apontando para docs específicos. Regra prática: CLAUDE.md fica entre 80 e 200 linhas. Detalhes técnicos vão em arquivos referenciados.

3

Formatos de Documentação

Cada tipo de decisão tem um formato ideal. Conhecê-los permite escolher a ferramenta certa — e evita a síndrome de "martelo/prego", onde todo documento vira um wiki page genérico.

PRD — Product Requirements Document

O PRD é o documento dos Produtos: foca no problema do usuário, não na solução. Responde perguntas como "quem são os usuários?", "qual o comportamento atual?", "qual o comportamento desejado?", "quais as métricas de sucesso?". Um bom PRD é testável — você consegue construir soluções diferentes a partir dele.

MARKDOWN
prd-checkout-express.md
# PRD: Checkout Express (1-click)

**Autor:** Ana Souza (PM)
**Status:** Draft → Review → Approved
**Última atualização:** 2025-02-14

## 1. Problema
Taxa de abandono no checkout atual é de 43% (vs. benchmark do mercado de 28%).
Principais causas (research com 500 usuários em Jan/25):
- 58%: "processo longo demais"
- 31%: "precisei digitar cartão de novo"
- 11%: outros

## 2. Usuário-alvo
Usuários recorrentes (2+ compras nos últimos 90 dias) que já têm ao menos um
meio de pagamento salvo. Volume estimado: 240k usuários ativos.

## 3. Solução proposta (sem implementação!)
Permitir que o usuário conclua a compra em um único clique a partir da página
do produto, pulando o fluxo padrão de checkout.

## 4. Requisitos funcionais
- FR-01: Botão "Comprar em 1-click" visível na página do produto
- FR-02: Modal de confirmação com endereço + pagamento pré-selecionados
- FR-03: Usuário pode alterar endereço/pagamento antes de confirmar
- FR-04: Email de confirmação enviado em até 60s

## 5. Requisitos não-funcionais
- NFR-01: Tempo de resposta do botão < 300ms (p95)
- NFR-02: Disponibilidade de 99.95%
- NFR-03: Conformidade com PCI-DSS (sem armazenar CVV)

## 6. Métricas de sucesso
- **Primária:** redução de 20% no abandono de checkout em 90 dias
- **Secundária:** aumento de 15% no LTV dos usuários ativos
- **Guardrail:** taxa de chargeback não pode aumentar mais de 0.3%

## 7. Fora de escopo
- Suporte a parcelamento customizado (próximo quarter)
- Integração com carteiras digitais (Apple Pay, Google Pay) — RFC separado
- Experiência em tablet (foco inicial: desktop + mobile)

## 8. Riscos
- **R1:** Usuários podem acidentalmente comprar. Mitigação: modal de confirmação.
- **R2:** Fraude pode aumentar. Mitigação: antifraude obrigatório em 1-click.

## 9. Prazo
- Discovery/PRD: concluído
- Design técnico (TRD): até 2025-02-28
- Desenvolvimento: 4 sprints (mar-abr/25)
- Launch: 2025-05-05 (com feature flag para 10% do tráfego)

TRD, FRD, User Stories e Epics

🔧
TRD (Technical Requirements Doc)

A tradução técnica do PRD. Descreve APIs, schemas, dependências e implementação. Escrito por engenheiros, revisado pelo tech lead.

⚙️
FRD (Functional Requirements)

Detalhamento funcional exaustivo: fluxos, estados, validações, mensagens de erro. Comum em sistemas regulados (saúde, finanças).

📖
User Stories

"Como [persona], quero [ação], para que [benefício]". Unidade atômica de valor no backlog, pronta para execução em um sprint.

📚
Epics

Agrupamento de stories que entregam um tema maior. Tipicamente 1-3 meses. Permite visão estratégica sem perder rastreabilidade.

RFC — Request for Comments

Herdado da IETF, o RFC é usado para propostas abertas a discussão. É ideal quando existem múltiplas soluções possíveis e você quer input do time antes de comprometer-se. Diferente de um ADR, um RFC pode ser rejeitado.

ADR — Architecture Decision Record

O ADR captura decisões já tomadas em formato conciso e imutável. Popularizado por Michael Nygard, tornou-se padrão para documentar o "porquê" de decisões arquiteturais. ADRs nascem pequenos (1-2 páginas) e vivem para sempre no repositório.

MARKDOWN
docs/adr/0023-mensageria-rabbitmq.md
# ADR-0023: Adoção de RabbitMQ como broker primário

- **Status:** Accepted
- **Data:** 2024-11-07
- **Decisores:** João (Tech Lead), Marina (SRE), Pedro (Arquiteto)
- **Contexto técnico:** Sistema de pagamentos, 2M tx/dia, picos de 10x

## Contexto
Precisamos de um broker de mensageria para desacoplar o serviço de ingestão
do serviço de processamento. Critérios de avaliação:
- Latência < 50ms (p99)
- Suporte a DLQ (Dead Letter Queue) nativo
- Operação por time interno sem SaaS obrigatório
- Maturidade e ecossistema em Go

## Opções consideradas
1. **Apache Kafka** — alto throughput, mas overhead operacional alto
2. **AWS SQS** — managed, mas vendor lock-in e latência maior
3. **RabbitMQ** — protocolo AMQP, DLQ nativo, operação conhecida pelo time
4. **NATS** — leve, mas ecossistema Go menos maduro em 2024

## Decisão
Adotar **RabbitMQ 3.13** como broker primário para mensageria entre serviços.

## Consequências

### Positivas
- Time tem 5+ anos de experiência operando RabbitMQ
- DLQ e retry exponencial nativos reduzem código boilerplate
- Protocolo AMQP permite clientes em qualquer linguagem

### Negativas (aceitas)
- Throughput menor que Kafka (~40k msgs/s vs 200k+) — não é gargalo no volume atual
- Replicação entre DCs mais complexa — mitigado com federation
- Escala vertical primária — planejar sharding se >100k msgs/s

## Revisão
Reavaliar decisão se:
- Volume ultrapassar 80k msgs/s sustentado
- Surgir requisito de event sourcing/replay por >7 dias
- Time perder expertise operacional em RabbitMQ

Engineering Guidelines

Guidelines são o DNA operacional do time. Cobrem workflow (Git, branches), coding standards (linting, naming), code review (o que aprovar/rejeitar), PRs (template, tamanho máximo) e testing (pirâmide, cobertura, fixtures). São a fonte de verdade para a IA agente — se uma guideline não existe, a IA inventa uma.

C4 Model: Arquitetura em 4 Níveis

Criado por Simon Brown, o C4 Model propõe documentar arquitetura em zoom progressivo:

🌍
Nível 1: Context

Seu sistema + sistemas externos que interagem. Público-alvo: executivos, POs, stakeholders não-técnicos.

🏢
Nível 2: Containers

Aplicações e databases que compõem o sistema. Público: arquitetos, tech leads. Mostra tecnologias e protocolos.

📦
Nível 3: Components

Módulos/classes dentro de um container. Público: engenheiros do time. Detalha responsabilidades.

🔬
Nível 4: Code

Classes, interfaces, fluxos de chamada. Geralmente gerado automaticamente do código — raramente escrito manualmente.

Operações: Runbooks, Playbooks e Postmortems

📘
Runbook

Passo-a-passo para situações rotineiras: deploy, rollback, rotação de certificados. Foco: reduzir variância operacional.

🚨
Playbook

Guia para situações excepcionais: alta latência, queda de serviço, ataque. Foco: decisões sob pressão com diagnóstico.

🔍
Postmortem

Análise após um incidente: timeline, causa raiz, action items. Blameless: foca em sistema, não em pessoas.

4

Geração Automatizada de Documentação

Com IA, você pode gerar rascunhos de design docs em segundos. A chave é prompts estruturados que obrigam a IA a seguir o formato esperado e preencher os campos corretos. O output deve ser sempre revisado por um humano — a IA produz o esqueleto; você coloca o julgamento.

Pipeline de Geração de ADR com OpenAI

O script abaixo automatiza a geração de ADRs a partir de notas de reunião. Você cola as notas, a IA extrai a decisão, contexto, alternativas e consequências no formato padrão.

PYTHON
gerar_adr.py
"""
Gerador automatizado de ADR (Architecture Decision Record) a partir de notas livres.
Uso: python gerar_adr.py --input notas_reuniao.txt --output docs/adr/
"""

import argparse
import os
from datetime import date
from pathlib import Path
from openai import OpenAI

# Cliente OpenAI (define OPENAI_API_KEY no ambiente)
client = OpenAI()

# Template/prompt estruturado — força o formato ADR
ADR_SYSTEM_PROMPT = """
Você é um arquiteto de software sênior especializado em documentação técnica.
Sua tarefa é transformar notas livres de uma reunião em um ADR formal.

Regras obrigatórias:
1. Use EXATAMENTE o formato Markdown abaixo (não adicione seções extras).
2. Se alguma informação estiver ausente, escreva "[A preencher: ...]" — NUNCA invente.
3. Seja conciso: cada seção em no máximo 4-5 linhas.
4. Use voz ativa e tempo presente.
5. O status inicial é sempre "Proposed".

Formato:
# ADR-{id}: {título curto e descritivo}

- **Status:** Proposed
- **Data:** {data_atual}
- **Decisores:** {nomes extraídos}

## Contexto
{Situação que motivou a decisão — 3 a 5 linhas}

## Opções consideradas
1. **{Opção 1}** — {prós e contras em 1 linha}
2. **{Opção 2}** — {prós e contras em 1 linha}
N. ...

## Decisão
{Qual opção foi escolhida e justificativa principal — 2 a 4 linhas}

## Consequências

### Positivas
- {Benefício 1}
- {Benefício 2}

### Negativas (aceitas)
- {Custo 1}
- {Custo 2}
"""

def gerar_adr(notas: str, proximo_id: int) -> str:
    """Gera um ADR formatado a partir de notas brutas."""
    resposta = client.chat.completions.create(
        model="gpt-4o",
        temperature=0.2,  # Baixa para maior consistência no formato
        messages=[
            {"role": "system", "content": ADR_SYSTEM_PROMPT},
            {"role": "user", "content": (
                f"ID do ADR: {proximo_id:04d}\n"
                f"Data atual: {date.today().isoformat()}\n\n"
                f"Notas brutas:\n{notas}"
            )}
        ]
    )
    return resposta.choices[0].message.content

def proximo_id(pasta: Path) -> int:
    """Descobre o próximo número de ADR disponível."""
    pasta.mkdir(parents=True, exist_ok=True)
    existentes = [f.stem.split('-')[0] for f in pasta.glob("*.md")]
    ids = [int(e) for e in existentes if e.isdigit()]
    return max(ids, default=0) + 1

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--input", required=True, help="Arquivo com as notas")
    parser.add_argument("--output", default="docs/adr/", help="Pasta de destino")
    args = parser.parse_args()

    pasta = Path(args.output)
    notas = Path(args.input).read_text(encoding="utf-8")
    novo_id = proximo_id(pasta)

    print(f"🧠 Gerando ADR-{novo_id:04d} a partir de {args.input}...")
    conteudo = gerar_adr(notas, novo_id)

    # Extrai o título da primeira linha do markdown para o nome do arquivo
    titulo = conteudo.split("\n")[0].replace("#", "").strip()
    slug = "-".join(titulo.lower().split()[2:7])  # pula "ADR-XXXX:"
    slug = "".join(c if c.isalnum() or c == "-" else "" for c in slug)

    arquivo = pasta / f"{novo_id:04d}-{slug}.md"
    arquivo.write_text(conteudo, encoding="utf-8")
    print(f"✅ ADR salvo em {arquivo}")

if __name__ == "__main__":
    main()
▶ Passos para executar
  1. Instale a dependência: pip install openai
  2. Configure sua chave: export OPENAI_API_KEY="sk-..." (Linux/Mac) ou set OPENAI_API_KEY=sk-... (Windows).
  3. Crie um arquivo notas_reuniao.txt com qualquer conteúdo livre da reunião (bullets, frases soltas, trechos de chat).
  4. Execute: python gerar_adr.py --input notas_reuniao.txt
  5. Revise o ADR gerado em docs/adr/NNNN-titulo.md e ajuste campos marcados com "[A preencher: ...]".
  6. Abra um PR com o arquivo para revisão formal pelo time.

Assistente Conversacional para Docs

Para organizações com muita documentação existente, criar um assistente RAG que responde perguntas baseadas em todos os docs é transformador. Um engenheiro novo pergunta "por que usamos RabbitMQ e não Kafka?" e recebe a resposta citando o ADR-0023 diretamente.

PYTHON
assistente_docs.py
"""
Assistente conversacional para documentação técnica.
Indexa docs/**/*.md e responde perguntas citando as fontes.
"""

from pathlib import Path
from openai import OpenAI
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain.text_splitter import MarkdownTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma

client = OpenAI()

# ── 1. Carregamento dos documentos ─────────────────────────────
def carregar_docs(pasta: str = "docs/") -> list:
    """Carrega todos os .md da pasta de documentação."""
    loader = DirectoryLoader(
        pasta,
        glob="**/*.md",
        loader_cls=TextLoader,
        loader_kwargs={"encoding": "utf-8"}
    )
    docs = loader.load()
    print(f"📚 Carregados {len(docs)} documentos.")
    return docs

# ── 2. Chunking semântico ──────────────────────────────────────
def dividir_em_chunks(docs: list) -> list:
    """Quebra os documentos em pedaços que cabem no contexto do modelo."""
    splitter = MarkdownTextSplitter(
        chunk_size=1000,       # ~250 tokens por chunk
        chunk_overlap=150       # overlap preserva contexto entre chunks
    )
    return splitter.split_documents(docs)

# ── 3. Indexação vetorial ──────────────────────────────────────
def construir_indice(chunks: list, pasta_db: str = "./chroma_docs") -> Chroma:
    """Cria o vector store com embeddings dos chunks."""
    embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
    db = Chroma.from_documents(
        chunks,
        embedding=embeddings,
        persist_directory=pasta_db
    )
    print(f"💾 Índice persistido em {pasta_db}")
    return db

# ── 4. Busca + Resposta com citações ───────────────────────────
def responder(pergunta: str, db: Chroma, k: int = 4) -> str:
    """Busca os k chunks mais relevantes e pede ao LLM uma resposta com citações."""
    # Recupera chunks semanticamente similares à pergunta
    resultados = db.similarity_search(pergunta, k=k)

    # Monta o contexto com citações
    contexto = "\n\n".join(
        f"[fonte: {r.metadata.get('source', 'desconhecido')}]\n{r.page_content}"
        for r in resultados
    )

    prompt = f"""Você é um assistente técnico da empresa. Responda à pergunta
usando APENAS o contexto fornecido. Cite as fontes como [fonte: arquivo.md]
ao longo da resposta. Se o contexto não responder a pergunta, diga isso
explicitamente — NUNCA invente informações.

CONTEXTO:
{contexto}

PERGUNTA: {pergunta}

RESPOSTA:"""

    resposta = client.chat.completions.create(
        model="gpt-4o-mini",    # mais barato, suficiente para RAG
        temperature=0.1,
        messages=[{"role": "user", "content": prompt}]
    )
    return resposta.choices[0].message.content

# ── Main ───────────────────────────────────────────────────────
if __name__ == "__main__":
    docs = carregar_docs("docs/")
    chunks = dividir_em_chunks(docs)
    db = construir_indice(chunks)

    # Loop interativo
    print("\n🤖 Assistente de docs pronto. Digite 'sair' para encerrar.\n")
    while True:
        pergunta = input("❓ Pergunta: ").strip()
        if pergunta.lower() in {"sair", "exit", "quit"}:
            break
        print("\n" + responder(pergunta, db) + "\n")
▶ Passos para executar
  1. Instale as dependências: pip install openai langchain langchain-openai langchain-community chromadb
  2. Configure sua chave: export OPENAI_API_KEY="sk-..."
  3. Organize sua documentação em uma pasta docs/ contendo arquivos .md (ADRs, RFCs, runbooks, etc).
  4. Execute: python assistente_docs.py
  5. Faça perguntas em linguagem natural. Exemplo: "Por que escolhemos RabbitMQ?" ou "Como faço rollback em produção?"
  6. A primeira execução indexa tudo (lento). Execuções seguintes reusam ./chroma_docs (rápido).
💎
Ganho real em organizações

Organizações que adotam assistentes RAG para documentação reportam redução de 40-60% no tempo de onboarding de novos engenheiros e queda drástica em perguntas repetitivas no Slack. O ROI costuma ser positivo em 2-3 meses.

5

Manutenção e Referenciação de Documentação

Documentação desatualizada é pior que documentação ausente — ela induz ao erro. Manter docs vivos é um problema estrutural; IA ajuda de três formas: (1) detectando drift, (2) atualizando automaticamente e (3) desabilitando docs obsoletos.

Detecção de Drift entre Código e Docs

Um pipeline CI pode executar, a cada PR, um job que pergunta à IA: "o código alterado está coerente com a documentação atual?". Se houver divergência, a IA gera sugestão de atualização e adiciona comentário no PR.

YAML
.github/workflows/docs-drift.yml
# Detecta drift entre código e documentação a cada Pull Request
name: Docs Drift Check

on:
  pull_request:
    paths:
      - 'src/**'
      - 'docs/**'

jobs:
  drift-check:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write  # para comentar no PR
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # precisamos do histórico para ver o diff

      - name: Gerar diff do PR
        id: diff
        run: |
          git diff origin/${{ github.base_ref }}...HEAD -- src/ > code.diff
          git diff origin/${{ github.base_ref }}...HEAD -- docs/ > docs.diff

      - name: Perguntar à IA se há drift
        id: ai-check
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
        run: |
          python - << 'EOF'
          import os, json
          from openai import OpenAI
          client = OpenAI()

          code_diff = open("code.diff").read()
          docs_diff = open("docs.diff").read()

          if not code_diff.strip():
              print("Sem mudanças em src/, nada a verificar.")
              exit(0)

          prompt = f"""Analise o diff de código abaixo e verifique se a documentação
          foi atualizada adequadamente. Responda em JSON:
          {{
            "drift_detected": true|false,
            "severity": "low|medium|high",
            "affected_docs": ["caminho/arquivo.md", ...],
            "suggested_changes": "texto explicando o que atualizar"
          }}

          CÓDIGO ALTERADO:
          {code_diff[:8000]}

          DOCS ALTERADOS:
          {docs_diff[:4000] or "(nenhum)"}
          """

          resp = client.chat.completions.create(
              model="gpt-4o-mini",
              response_format={"type": "json_object"},
              messages=[{"role": "user", "content": prompt}]
          )
          result = json.loads(resp.choices[0].message.content)

          with open("drift_result.json", "w") as f:
              json.dump(result, f)

          if result["drift_detected"]:
              print(f"::warning::Drift detectado: {result['severity']}")
          EOF

      - name: Comentar no PR se houver drift
        if: always()
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            if (!fs.existsSync('drift_result.json')) return;
            const r = JSON.parse(fs.readFileSync('drift_result.json', 'utf8'));
            if (!r.drift_detected) return;

            const body = `### 📚 Docs Drift Detectado (${r.severity})

            **Documentos afetados:** ${r.affected_docs.join(', ')}

            **Sugestão:**
            ${r.suggested_changes}

            _Este comentário foi gerado automaticamente pelo workflow \`docs-drift\`._`;

            github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              body
            });

Referenciação Cruzada de Documentos

Documentos isolados perdem valor; documentos interligados criam um grafo de conhecimento. Padrões úteis:

🔗
Links relativos

Use [ADR-023](./0023-rabbitmq.md) em vez de URLs absolutas. Sobrevive a renames de repositório.

🏷️
Tags e frontmatter

Adicione metadados YAML no topo: tags: [auth, security]. Permite buscas por tópico.

📅
Status explícito

Campo status: Deprecated permite filtrar docs ativos vs. obsoletos em queries e indexação.

↩️
Substituições (Supersedes)

ADR-045 supersedes: ADR-023. Permite IA entender qual é a decisão vigente.

Desabilitação e Deprecação

Apagar documentação é tentador, mas destrói contexto histórico. A prática madura é desabilitar: marcar como obsoleto, adicionar link para o substituto, e configurar a indexação do RAG para dar peso menor (ou excluir) esses documentos.

💡
Template de cabeçalho para doc deprecado

Adicione este bloco no topo de qualquer doc obsoleto:

---
status: Deprecated
deprecated_at: 2025-01-15
superseded_by: ./0045-nova-decisao.md
---

> ⚠️ **DOCUMENTO OBSOLETO**
> Esta decisão foi substituída em 2025-01-15.
> Consulte ADR-045 para o estado atual.
🛑
Não apague docs históricos

Mesmo docs "errados" carregam contexto valioso: mostram o que foi considerado e por que mudou. Quando um novo engenheiro ou agente de IA pergunta "por que não usamos X?", a resposta pode estar num doc deprecado. Arquive, não delete.