Documentação Técnica
Por Que Documentação É Multiplicador de Impacto
Muitos engenheiros veem documentação como tarefa menor — algo que “deveria ser feito” mas nunca é prioridade. Na prática, documentação é multiplicador de impacto: uma boa ADR economiza semanas de discussão futura, um runbook bem escrito reduz o MTTR (Mean Time to Recovery) de horas para minutos, e uma RFC clara alinha um time inteiro antes de uma linha de código ser escrita.
Impacto da documentação no ciclo de vida do software:
Onboarding sem docs: 2-4 semanas para novo dev ser produtivo
Onboarding com docs: 3-5 dias para novo dev ser produtivo
Incidente sem runbook: 1-4 horas de MTTR (debugging ao vivo)
Incidente com runbook: 10-30 minutos de MTTR (passos claros)
Decisão sem ADR: Relitigada a cada 6 meses por novos membros
Decisão com ADR: "Leia ADR-042, o contexto está documentado"
Tipos de Documentação Técnica
Taxonomia Completa
Tipo | Audiência | Quando escrever
---------------------|--------------------|--------------------------
ADR | Time de eng. | Cada decisão arquitetural
RFC | Org. de eng. | Mudanças grandes/cross-team
Runbook | On-call/SRE | Cada alerta novo
API Reference | Consumidores | Cada endpoint/função pública
README | Novos devs | Início do projeto + manutenção
Onboarding Guide | Novos membros | Quando time cresce
How-to Guide | Devs do time | Tarefas recorrentes
Architecture Doc | Time + liderança | Visão geral do sistema
Post-mortem | Toda org. | Após incidentes
Changelog | Usuários/devs | Cada release
ADR — Architecture Decision Records
O Formato
ADRs são documentos curtos (1-2 páginas) que registram uma decisão arquitetural, o contexto, as alternativas e as consequências.
# ADR-042: Adotar PostgreSQL como Banco Principal
## Status
Aceito (2024-01-15)
Substitui: ADR-003 (MySQL como banco principal)
## Contexto
O sistema evoluiu para precisar de:
- Queries complexas com dados semi-estruturados (JSON)
- Full-text search nativo (sem Elasticsearch para MVP)
- CTEs recursivas para hierarquia de categorias
- Suporte a tipos geográficos (PostGIS) para feature futura
O time tem 3 devs com experiência em MySQL e 1 em PostgreSQL.
O volume atual é ~500 RPS com projeção de 5K RPS em 12 meses.
## Decisão
Adotaremos PostgreSQL 16 como banco de dados principal.
## Justificativa
| Critério | PostgreSQL | MySQL 8 | MongoDB |
|--------------------|---------------------|-------------------|-----------------|
| JSONB | Nativo, indexável | JSON (sem índice) | Nativo |
| Full-text search | Built-in (ts_vector)| Limitado | Atlas Search |
| CTEs recursivas | Completo | Limitado | Não aplicável |
| ACID | Completo | Completo | Parcial |
| Extensões | PostGIS, pg_trgm | Limitadas | N/A |
| Curva do time | Média | Baixa | Média |
Trade-off aceito: curva de aprendizado para 3 devs do MySQL (~2 semanas).
## Alternativas Rejeitadas
1. **MySQL 8**: Familiar ao time, mas JSONB inferior, FTS limitado
2. **MongoDB**: Bom para dados flexíveis, mas perdemos ACID completo
e JOINs eficientes. Complexidade operacional maior.
3. **CockroachDB**: PostgreSQL-compatible e distribuído, mas custo
operacional alto para o estágio atual do projeto.
## Consequências
- Time precisa de 1-2 semanas de adaptação
- Configurar pgbouncer para connection pooling
- Backups com pg_dump (MVP) → WAL archiving (produção)
- Migrations com golang-migrate ou Prisma
- Monitoramento com pg_stat_statements
Quando Escrever um ADR
ESCREVA um ADR quando:
✓ Escolher uma tecnologia (banco, framework, linguagem, cloud provider)
✓ Definir um padrão arquitetural (event-driven, CQRS, microserviço)
✓ Escolher entre abordagens técnicas com trade-offs significativos
✓ Decidir NÃO fazer algo (decisões de exclusão são valiosas)
✓ A decisão afeta múltiplos módulos ou times
NÃO escreva um ADR quando:
✗ É uma decisão facilmente reversível (nome de variável, lib pequena)
✗ É um padrão já estabelecido no time (não precisa re-decidir)
✗ É uma decisão de produto, não técnica
Tooling para ADRs
# adr-tools (CLI para gerenciar ADRs)
brew install adr-tools
adr new "Adotar PostgreSQL como banco principal"
# Cria: docs/adr/0042-adotar-postgresql-como-banco-principal.md
adr list # Lista todos os ADRs
adr link 42 "Substitui" 3 # Linka ADR-042 como substituto de ADR-003
# Estrutura no repositório:
docs/
adr/
0001-usar-typescript.md
0002-adotar-monorepo.md
0003-mysql-como-banco-principal.md # Status: Substituído por ADR-042
...
0042-adotar-postgresql-como-banco-principal.md
RFC — Request for Comments
Quando Usar RFC vs ADR
ADR: Decisão pontual já tomada (ou prestes a ser tomada)
→ 1-2 páginas, registro histórico
RFC: Proposta de mudança significativa que precisa de input coletivo
→ 3-10 páginas, design técnico, plano de execução
→ Geralmente resulta em 1+ ADRs após aprovação
Template de RFC Completo
# RFC: Migração para Event-Driven Architecture
## Metadados
- **Autor**: Lucas Gomes
- **Data**: 2024-02-01
- **Status**: Em Discussão → Aceito / Rejeitado
- **Reviewers**: @maria, @pedro, @tech-lead
- **Deadline para comentários**: 2024-02-15
## 1. Problema
O endpoint POST /orders leva 8s (P99) porque executa 5 operações
síncronas sequenciais:
1. Validação de estoque (InventoryService) — ~200ms
2. Cálculo de frete (ShippingService) — ~1500ms (API externa)
3. Processamento de pagamento (PaymentService) — ~3000ms
4. Envio de email de confirmação (EmailService) — ~2000ms
5. Atualização de analytics (AnalyticsService) — ~1300ms
Consequências atuais:
- Timeout em 15% das requests (SLA: < 1%)
- UX degradada: usuário espera 8s+ na tela de checkout
- Falha em qualquer etapa cancela todo o pedido (falta de resiliência)
## 2. Proposta
Migrar operações não-críticas para processamento assíncrono via
RabbitMQ, mantendo apenas validação e pagamento no caminho síncrono.
Fluxo proposto:
POST /orders → Validar estoque (síncrono, ~200ms) → Processar pagamento (síncrono, ~3000ms) → Retornar 202 Accepted (~3200ms total) → Publicar evento OrderCreated → Consumer: enviar email → Consumer: atualizar analytics → Consumer: calcular frete e agendar entrega
## 3. Design Técnico
### 3.1 Topologia de Mensageria
- Exchange: `orders.events` (tipo: topic)
- Routing keys: `order.created`, `order.paid`, `order.shipped`
- Queues: uma por consumer, com dead-letter queue individual
- Retry policy: 3 tentativas, exponential backoff (1s, 5s, 25s)
### 3.2 Schema do Evento
```json
{
"eventId": "uuid-v4",
"type": "order.created",
"timestamp": "2024-02-01T10:00:00Z",
"version": 1,
"data": {
"orderId": "ord_abc123",
"userId": "usr_xyz789",
"items": [...],
"total": 299.90
},
"metadata": {
"correlationId": "req_def456",
"source": "order-service"
}
}
4. Riscos e Mitigações
| Risco | Probabilidade | Impacto | Mitigação |
|---|---|---|---|
| Mensagem perdida | Baixa | Alto | Publisher confirms + DLQ |
| Consistência eventual visível | Média | Médio | Optimistic UI + polling |
| RabbitMQ indisponível | Baixa | Alto | Fallback síncrono + alertas |
| Mensagem processada 2x | Média | Médio | Idempotência em consumers |
5. Plano de Rollout
- Semana 1: Setup RabbitMQ + infra de consumers + monitoramento
- Semana 2: Migrar envio de email para async (menor risco)
- Semana 3: Migrar analytics para async
- Semana 4: Migrar cálculo de frete para async
- Semana 5: Remover chamadas síncronas antigas + cleanup
Feature flag USE_ASYNC_ORDER_PROCESSING para rollback instantâneo.
6. Métricas de Sucesso
- Latência P99 de POST /orders: < 500ms (atual: 8s)
- Taxa de timeout: < 0.1% (atual: 15%)
- Disponibilidade do fluxo de checkout: > 99.9%
- Tempo de processamento de email: < 30s após criação do pedido
7. Alternativas Consideradas
- Otimizar chamadas síncronas: Paralelizar com Promise.all. Rejeitado: ainda falha se uma etapa falhar, não resolve resiliência.
- AWS SQS: Mais simples, mas vendor lock-in e sem routing avançado.
- Apache Kafka: Mais robusto, mas complexidade operacional excessiva para nosso volume atual (~500 RPS).
---
## O Framework Diátaxis
O Diátaxis (criado por Daniele Procida) organiza documentação em **4 quadrantes** baseados em dois eixos: teoria vs prática e aprendizado vs trabalho.
APRENDIZADO TRABALHO (estudando) (fazendo) ┌───────────────────┬───────────────────────┐ PRÁTICA │ │ │ (passos) │ TUTORIALS │ HOW-TO GUIDES │ │ (aprender) │ (resolver) │ │ │ │ ├───────────────────┼───────────────────────┤ TEORIA │ │ │ (conceito) │ EXPLANATION │ REFERENCE │ │ (entender) │ (consultar) │ │ │ │ └───────────────────┴───────────────────────┘
### Os 4 Tipos na Prática
-
TUTORIAL (learning-oriented) → Guia passo-a-passo para INICIANTE aprender fazendo → “Crie sua primeira API com NestJS em 15 minutos” → O autor controla o caminho, o leitor segue → Foco: sucesso garantido, experiência positiva
-
HOW-TO GUIDE (task-oriented) → Guia para resolver um PROBLEMA ESPECÍFICO → “Como configurar autenticação JWT no NestJS” → Assume que o leitor já conhece o básico → Foco: resolver, não ensinar
-
EXPLANATION (understanding-oriented) → Explicação conceitual para ENTENDER o porquê → “Como o event loop do Node.js funciona” → Não tem passos — é discussão, contexto, história → Foco: modelo mental, razões, trade-offs
-
REFERENCE (information-oriented) → Descrição técnica completa e PRECISA → “API Reference: todos os métodos do OrderService” → Estruturada por consistência, não por narrativa → Foco: acurácia, completude, fácil de buscar
### Erros Comuns ao Misturar Tipos
ERRO: Tutorial que vira reference → “Passo 1: use o método createUser(data: UserData): Promise<User> que aceita os seguintes 47 parâmetros…” → O iniciante se perde. Reference vai na API docs, não no tutorial.
ERRO: How-to que vira explanation → “Para configurar o Redis, primeiro entenda que Redis é um data structure server criado por Salvatore Sanfilippo em 2009…” → Quem busca how-to quer RESOLVER, não estudar história.
ERRO: Reference que tenta ser tutorial → “Array.map() — vamos aprender a usar map! Primeiro, imagine uma lista de compras…” → Reference é para CONSULTAR, não para ensinar do zero.
---
## Escrevendo READMEs Eficazes
### Template para Projetos de Software
```markdown
# Nome do Projeto
Descrição de uma linha: o que faz e para quem.
## Quick Start
Comandos mínimos para rodar o projeto:
git clone <repo>
cd <projeto>
cp .env.example .env
docker compose up -d
npm install
npm run dev
## Arquitetura
Diagrama de alto nível (C4 level 1 ou 2).
## Desenvolvimento
### Pré-requisitos
- Node.js >= 20
- Docker e Docker Compose
- PostgreSQL 16 (ou use o docker-compose)
### Comandos Úteis
- npm run dev → servidor de desenvolvimento
- npm test → roda testes
- npm run lint → linting
- npm run build → build de produção
- npm run migration → roda migrations pendentes
### Variáveis de Ambiente
Documentar CADA variável do .env.example com descrição e exemplo.
## Deploy
Como fazer deploy (CI/CD pipeline, ambientes, aprovações).
## ADRs
Link para docs/adr/ com as decisões arquiteturais.
## Contribuindo
Link para CONTRIBUTING.md.
Documentação de API
OpenAPI / Swagger — Melhores Práticas
# Não documente apenas o "happy path"
paths:
/orders:
post:
summary: Criar novo pedido
description: |
Cria um pedido com os itens fornecidos. O estoque é
verificado sincronamente e o pagamento processado via
gateway. Operações assíncronas (email, analytics) são
disparadas via evento OrderCreated.
**Idempotência**: Use o header X-Idempotency-Key para
garantir que retries não criem pedidos duplicados.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateOrderRequest'
examples:
minimal:
summary: Pedido mínimo
value:
items: [{ productId: "prod_123", quantity: 1 }]
complete:
summary: Pedido completo com cupom
value:
items: [{ productId: "prod_123", quantity: 2 }]
couponCode: "DESCONTO10"
shippingAddress:
zipCode: "01310-100"
responses:
'202':
description: Pedido aceito para processamento
'400':
description: Dados inválidos
content:
application/json:
examples:
missing_items:
value:
error: "VALIDATION_ERROR"
message: "O campo 'items' é obrigatório"
details: [{ field: "items", rule: "required" }]
'409':
description: Conflito de estoque
content:
application/json:
examples:
out_of_stock:
value:
error: "STOCK_UNAVAILABLE"
message: "Produto prod_123 sem estoque"
availableQuantity: 0
'422':
description: Regra de negócio violada
'429':
description: Rate limit excedido
headers:
Retry-After:
description: Segundos para aguardar
schema:
type: integer
Runbooks para Resposta a Incidentes
Estrutura de um Runbook
# Runbook: Alerta "High Database Connection Pool Usage"
## Severidade: P2 (Alta)
## Tempo esperado de resolução: 10-15 minutos
## Último teste: 2024-01-20
## Sintomas
- Alerta no Grafana: `db_pool_active_connections > 80%`
- Usuários reportam lentidão ou timeouts
- Logs com `Error: connection pool exhausted`
## Diagnóstico
### 1. Verificar métricas atuais
SELECT count(*) as total,
state,
wait_event_type
FROM pg_stat_activity
GROUP BY state, wait_event_type
ORDER BY total DESC;
### 2. Identificar queries lentas
SELECT pid, now() - pg_stat_activity.query_start AS duration,
query, state
FROM pg_stat_activity
WHERE (now() - pg_stat_activity.query_start) > interval '5 seconds'
ORDER BY duration DESC;
### 3. Verificar locks
SELECT blocked_locks.pid AS blocked_pid,
blocking_locks.pid AS blocking_pid,
blocked_activity.query AS blocked_query
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_locks blocking_locks
ON blocking_locks.locktype = blocked_locks.locktype
WHERE NOT blocked_locks.granted;
## Resolução
### Cenário A: Queries lentas (mais comum — ~70% dos casos)
1. Identifique a query lenta com o passo 2 acima
2. Se for query conhecida, aplique o fix documentado em [link]
3. Se for query desconhecida, mate a conexão:
SELECT pg_terminate_backend(<pid>);
4. Abra ticket para investigar root cause
### Cenário B: Connection leak no aplicativo
1. Verifique os pods: kubectl get pods -n production
2. Identifique qual pod tem mais conexões:
kubectl exec <pod> -- curl localhost:9090/metrics | grep db_pool
3. Restart do pod problemático:
kubectl rollout restart deployment/<service> -n production
### Cenário C: Spike de tráfego legítimo
1. Escale horizontalmente: kubectl scale deployment/<service> --replicas=5
2. Aumente pool temporariamente via config map
3. Monitore normalização
## Escalação
Se não resolver em 15 minutos → escalar para Tech Lead on-call
Se afetar > 50% dos usuários → declarar incidente P1
## Pós-incidente
- Preencher template de post-mortem: [link]
- Atualizar este runbook se procedimento mudou
Documentation as Code
Princípios
1. Documentação vive no MESMO repositório que o código
→ Versionada junto, revisada em PR, mesma fonte de verdade
2. Formatos de texto puro (Markdown, AsciiDoc, reStructuredText)
→ Diff-friendly, mergeável, pesquisável
3. CI/CD para documentação
→ Linting automático (markdownlint, vale)
→ Build automático (Docusaurus, MkDocs, Astro)
→ Deploy automático para site interno
4. Diagramas como código
→ Mermaid, PlantUML, D2 — diagramas em texto
→ Versionáveis e diff-friendly
Diagramas com C4 Model e Mermaid
Nível 1 — System Context: Quem usa o sistema e do que ele depende
Nível 2 — Container: Os "containers" dentro do sistema (APIs, DBs, queues)
Nível 3 — Component: Componentes dentro de cada container
Nível 4 — Code: Detalhes de implementação (geralmente desnecessário)
Exemplo Mermaid — Sequence Diagram:
sequenceDiagram
participant U as Usuário
participant API as API Gateway
participant OS as OrderService
participant PS as PaymentService
participant Q as RabbitMQ
participant ES as EmailService
U->>API: POST /orders
API->>OS: placeOrder(dto)
OS->>PS: processPayment(amount)
PS-->>OS: PaymentResult
OS->>Q: publish(OrderCreated)
OS-->>API: 202 Accepted
API-->>U: { orderId, status: "processing" }
Q->>ES: consume(OrderCreated)
ES-->>U: Email de confirmação
Princípios de Escrita Técnica
Clareza Acima de Tudo
RUIM: "O sistema utiliza uma abordagem baseada em eventos para
realizar a comunicação entre os diversos microsserviços
que compõem a infraestrutura da plataforma."
BOM: "Os microsserviços se comunicam via eventos assíncronos
publicados no RabbitMQ."
REGRAS:
1. Use voz ativa ("O serviço processa" em vez de "É processado pelo serviço")
2. Uma ideia por frase
3. Elimine palavras desnecessárias ("utiliza" → "usa", "realizar" → "fazer")
4. Seja específico ("banco de dados" → "PostgreSQL 16")
5. Defina siglas na primeira ocorrência
6. Use listas e tabelas para informação estruturada
7. Escreva para quem vai ler, não para impressionar
Audiência Define o Formato
Audiência | O que espera | Como escrever
-----------------------|-----------------------|---------------------
Novo dev no time | Contexto, quick start | Tutorial, onboarding
Dev do time | Como fazer X | How-to guide
Dev de outro time | O que esta API faz | API reference
On-call engineer | Como resolver agora | Runbook (passos claros)
Tech lead / architect | Por que esta decisão | ADR, RFC
VP of Engineering | Impacto e timeline | Executive summary
Referências
- Diátaxis framework: https://diataxis.fr
- "Docs for Developers" — Jared Bhatti et al.
- ADR Tools: https://github.com/npryce/adr-tools
- C4 Model: https://c4model.com
- Google Developer Documentation Style Guide
- "The Documentation System" — Daniele Procida (PyCon talk)
- Vale (linter de prosa): https://vale.sh