Pular para o conteúdo

Padrão de documentação

Status: canônico · Escopo: Todo arquivo .md do repositório · Atualizado em: 2026-07-28

Este documento define como todo .md deste repositório deve ser escrito. Não é sugestão: as regras aqui são verificadas por pnpm docs:lint, que faz parte do gate bloqueante pnpm quality.

Vale para quem escreve à mão e para agentes de IA. Se uma regra aqui atrapalha um caso legítimo, mude a regra e o gate juntos — nunca contorne em silêncio.

1. Idioma: documentação em português, código em inglês

Seção intitulada “1. Idioma: documentação em português, código em inglês”

A regra mais importante, e ela é absoluta:

O queIdioma
Prosa de documentação (.md)Português
Código, identificadores, nomes de arquivo, commits, comentários no códigoInglês
Termos técnicos, nomes de produto, caminhos, comandos, trechos de código dentro de .mdInglês (permanecem como são)

Ou seja: um .md explica em português, mas cita runInTenantScope, apps/api-worker/src/, pnpm boundaries e blocos de código em inglês. O linter ignora blocos de código, código inline, URLs e caminhos ao avaliar idioma — ele julga só a prosa.

Não traduza termos consagrados. Escreva “endpoint”, “deploy”, “gate”, “webhook”, “commit” — traduzir prejudica a busca e conflita com o código.

Todo .md fora de docs/archive/ traz uma linha de metadados logo depois do H1:

# Título do documento
**Status:** `canônico` · **Escopo:** A que parte do produto ou do código se aplica · **Atualizado em:** 2026-07-28

Significado de cada status:

StatusSignificadoManutenção
canônicoGuia atual e confiávelRevisar quando o assunto mudar
corrigirConteúdo útil com trechos desatualizadosCorrigir ao tocar no assunto
estacionadoCapacidade que ainda queremos, fora do foco atualNenhuma
históricoSuperado; valor de arqueologiaNenhuma — não usar como guia
geradoProduzido por script a partir do códigoNunca editar à mão

Por que não YAML frontmatter. A primeira versão deste padrão exigia um bloco --- no topo de todo arquivo. O GitHub renderiza esse bloco como uma tabela crua acima do título, então justamente os documentos mais visíveis abriam com titulo: … status: … escopo: … na cara do leitor. Metadado que suja a página que ele descreve é o gate servindo a si mesmo em vez de servir quem lê. A linha acima resolve os dois lados: renderiza como prosa e continua sendo verificável por pnpm docs:lint.

README.md não leva linha de metadados. É a capa do repositório — o status dela é o status do projeto, e qualquer metadado ali aparece antes do que o leitor veio ver. A capa também escapa das regras de §3 que pressupõem documento interno: um README bem feito abre com hero centralizado (H1 com o nome, H3 com a tagline, badges), não com parágrafo corrido. Idioma, tabelas, links e seções vazias continuam valendo.

Todo documento segue a mesma forma:

  1. Início — um # H1 com o título, seguido de um parágrafo de abertura que responde: o que este documento resolve, e para quem? Sem enrolação, sem “neste documento iremos”.
  2. Meio — seções ## numeradas quando a ordem importa (## 1., ## 2.), ou sem número quando são independentes. Subseções ### só dentro de uma ##.
  3. Fim — uma seção final de saída: para onde ir depois, referências, ou estado/lacunas conhecidas. Documento não termina no meio de uma tabela.

Regras verificadas pelo linter:

  • Exatamente um H1, e ele abre o documento.
  • Sem pular nível de heading (## não pode ser seguido de ####).
  • Toda tabela tem linha de cabeçalho e separador — sem linha de tabela órfã.
  • Todo link relativo resolve.
  • Documento vivo não linka para docs/archive/.
  • Frase curta, voz ativa. “O gate falha quando…” e não “É feita uma verificação na qual…”.
  • Tabela para comparar, lista para enumerar, prosa para explicar o porquê. Escolha uma; não repita a mesma informação em dois formatos.
  • Afirme o estado real, com data. “Construído mas não entrega — falta TWILIO_*” vale mais que “parcialmente implementado”.
  • Registre o porquê, não só o quê. O código já mostra o quê. Documento existe para a decisão.
  • Sem “TODO”, sem seção vazia, sem placeholder. Se não tem conteúdo, não tem seção.
  • Emoji ajuda a escanear, não a decorar. Use em título de seção (## 🎯 O problema), como marcador de status em tabela (✅ ⚠️ ❌ 📦 📚) e como ícone de linha. Não use no meio da prosa.
  • Formate para ser lido. Tabela alinhada, bloco de código com linguagem declarada, diagrama mermaid quando o fluxo importa mais que o texto, > [!NOTE] e > [!IMPORTANT] para destacar o que não pode passar batido. Documento é interface, não despejo de texto.
  • Números e nomes precisos. Cite arquivo:linha quando aponta para código.
TipoOndeCiclo de vida
Escopo e direção do produtoDIRECTION.md (raiz)Vivo, revisado sempre
Mapa do códigoARCHITECTURE.md (raiz)Vivo
Porta de entrada do repositórioREADME.md (raiz)Vivo
Índice das pastasdocs/README.mdPorta — live/ · parked/ · text-pipeline/ · archive/
Guia do dia a diadocs/live/**Vivo ou corrigir
Decisão arquiteturaldocs/live/adr/NNNN-titulo.mdImutável
Pipeline de texto/chat (não voz)docs/text-pipeline/Fora do foco diário; pode citar archive/
Ainda queremos, fora do focodocs/parked/estacionado; pode citar archive/
Superadodocs/archive/Sem manutenção, sem metadados obrigatórios
Gerado do códigodocs/gerado/Nunca editar

Decisão arquitetural não vira parágrafo dentro de um documento grande — vira um ADR próprio, pequeno e imutável. Prosa que envelhece foi a causa da bagunça que este padrão corrige: 66 documentos sem dono e sem validade.

Formato de docs/live/adr/NNNN-titulo-curto.md:

# ADR 0001 — Título da decisão
**Status:** `canônico` · **Escopo:** O que a decisão afeta · **Atualizado em:** AAAA-MM-DD
Uma ou duas frases dizendo o que este ADR registra e por que ele existe.
## Contexto
O que era verdade quando decidimos, e qual pressão gerou a decisão.
## Decisão
O que foi decidido, em uma ou duas frases afirmativas.
## Consequências
O que fica mais fácil, o que fica mais difícil, o que passa a ser proibido.
## Alternativas descartadas
Cada alternativa e por que perdeu.

Um ADR nunca é editado para “atualizar”. Se a decisão mudou, escreva outro ADR e marque o antigo com status histórico mais a linha Substituído por ADR-NNNN.

O linter rejeita seções do tipo “Fora do plano”, “Não planejado” ou “Alternativas descartadas” em qualquer documento que não seja um ADR. O motivo é critério de parada: uma lista de tecnologias que não usamos é infinita. Se cabe NestJS e Nx, por que não Express, Fastify, Prisma, Kafka, MongoDB? Sem linha divisória, a lista cresce, se espalha e vira ruído — exatamente o que aconteceu: a mesma lista estava repetida em quatro documentos vivos, inclusive no README.md.

A regra que limita: registra-se uma alternativa rejeitada uma única vez, dentro do ADR da decisão que ela perdeu, e só se ela foi de fato proposta e custou deliberação. Isso é limitado por definição — o conjunto de coisas realmente consideradas é pequeno — e mantém o valor que importa: impedir que a mesma pergunta seja re-litigada do zero.

Documento vivo descreve o que é. O que não é fica no ADR, com o porquê.