{
 "grupo": "backend",
 "descricao": "Back-end e servidores",
 "itens": [
  {
   "id": "backend-fila-de-tarefas-com-retry",
   "nome": "Fila de tarefas com retry",
   "receita": "enfileire só o id; worker com tentativas, backoff exponencial e fila de mortos; job idempotente",
   "prova": "falha forçada repete N vezes e cai na fila de mortos"
  },
  {
   "id": "backend-cache-de-resposta-com-invalidacao",
   "nome": "Cache de resposta com invalidação",
   "receita": "chave versionada, TTL e invalidação na escrita; evite avalanche com TTL aleatório",
   "prova": "segunda chamada responde do cache e a escrita apaga a chave"
  },
  {
   "id": "backend-rate-limit-por-usuario",
   "nome": "Rate limit por usuário",
   "receita": "janela deslizante ou token bucket no Redis; responda 429 com Retry-After",
   "prova": "a requisição N+1 recebe 429 e o cabeçalho de espera"
  },
  {
   "id": "backend-autenticacao-por-sessao-em-cookie",
   "nome": "Autenticação por sessão em cookie",
   "receita": "id aleatório no cookie HttpOnly Secure SameSite; dado da sessão no servidor; rotacione o id ao logar",
   "prova": "o cookie não aparece em document.cookie e muda após login"
  },
  {
   "id": "backend-refresh-token-rotativo",
   "nome": "Refresh token rotativo",
   "receita": "cada refresh emite novo par e invalida o anterior; reuso do antigo revoga a cadeia",
   "prova": "usar o refresh velho duas vezes derruba a sessão"
  },
  {
   "id": "backend-login-com-oauth-e-pkce",
   "nome": "Login com OAuth e PKCE",
   "receita": "gere code_verifier, envie code_challenge, valide state e a redirect URI exata, troque o code no servidor",
   "prova": "o fluxo completo dá token e state adulterado é recusado"
  },
  {
   "id": "backend-upload-seguro-de-arquivo",
   "nome": "Upload seguro de arquivo",
   "receita": "valide tipo pelo conteúdo, limite o tamanho, renomeie com id, guarde fora da raiz pública e sirva com Content-Disposition",
   "prova": "arquivo .php renomeado para .png é recusado"
  },
  {
   "id": "backend-upload-direto-ao-bucket",
   "nome": "Upload direto ao bucket",
   "receita": "servidor emite URL pré-assinada curta e restrita ao caminho e ao tipo; cliente envia direto",
   "prova": "o envio sem a assinatura falha com 403"
  },
  {
   "id": "backend-webhook-recebido-com-seguranca",
   "nome": "Webhook recebido com segurança",
   "receita": "valide a assinatura HMAC sobre o corpo cru, compare em tempo constante, rejeite timestamp antigo, responda 2xx logo",
   "prova": "corpo adulterado dá 401 e o reenvio não duplica efeito"
  },
  {
   "id": "backend-webhook-enviado-com-confianca",
   "nome": "Webhook enviado com confiança",
   "receita": "registre cada entrega, reenvie com backoff, assine o corpo e exponha o histórico",
   "prova": "endpoint fora do ar recebe novas tentativas e depois marca falha"
  },
  {
   "id": "backend-idempotencia-de-post",
   "nome": "Idempotência de POST",
   "receita": "exija Idempotency-Key, grave a chave com o resultado e devolva o mesmo resultado na repetição",
   "prova": "duas chamadas iguais criam um único registro"
  },
  {
   "id": "backend-paginacao-por-cursor",
   "nome": "Paginação por cursor",
   "receita": "ordene por coluna única estável, devolva cursor opaco e a ordem no índice; evite OFFSET grande",
   "prova": "inserir registros durante a paginação não repete nem pula itens"
  },
  {
   "id": "backend-filtro-e-ordenacao-seguros",
   "nome": "Filtro e ordenação seguros",
   "receita": "aceite só campos de uma lista permitida; nunca interpole nome de coluna vindo do cliente",
   "prova": "ordenar por campo fora da lista dá 400"
  },
  {
   "id": "backend-validacao-na-borda",
   "nome": "Validação na borda",
   "receita": "parse o corpo com schema (zod, pydantic, Joi) e devolva erros por campo com status 422",
   "prova": "corpo inválido lista cada campo errado"
  },
  {
   "id": "backend-erros-de-api-padronizados",
   "nome": "Erros de API padronizados",
   "receita": "formato único (code, message, details, requestId); 4xx para cliente, 5xx para servidor; sem stack trace",
   "prova": "toda rota de erro devolve o mesmo formato"
  },
  {
   "id": "backend-versionamento-de-api",
   "nome": "Versionamento de API",
   "receita": "versão no caminho (/v1) ou cabeçalho; nunca quebre campo existente; deprecie com data e aviso",
   "prova": "cliente antigo continua funcionando após a v2 sair"
  },
  {
   "id": "backend-cors-configurado-certo",
   "nome": "CORS configurado certo",
   "receita": "liste origens exatas, trate OPTIONS, só ative credentials com origem específica",
   "prova": "requisição de origem não listada falha e a listada passa"
  },
  {
   "id": "backend-idempotencia-de-job-agendado",
   "nome": "Idempotência de job agendado",
   "receita": "cron com lock distribuído (chave com TTL) para uma instância só executar",
   "prova": "duas instâncias ligadas executam o job uma única vez"
  },
  {
   "id": "backend-transacao-com-varias-escritas",
   "nome": "Transação com várias escritas",
   "receita": "abra transação, faça todas as escritas, confirme ou desfaça; mantenha curta e sem chamada de rede dentro",
   "prova": "erro no meio não deixa escrita parcial"
  },
  {
   "id": "backend-outbox-para-eventos-confiaveis",
   "nome": "Outbox para eventos confiáveis",
   "receita": "grave o evento na mesma transação do dado e um publicador envia depois e marca como enviado",
   "prova": "derrubar o publicador não perde evento nem duplica sem controle"
  },
  {
   "id": "backend-soft-delete-com-cuidado",
   "nome": "Soft delete com cuidado",
   "receita": "coluna deleted_at, filtro padrão em toda consulta e rotina de purga definitiva por retenção",
   "prova": "registro apagado some da lista e a purga remove de vez"
  },
  {
   "id": "backend-auditoria-de-alteracao",
   "nome": "Auditoria de alteração",
   "receita": "tabela de log com quem, quando, antes e depois, gravada na mesma transação",
   "prova": "editar um registro cria a linha de auditoria com o diff"
  },
  {
   "id": "backend-configuracao-por-ambiente",
   "nome": "Configuração por ambiente",
   "receita": "leia variáveis na partida, valide com schema e falhe cedo se faltar; sem default perigoso",
   "prova": "subir sem variável obrigatória aborta com mensagem clara"
  },
  {
   "id": "backend-health-check-e-readiness",
   "nome": "Health check e readiness",
   "receita": "/health responde vivo; /ready verifica banco e dependências; orquestrador usa os dois",
   "prova": "derrubar o banco faz /ready falhar sem matar o processo"
  },
  {
   "id": "backend-encerramento-gracioso",
   "nome": "Encerramento gracioso",
   "receita": "no SIGTERM pare de aceitar, termine as requisições em curso, feche conexões e saia com prazo",
   "prova": "deploy sem erros 502 durante a troca"
  },
  {
   "id": "backend-pool-de-conexoes-dimensionado",
   "nome": "Pool de conexões dimensionado",
   "receita": "tamanho do pool abaixo do limite do banco dividido pelas instâncias; timeout de aquisição",
   "prova": "carga alta enfileira sem estourar max_connections"
  },
  {
   "id": "backend-timeout-e-retry-em-chamada-externa",
   "nome": "Timeout e retry em chamada externa",
   "receita": "timeout curto, retry só em erro transitório com backoff e jitter, circuit breaker para falha longa",
   "prova": "serviço parceiro lento não trava a API"
  },
  {
   "id": "backend-streaming-de-resposta-grande",
   "nome": "Streaming de resposta grande",
   "receita": "envie em pedaços com stream e cabeçalhos corretos; nunca carregue tudo em memória",
   "prova": "exportar 1 GB mantém a memória estável"
  },
  {
   "id": "backend-geracao-de-pdf-no-servidor",
   "nome": "Geração de PDF no servidor",
   "receita": "renderize HTML com navegador headless em fila, limite de tempo e tamanho, e guarde o arquivo em storage",
   "prova": "o PDF abre, tem as páginas certas e a geração não bloqueia a API"
  },
  {
   "id": "backend-envio-de-e-mail-transacional",
   "nome": "Envio de e-mail transacional",
   "receita": "fila + provedor com SPF/DKIM, template versionado, link de uso único e registro do status",
   "prova": "e-mail de teste chega ao inbox com links funcionando"
  },
  {
   "id": "backend-busca-textual-simples",
   "nome": "Busca textual simples",
   "receita": "índice full-text do banco (tsvector, FULLTEXT) com ranqueamento antes de adotar um motor externo",
   "prova": "consulta por palavra parcial retorna resultados ordenados"
  },
  {
   "id": "backend-multi-tenant-com-isolamento",
   "nome": "Multi-tenant com isolamento",
   "receita": "tenant_id em toda tabela, filtro aplicado na camada de dados (ou RLS) e teste de vazamento",
   "prova": "usuário do tenant A nunca lê dado do tenant B"
  },
  {
   "id": "backend-permissoes-por-papel-e-recurso",
   "nome": "Permissões por papel e recurso",
   "receita": "matriz papel, ação, recurso; checagem central com política; dono do recurso conferido",
   "prova": "usuário sem permissão recebe 403 em cada rota da matriz"
  },
  {
   "id": "backend-feature-flags",
   "nome": "Feature flags",
   "receita": "flag lida de uma fonte única, com padrão seguro, rollout por percentual e remoção planejada",
   "prova": "ligar para 10% muda só esse grupo e desligar reverte na hora"
  },
  {
   "id": "backend-geracao-de-id-segura",
   "nome": "Geração de ID segura",
   "receita": "UUID v4 ou ULID/UUIDv7 para ordenação; nunca sequencial exposto quando enumeração é risco",
   "prova": "IDs não são adivinháveis por sequência"
  },
  {
   "id": "backend-tempo-e-relogio-testaveis",
   "nome": "Tempo e relógio testáveis",
   "receita": "injete o relógio como dependência; use UTC; nunca chame now() solto em regra de negócio",
   "prova": "o teste congela o tempo e valida expiração sem esperar"
  },
  {
   "id": "backend-exportacao-de-dados-em-csv",
   "nome": "Exportação de dados em CSV",
   "receita": "stream por linha, escape de aspas e quebra de linha, BOM para Excel e proteção contra injeção de fórmula (=,+,-,@)",
   "prova": "o arquivo abre no Excel com acentos certos e sem executar fórmula"
  },
  {
   "id": "backend-limitar-tamanho-de-corpo-e-profundidade",
   "nome": "Limitar tamanho de corpo e profundidade",
   "receita": "limite de bytes do corpo, profundidade de JSON e número de campos",
   "prova": "corpo gigante recebe 413 sem consumir memória"
  },
  {
   "id": "backend-logs-de-requisicao-com-correlacao",
   "nome": "Logs de requisição com correlação",
   "receita": "gere requestId na entrada, propague por cabeçalho e inclua em todo log e erro",
   "prova": "seguir um requestId no log reconstrói a requisição inteira"
  },
  {
   "id": "backend-documentar-a-api-com-openapi",
   "nome": "Documentar a API com OpenAPI",
   "receita": "gere o contrato do código ou o código do contrato, valide em CI e publique a página",
   "prova": "a resposta real casa com o schema (teste de contrato)"
  }
 ]
}
