Edição Definitiva · Estado da Arte

Engenharia de
Plataformas
de Software

Meta Dados · Engenharia de Plataformas · Obra Completa

Uma obra única: do Manifesto à engenharia de produto, um percurso contínuo sobre como construir plataformas de software que mereçam durar, e que, por isso, possam crescer sem quebrar.

Escrito por Djeferson Capelli Arquiteto de Soluções · Fundador da Mettric Tecnologia
Obra completa · 80 decisões de arquitetura · Edição 1.1 · Junho de 2026 · metadados.io
"Construa com menos. Desenhe com propósito. Forje plataformas que mereçam durar."
A obra

Sumário

Cada capítulo preserva sua própria capa e navegação interna. Use o botão de índice (canto inferior direito) para saltar entre os capítulos a qualquer momento.
Meta Dados · Engenharia de Plataformas

O Manifesto da
Engenharia Essencial

Um juramento de ofício

ler

A elegância suprema não chega quando não há mais nada a incluir — chega quando arrancar uma única engrenagem faria o todo desmoronar. A tese que abre este ofício

I

A arte de subtrair

Vivemos uma era de excesso, em que a força bruta disfarça a fragilidade do projeto e camadas de abstração escondem a ausência de entendimento. Mas a engenharia de verdade não é o ato de acrescentar — é a arte implacável de subtrair. Não construímos para medir quanto um sistema é capaz de acumular, e sim para descobrir quanto podemos remover até que reste apenas a sua essência inquebrável.

Cada linha que sobrevive a esse corte ganha um direito: o de existir. O que não se justifica, cai. A elegância suprema não chega quando não há mais nada a incluir; ela chega quando arrancar uma única engrenagem faria o todo desmoronar.

II

A rocha e o vento

Conhecemos a diferença entre a rocha e o vento. O que é estrutural, invariante e perene, forjamos em código puro — disciplinado, tipado, testado. O que respira, muda e se adapta ao tempo, libertamos como metadado.

Recusamo-nos a aprisionar a fluidez do comportamento humano em lógica rígida, assim como nos recusamos a deixar a configuração degenerar num caos sem governo. Entre o dado que descreve e o código que executa há uma fronteira — e essa fronteira é sagrada. Cruzá-la por conveniência é o primeiro passo da ruína.

Descrever o que existe é dado.
Realizar como funciona é código.

III

Contra as caixas-pretas

Rejeitamos o conforto ilusório das caixas-pretas. Quem constrói de verdade domina a própria obra de ponta a ponta — da interface que antecipa a necessidade de quem a usa ao elétron invisível que atravessa o silício. Não delegamos à magia aquilo que temos o dever de compreender.

Traçamos fronteiras nítidas e contratos estritos não para erguer muros entre as pessoas, mas para que os sistemas conversem e evoluam sem se ferir. E cada decisão que tomamos deixa rastro: registramos o porquê, para que quem vier depois herde o raciocínio — e não apenas o resultado.

IV

A arquitetura como empatia

A nossa arquitetura é a medida exata da nossa empatia. Não escrevemos software apenas para as máquinas frias que o executam, mas para o humano solitário que precisará operá-lo, compreendê-lo e curá-lo às três da manhã.

A honra de um sistema está na sua clareza diante do caos: onde o erro tem voz compreensível, onde a falha ensina sem caçar culpados, onde cada alerta ilumina o caminho da correção em vez de apenas anunciar a dor. Damos a cada um apenas o poder de que precisa, e tornamos visível tudo o que importa — porque cuidar de quem opera é parte de construir.

Projetamos para quem chega depois.
Inclusive — sobretudo — às três da manhã.

V · O Juramento

Este livro não é um catálogo de padrões, diagramas e dados. É um juramento de ofício.

Um compromisso inegociável com a clareza contra o ruído, com a essência contra o excesso, com a duração contra a pressa.

Construa com menos. Desenhe com propósito. Forje plataformas que mereçam durar.
Meta Dados · o ofício

Os casos deste livro são reais.
Os nomes, não. O que importa permanece: o padrão, a lição, a essência.

— fim do manifesto —
Meta Dados · Engenharia de Plataformas
Introdução

Engenharia
de Plataformas
de Software

Uma obra sobre como pensar, projetar e operar plataformas que duram. A quem se destina, como funciona, e o nível de maestria que você alcançará.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Introdução
Introdução · Como Ler Esta Obra
00

Introdução

Este não é um livro sobre uma linguagem ou um framework. É um livro sobre como pensar, projetar e operar plataformas de software que duram — e que se mantêm sãs enquanto crescem.

Para quem · engenheiros, arquitetos, tech leadsComo ler · linear ou por necessidadeTempo de leitura · ~15 min

A Quem Este Livro se Destina

Este livro foi escrito para quem constrói software de verdade e quer construí-lo bem. Não pressupõe um título nem um cargo — pressupõe curiosidade e a disposição de entender o porquê por trás de cada decisão, não apenas o como.

Ele serve a três tipos de leitor, em três momentos diferentes da jornada. Ao desenvolvedor que já programa mas sente que falta uma estrutura maior — que escreve código que funciona, mas não sabe ainda por que alguns sistemas envelhecem bem e outros viram um pântano. Ao arquiteto ou tech lead que precisa tomar decisões que outras pessoas vão herdar, e quer um repertório de padrões testados e um jeito de registrar o raciocínio. E ao fundador técnico que constrói uma plataforma sozinho ou em equipe pequena, e não tem o luxo de errar nas fundações.

O pré-requisito honesto

Você precisa saber programar — ter escrito código, entendido o que é uma função, uma classe, um banco de dados. O resto, este livro constrói com você, do conceito ao código, sempre partindo do problema real.

O que une esses leitores é uma ambição: não se contentar em fazer o software funcionar hoje, mas torná-lo sustentável — capaz de crescer, de ser entendido por quem chega depois, de sobreviver a mudanças de requisito, de parceiro, de escala. Essa é a diferença entre escrever um programa e construir uma plataforma.

O Que Você Será Capaz de Construir

Ao final desta obra, você terá os fundamentos para projetar plataformas no mesmo patamar conceitual dos grandes sistemas corporativos do mundo.

Os softwares que admiramos por sua robustez — os sistemas operacionais que rodam por décadas, as plataformas corporativas que sustentam empresas inteiras, os ERPs que orquestram operações globais — não são feitos de truques secretos. São feitos de princípios disciplinados, aplicados com consistência: separação clara de responsabilidades, fronteiras que se respeitam, dados modelados com rigor, operação observável, segurança construída desde o início.

Este livro destila esses princípios e os torna concretos. Você não vai sair daqui sabendo recriar um sistema operacional inteiro — mas vai sair com o modo de pensar que os torna possíveis, e com padrões aplicáveis imediatamente ao seu próprio software. A meta é elevar o teto do que você considera construível.

Mapa de competências A trajetória do fundamento à maestria
A TRAJETÓRIA DE COMPETÊNCIA 1 FUNDAMENTO · a tese e os princípios Metadado vs código · arquitetura por capacidades · ADRs · economia da plataforma Cap I 2 ESTRUTURA · o núcleo que sustenta tudo Regra de dependência · engines · composição · resiliência · eventos · contratos · dados Cap II–IV 3 SUPERFÍCIE · como o sistema é usado e integrado Design system · acessibilidade · integração · idempotência · workflows Cap V–VI 4 SUSTENTAÇÃO · proteger e operar em produção Segurança · privacidade · LGPD · observabilidade · SLOs · runbooks · incidentes Cap VII–VIII 5 MAESTRIA · fronteiras avançadas IA & agentes · cloud native · data engineering · SRE global · arquitetura empresarial · growth Cap IX–XV
Mapa de competências. A obra é uma escada: cada nível repousa sobre o anterior. Você pode subir degrau a degrau, ou saltar ao nível que precisa — mas a força vem da base.
Como medir seu progresso

A cada volume, o mapa acima ganha um degrau sólido. Não se trata de decorar — trata-se de, ao terminar um nível, conseguir explicar a um colega por que cada decisão foi tomada. Se você consegue ensinar, você dominou.

Como Esta Obra Funciona

A obra é organizada em capítulos, e cada capítulo em tópicos. Mas a verdadeira estrutura está dentro de cada tópico: uma anatomia fixa de sete seções que se repete do início ao fim. Essa repetição não é monotonia — é um contrato com o leitor. Você sempre sabe onde está e o que vem a seguir.

Tabela de decisão A anatomia de todo tópico
SeçãoO que ela entregaPor que existe
ProblemaA dor real, antes de qualquer soluçãoNenhum padrão faz sentido sem o problema que resolve
DecisãoA regra adotada, declarada com clarezaO princípio que guia tudo o que vem depois
ModeloO conceito visualizado em diagramaVer a ideia antes de codificá-la
ImplementaçãoCódigo real, comentado, em PHP ou PythonDo conceito à prática, sem saltos
VerificaçãoComo provar que funciona (testes)Uma decisão não comprovada é só uma esperança
AntipadrõesOs erros comuns e suas correçõesAprender pelo contraste com o que dá errado
ChecklistO resumo acionável do tópicoLevar o aprendizado para o seu próprio código

Além dessas sete seções, cada tópico traz elementos que tornam o aprendizado concreto e confortável: estudos de caso baseados em situações reais de operação, incidentes reais apresentados como linhas do tempo — porque se aprende muito com o que quebrou —, decisões arquiteturais (ADRs) completas que preservam o raciocínio, e exercícios que levam o conceito ao seu contexto.

Os casos são reais, os nomes não

Os estudos de caso e incidentes vêm de operações reais de plataformas em produção. Os nomes de sistemas, empresas e produtos foram tornados genéricos de propósito — o que importa é o padrão, a lição, a dinâmica do problema. Eles se aplicam ao seu sistema, qualquer que seja o domínio.

As Técnicas e a Stack

Este livro usa duas linguagens, deliberadamente, porque cada uma brilha em um papel — e porque saber quando usar qual é parte da maturidade de engenharia.

PHP sustenta o núcleo da plataforma: as engines, o domínio, os casos de uso. Não o PHP de scripts soltos, mas o PHP moderno, tipado, organizado segundo a Arquitetura Limpa — provando que a disciplina arquitetural independe da reputação de uma linguagem. Python assume os pipelines de dados, a automação e os coletores: o ETL que move milhões de registros, os scripts que sustentam a operação, a manipulação de dados em escala. Juntas, elas cobrem o espectro de uma plataforma real.

Arquitetura da stack As camadas técnicas que você dominará
A STACK QUE VOCÊ DOMINARÁ PHP o núcleo da plataforma engines · domínio · casos de uso Clean Architecture aplicada Python os pipelines e a automação ETL · coletores · scripts de operação dados em escala OS BANCOS DE DADOS · cada um na sua força PostgreSQL (OLTP) · ClickHouse / DuckDB (OLAP) · MySQL · Firebird · Oracle (legados) A FUNDAÇÃO · arquitetura limpa · metadados · observabilidade · segurança por construção princípios que independem de linguagem
A stack. Duas linguagens em seus papéis naturais, vários bancos cada um na sua força, sobre uma fundação de princípios que não dependem de nenhuma tecnologia específica.

Os bancos de dados são tratados com a seriedade que merecem, cada um por sua força: PostgreSQL como o cavalo de batalha transacional (OLTP); ClickHouse e DuckDB para análise em escala (OLAP); e MySQL, Firebird e Oracle como realidades de sistemas legados com os quais toda plataforma madura precisa conviver. Você aprenderá não só a usá-los, mas a decidir qual usar e por quê.

Uma palavra sobre conforto

Conceitos avançados de engenharia podem intimidar. Por isso, esta obra nunca apresenta um padrão sem antes mostrar a dor que ele cura, nunca usa um termo sem defini-lo, e nunca mostra código sem explicá-lo linha a linha. Se em algum ponto você se sentir perdido, é falha do texto — não sua. Volte ao "Problema" do tópico: tudo parte de lá.

Como Ler

Há dois caminhos legítimos por esta obra, e ambos funcionam.

O caminho linear, do Capítulo I ao XV, constrói a competência camada por camada — é o ideal para quem quer a formação completa, do fundamento à maestria. O caminho por necessidade, saltando direto ao volume que resolve um problema atual, funciona porque cada volume é razoavelmente autossuficiente e aponta explicitamente para os conceitos de que depende. Você verá, ao longo do texto, referências cruzadas como "(Cap II)" — elas são pontes, não pré-requisitos rígidos.

Antes de começar, situe-se

  1. Olhe o mapa de competências acima. Em que degrau você se sente hoje? Seja honesto — não há vergonha em começar pela base.
  2. Pense num sistema que você mantém ou quer construir. Qual dor dele mais o incomoda? Provavelmente há um volume dedicado a ela.
  3. Decida seu caminho: formação completa (linear) ou solução de um problema específico (por necessidade)? Ambos são válidos.
  4. Prepare-se para pensar pelo porquê. A cada padrão que encontrar, pergunte: que problema isto resolve? É assim que o aprendizado gruda.
Meta: entrar nesta obra sabendo onde você está, aonde quer chegar, e por qual caminho — confortável e no comando do seu aprendizado.
✓ Checklist do tópico
  • Entendi a quem o livro se destina — e se isso me inclui
  • Sei o que serei capaz de construir ao final
  • Conheço a anatomia de sete seções que se repete em cada tópico
  • Entendi a stack: PHP no núcleo, Python nos pipelines, vários bancos
  • Escolhi meu caminho de leitura: linear ou por necessidade
Meta Dados · Engenharia de Plataformas · IntroduçãoComeçar → Capítulo I · Fundamentos
Meta Dados · Engenharia de Plataformas
Capítulo I

Fundamentos,
Visão e
Estratégia

A tese que sustenta a plataforma: comportamento variável é metadado, invariante é código, e a operação vem antes da automação opaca.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo I
Capítulo I · Fundamentos, Visão e Estratégia
1.1

Visão de Produto e Tese

Uma plataforma não é um sistema grande. É um sistema cuja variação vive em dados, não em código — e que por isso se adapta sem reescrever.

Pré-requisitos · nenhum · é o ponto de partidaConecta com · 1.3 Capacidades · Cap III MetadadosTempo de leitura · ~12 min

O Problema

A maioria dos sistemas corporativos nasce rígida. A regra de negócio é cravada no código, e qualquer mudança — um novo tipo de cliente, uma alíquota diferente, um fluxo de aprovação — exige um desenvolvedor, um deploy, um ciclo. O negócio espera dias pelo que deveria levar minutos.

Esse engessamento não é falha de execução; é falha de arquitetura. Quando não se distingue o que varia do que é estável, tudo vira código — e código só muda com programador. A pergunta fundadora desta obra é: o que precisa ser código, e o que pode ser dado?

O sintoma do engessamento

Se adicionar um novo tipo de cliente exige editar uma classe e fazer deploy, a variação está no lugar errado. O negócio deveria conseguir isso sozinho, por configuração.

A Decisão

Comportamento variável é metadado. Comportamento invariante é código. A plataforma é a fronteira disciplinada entre os dois.

O que varia entre clientes, entre contextos, ao longo do tempo — campos de um formulário, regras de aprovação, fluxos, telas — é descrito como dado (metadado), editável sem deploy. O que é invariante — como persistir com segurança, como isolar tenants, como cifrar — é código, testado e estável. A plataforma é o mecanismo que lê o metadado e o executa sobre o código.

Diagrama conceitual Os quatro planos da plataforma
INTENÇÃO · o que o negócio quer (metadado declarativo) POLÍTICA · regras e permissões (configuração governada) EXECUÇÃO · o código que realiza (invariante, testado) EVIDÊNCIA · logs, métricas, auditoria (a prova do que ocorreu)
Figura 1.1-a. Intenção e política são dados (mudam sem deploy); execução é código; evidência é a prova. As setas mostram como a intenção desce até virar ação observável.

Um segundo princípio acompanha o primeiro: operação antes de automação opaca. Automatizar algo que não se consegue observar é construir uma caixa-preta que ninguém saberá consertar. Toda automação nasce acompanhada da evidência que a torna operável — o quarto plano da figura.

Tabela de decisão Código ou metadado?
Aspecto do sistemaVaria entre clientes?Onde moraQuem altera
Campos de um cadastroSimMetadadoAnalista, sem deploy
Como salvar no banco com segurançaNãoCódigoDesenvolvedor, com teste
Regra "frota > 50 exige aprovação"SimMetadado (BRE)Analista de negócio
Isolamento entre empresas (tenant)NãoCódigoPlataforma, invariante
Fluxo pós-emissão de documentoSimMetadado (Workflow)Analista, no Studio

Estudo de Caso

Caso aplicado

Um núcleo, muitos produtos

Contexto. A plataforma opera produtos distintos — logística (suíte de logística), dados (plataforma de dados empresariais), recrutamento (plataforma de recrutamento) — que à primeira vista nada compartilham. Construí-los como sistemas separados significaria reescrever autenticação, permissões, telas e auditoria em cada um.

Sem a tese
N sistemas reescritos
Com a tese
1 núcleo + metadados
Ganho
Reúso real entre verticais

Decisão. Tratar o que é comum (identidade, permissão, metamodelo, auditoria) como núcleo genérico, e o que é específico de cada vertical como metadado sobre esse núcleo.

Resultado. Um formulário novo, uma regra nova, uma tela nova nascem como dado — não como código. O núcleo não sabe se está servindo logística ou recrutamento, e é exatamente essa ignorância que o torna reutilizável.

Decisão Arquitetural

ADR-0001

Variação de negócio vive em metadado, não em código

Aceito
Contexto

Sistemas corporativos tradicionais cravam regras de negócio no código, exigindo deploy para qualquer mudança. A plataforma precisa servir múltiplos clientes e verticais sem multiplicar a base de código.

Decisão

O que varia entre clientes e ao longo do tempo é descrito como metadado, editável sem deploy. O que é invariante é código testado. Um motor lê o metadado e o executa sobre o código.

Alternativas consideradas

Tudo em código — flexível para o dev, mas inacessível ao negócio e caro de manter. Tudo configurável — leva à "linguagem de programação acidental" na config (combatida no Cap III). Sistemas separados por vertical — multiplica esforço e diverge.

Consequências
PositivasNegócio muda regras sem deploy. Um núcleo serve muitos produtos. Reúso mensurável.
NegativasExige disciplina rigorosa na fronteira código/metadado e um metamodelo bem projetado.
RiscosMetadado pode degenerar em linguagem acidental; mitigado pela disciplina do Capítulo III.

Antipadrões

Regra de negócio no código

O if ($cliente->tipo === 'X') que exige deploy para mudar. Correção: a regra vira dado no Business Rules Engine (Cap III).

Automação opaca

Automatizar um fluxo sem instrumentá-lo cria uma caixa-preta. Correção: toda automação nasce com sua evidência (logs, métricas) — operação antes de automação.

Exercício

Separe o que varia do que é estável

  1. Liste cinco comportamentos do seu sistema que mudaram no último ano. Para cada um, pergunte: a mudança exigiu deploy?
  2. Classifique cada um: era variação de negócio (deveria ser dado) ou invariante técnico (corretamente código)?
  3. Para os que eram variação de negócio mas viviam no código, esboce como seriam descritos como metadado.
  4. Identifique um caso onde algo foi automatizado sem observabilidade. Que evidência faltou quando deu problema?
Meta: enxergar, no seu próprio sistema, onde a fronteira código/metadado está no lugar errado — e o custo que isso impôs.
✓ Checklist do tópico
  • A variação de negócio é descrita como metadado, não cravada em código
  • O invariante técnico é código testado e estável
  • Existe um motor que lê o metadado e o executa
  • Nenhuma automação é construída sem a evidência que a torna operável
  • O núcleo não conhece os verticais específicos que serve
Meta Dados · Engenharia de Plataformas · Capítulo I · §1.1Próximo → 1.2 Princípios Kanso
Capítulo I · Fundamentos, Visão e Estratégia
1.2

Princípios Kanso

Kanso é a beleza da simplicidade pela remoção do desnecessário. Em engenharia, é a disciplina de decidir olhando o trabalho real — não a suposição.

Pré-requisitos · 1.1 TeseConecta com · Cap VIII ObservabilidadeTempo de leitura · ~11 min

O Problema

Decisões de engenharia tomadas longe do trabalho real são chutes bem-vestidos. "Acho que o gargalo é o banco", "deve ser a rede", "provavelmente é o cache" — suposições que enviam a equipe a caçar o problema errado enquanto o real persiste.

A complexidade acidental agrava tudo: camadas que ninguém pediu, abstrações que não pagam seu custo, configuração que ninguém entende. Cada peça desnecessária é um lugar a mais onde o problema pode se esconder e onde a próxima pessoa vai tropeçar.

O que é Kanso

Um princípio estético japonês: a beleza que vem de remover o supérfluo, não de adicionar ornamento. Em engenharia, é preferir a solução que tem menos partes móveis — desde que resolva o problema inteiro.

A Decisão

Decida olhando o trabalho real (Genba). Remova o desnecessário (Kanso). Reflita sem culpa (Hansei). Melhore continuamente (Kaizen).

Genba — "o local real" — é a recusa de decidir por suposição: vá aos logs, às métricas, ao comportamento observado. Kanso é a recusa da complexidade que não paga seu custo. Hansei é a reflexão honesta sobre o que falhou, focada na causa sistêmica e não em culpados. Kaizen é a melhoria contínua: cada lição vira uma mudança concreta no sistema.

Fluxograma operacional O laço de operação centrado em Genba
TELEMETRIAo que está ocorrendo DIAGNÓSTICOpor que ocorre AÇÃOcorreção real a ação muda a telemetria — o laço recomeça
Figura 1.2-a. Da telemetria à ação corretiva, e de volta. Genba garante que cada etapa se baseie no que é, não no que se supõe.
Tabela de decisão Suposição vs Genba
SituaçãoReação por suposiçãoReação Genba
Sistema lento"Deve ser o banco" → otimiza o bancoOlha as métricas: o p95 está em qual engine?
Erros intermitentes"Deve ser a rede" → culpa a infraSegue o correlação_id até a causa real
Cliente reclama"Não consigo reproduzir" → fecha o casoProcura a evidência do que ele viveu
Incidente resolvido"Foi mal, vou ter cuidado"Hansei: qual causa sistêmica? Kaizen: que mudança evita a recorrência?

Estudo de Caso

Caso aplicado

A otimização que mirava o alvo errado

Contexto. Uma operação intermitentemente lenta gerava a hipótese recorrente de que "o banco precisa de mais índices". Horas foram investidas em tuning de queries que já eram rápidas.

Por suposição
Tuning de banco (semanas)
Por Genba
Timeout na chamada externa
Lição
Olhar antes de otimizar

Intervenção (Genba). Em vez de supor, a equipe instrumentou e olhou: a latência não estava no banco, mas numa chamada externa sem timeout que travava threads. A suposição apontava para o lugar errado.

Resultado. A correção real — um timeout e um circuit breaker — resolveu em uma tarde o que semanas de tuning não tocaram. Genba poupou o esforço desperdiçado; Kanso removeu a complexidade que a suposição teria adicionado.

Incidente Real

Severidade · Baixa

A reescrita que ninguém pediu

  • Antes Um módulo simples ganhou três camadas de abstração "para o futuro" — um futuro que nunca chegou. Cada mudança passava a tocar três arquivos.
  • Sintoma Tarefas triviais ficaram lentas; novos integrantes levavam dias para entender o fluxo de uma operação banal.
  • Diagnóstico (Hansei) A complexidade não resolvia problema nenhum — era ornamento. Violava Kanso diretamente.
  • Correção As camadas supérfluas foram removidas. O módulo voltou a ser legível e rápido de alterar.
  • Kaizen Vira princípio de revisão: abstração só entra quando paga seu custo agora, não "para o futuro".

Decisão Arquitetural

ADR-0002

Decisões de engenharia seguem Genba e Kanso

Aceito
Contexto

Decisões tomadas por suposição enviam a equipe a caçar problemas errados. Complexidade acidental, adicionada "para o futuro", esconde defeitos e atrasa mudanças.

Decisão

Toda decisão de diagnóstico se baseia em evidência observada (Genba), não em suposição. Toda solução prefere o menor número de partes móveis que resolve o problema inteiro (Kanso). Incidentes geram Hansei e Kaizen.

Alternativas consideradas

Decidir por experiência/intuição — rápido, mas frequentemente errado. Adicionar abstração preventiva — viola Kanso e raramente acerta o futuro. Post-mortem com culpados — destrói a honestidade que o Hansei exige.

Consequências
PositivasDiagnóstico acerta o alvo. Sistema permanece simples e legível. Cultura de melhoria sem medo.
NegativasExige instrumentação (Cap VIII) e disciplina para resistir à complexidade "elegante".
RiscosGenba sem boa observabilidade é cego; por isso o Capítulo VIII é pré-condição.

Antipadrões

Decidir por suposição

"Deve ser o banco" sem olhar a métrica. Correção: Genba — a evidência decide, não o palpite.

Complexidade preventiva

Camadas "para o futuro" que nunca chega. Correção: Kanso — abstração só quando paga seu custo agora.

Exercício

Pratique Genba no seu próximo problema

  1. No próximo incidente, antes de propor causa, escreva a evidência que você observou — não o que supõe. Há evidência suficiente? Se não, instrumente.
  2. Pegue um módulo que parece complexo demais. Liste cada camada e pergunte: qual problema concreto ela resolve hoje?
  3. No próximo post-mortem, force-se a descrever a causa sistêmica sem nomear pessoas. A frase muda de tom?
  4. Transforme a lição em uma mudança concreta (teste, alerta, remoção de código) — não em "ter mais cuidado".
Meta: substituir suposição por evidência e ornamento por simplicidade, em um caso real do seu sistema.
✓ Checklist do tópico
  • Diagnósticos se baseiam em evidência observada, não em suposição (Genba)
  • Soluções preferem o mínimo de partes móveis que resolve o problema (Kanso)
  • Complexidade só entra quando paga seu custo no presente
  • Post-mortems focam causa sistêmica, sem culpados (Hansei)
  • Cada lição vira uma mudança concreta no sistema (Kaizen)
Meta Dados · Engenharia de Plataformas · Capítulo I · §1.2Próximo → 1.3 Arquitetura Empresarial
Capítulo I · Fundamentos, Visão e Estratégia
1.3

Arquitetura Empresarial

Pense em capacidades — o que o negócio sabe fazer — não em funcionalidades soltas. Capacidades são estáveis; telas e botões mudam toda semana.

Pré-requisitos · 1.1 Tese · 1.2 KansoConecta com · Cap II Core · Cap IV DadosTempo de leitura · ~12 min

O Problema

Sistemas organizados por funcionalidade — "a tela de cadastro", "o botão de exportar", "o relatório X" — herdam a volatilidade da interface. Cada redesenho de tela ameaça a arquitetura, porque a arquitetura foi amarrada ao que mais muda: a aparência.

Capacidades de negócio, ao contrário, são notavelmente estáveis. "Emitir um documento fiscal", "aprovar uma operação", "cobrar um cliente" são coisas que o negócio faz há décadas e fará por décadas. A interface por onde se faz muda toda semana; a capacidade em si, quase nunca.

O erro de organizar por tela

Quando a arquitetura espelha as telas, redesenhar a UI vira risco arquitetural. Correção: organize por capacidade — a tela é só uma forma de acessá-la.

A Decisão

Arquitete por capacidades de negócio, em camadas concêntricas. O núcleo é genérico; a infraestrutura é detalhe substituível.

A plataforma se organiza em quatro camadas. O negócio expressa capacidades. A aplicação as realiza em casos de uso. O núcleo fornece engines genéricas (permissão, metadado, auditoria) que servem a qualquer capacidade. A infraestrutura — banco, rede, provedores — é detalhe que se troca sem abalar o resto. Cada camada depende apenas da camada interna, nunca o contrário.

Arquitetura lógica Capacidades em camadas concêntricas
NEGÓCIO · capacidades (emitir documento, aprovar, cobrar) APLICAÇÃO · casos de uso que realizam as capacidades NÚCLEO · engines genéricas (permissão, metadado, auditoria) INFRAESTRUTURA · banco, rede, provedores (substituível)
Figura 1.3-a. A dependência aponta para dentro: o negócio repousa sobre o núcleo genérico, e a infraestrutura é a camada mais externa e substituível.
Tabela de decisão Capacidade ou funcionalidade?
ItemTipoEstabilidadeOnde arquitetar
"Emitir documento fiscal"CapacidadeDécadasCamada de negócio
"Botão de emitir no topo"FuncionalidadeSemanasDetalhe de UI (Cap V)
"Aprovar operação"CapacidadeAnosCamada de negócio
"Relatório em PDF azul"FuncionalidadeMesesApresentação

Estudo de Caso

Caso aplicado

212 milhões de linhas em 36 minutos

Contexto. A base da base pública de dados empresariais — empresas, estabelecimentos, sócios, Simples — soma centenas de milhões de registros. Importá-la de forma ingênua levaria horas ou dias e produziria um banco impossível de consultar de forma transversal.

Volume
212M+ linhas
Tempo
< 36 minutos
Causa
Modelo disciplinado

Decisão arquitetural. Um modelo de dados disciplinado, com contrato estrutural uniforme (detalhado no Cap IV), e um pipeline de ETL que respeita esse contrato. A uniformidade não é burocracia — é o que torna a escala possível.

Resultado. A importação completa rodou em menos de 36 minutos. Não foi força bruta: foi a disciplina arquitetural que permitiu paralelizar e indexar sem que cada tabela fosse um caso especial.

Incidente Real

Severidade · Média

A query que levava minutos

  • Antes Uma consulta central da plataforma de dados empresariais varria a tabela simples sem índice adequado, levando minutos e travando outras operações.
  • Diagnóstico A capacidade ("consultar situação do Simples") estava correta; faltava a estrutura de dados que a sustentasse em escala.
  • Correção Criação de um índice sobre simples.cnpj_basico. O tempo da consulta caiu drasticamente.
  • Resultado A mesma capacidade, agora viável em escala: a query passou a responder em uma fração do tempo — uma redução de 96%.
  • Kaizen Vira regra: toda capacidade de consulta em tabela grande nasce com seu índice projetado, não descoberto em produção.

Decisão Arquitetural

ADR-0003

Organizar por capacidade de negócio, em camadas

Aceito
Contexto

Sistemas organizados por funcionalidade herdam a volatilidade da UI: redesenhar telas vira risco arquitetural. Capacidades de negócio, ao contrário, são estáveis por décadas.

Decisão

A arquitetura se organiza por capacidades de negócio em quatro camadas concêntricas (negócio, aplicação, núcleo, infraestrutura). A dependência aponta para dentro; a infraestrutura é substituível.

Alternativas consideradas

Organizar por tela/módulo de UI — frágil a redesenhos. Arquitetura em camadas técnicas puras (controller/service/dao) — não expressa o negócio. Microserviços desde o início — complexidade prematura (ver §1.5).

Consequências
PositivasA arquitetura resiste a mudanças de UI. O núcleo genérico serve muitas capacidades. Escala viável (212M linhas).
NegativasExige identificar corretamente as capacidades e manter a disciplina de dependência (verificada no Cap II).
RiscosCapacidades mal identificadas geram acoplamento; mitigado por revisão e pelos ADRs.

Antipadrões

Arquitetura espelhando telas

Cada tela vira um módulo arquitetural. Redesenhar a UI quebra tudo. Correção: arquitetar por capacidade, não por tela.

Infraestrutura no centro

O domínio depende diretamente do banco específico. Correção: infra é a camada mais externa, substituível (Cap II, regra de dependência).

Exercício

Mapeie suas capacidades

  1. Liste o que o seu negócio sabe fazer, em verbos: emitir, aprovar, cobrar, conciliar. Essas são suas capacidades candidatas.
  2. Para cada uma, identifique por quantas telas/interfaces diferentes ela é acessada. A capacidade é a mesma, independente da tela?
  3. Olhe seu código: ele está organizado por capacidade ou por tela? Onde um redesenho de UI forçaria mudança arquitetural?
  4. Escolha uma capacidade e desenhe suas quatro camadas: o que é negócio, aplicação, núcleo e infraestrutura nela?
Meta: enxergar suas capacidades estáveis sob a volatilidade das telas, e organizar a arquitetura sobre elas.
✓ Checklist do tópico
  • A arquitetura é organizada por capacidades de negócio, não por telas
  • As capacidades são expressas em verbos estáveis do negócio
  • As quatro camadas (negócio, aplicação, núcleo, infra) são distintas
  • A dependência aponta para dentro; a infraestrutura é substituível
  • Capacidades de consulta em escala nascem com sua estrutura de dados projetada
Meta Dados · Engenharia de Plataformas · Capítulo I · §1.3Próximo → 1.4 Decisões e o Registro (ADR)
Capítulo I · Fundamentos, Visão e Estratégia
1.4

Decisões e o Registro (ADR)

A decisão importa menos que o porquê dela. Sem registro, a equipe refaz a mesma discussão a cada seis meses — e às vezes desfaz, por engano, o que já se aprendeu.

Pré-requisitos · 1.1–1.3Conecta com · toda a obra — ADRs em cada capítuloTempo de leitura · ~10 min

O Problema

Uma decisão arquitetural sem registro é conhecimento condenado a evaporar. Seis meses depois, ninguém lembra por que se escolheu PostgreSQL em vez de outro banco, por que a auditoria é imutável, por que aquele serviço não foi quebrado em microserviços. A discussão se repete — ou pior, a decisão é revertida por quem não conhecia suas razões.

O custo não é só o tempo da rediscussão. É o risco de desfazer aprendizado: alguém, com boa intenção, remove uma trava que parecia desnecessária — e que existia justamente para evitar um problema já vivido. Sem o registro do porquê, cada decisão é uma armadilha esperando a próxima pessoa.

Conhecimento que evapora

"Por que isto é assim?" sem resposta documentada leva a duas reações ruins: rediscutir do zero, ou mudar às cegas. Correção: o ADR preserva o raciocínio.

A Decisão

Toda decisão arquitetural relevante vira um ADR: contexto, decisão, alternativas consideradas e consequências. O porquê sobrevive a quem decidiu.

Um ADR é deliberadamente curto — cabe em uma página. Registra o contexto (que forças levaram à decisão), a decisão em si, as alternativas consideradas (e por que foram preteridas) e as consequências (positivas, negativas e riscos). Quando uma decisão é superada, não se apaga o ADR: cria-se um novo que o substitui, preservando a trilha de raciocínio.

Fluxograma operacional Ciclo de vida de um ADR
Propostocontexto + opções Aceitodecisão registrada Vigenteguia o sistema Substituídopor novo ADR o raciocínio é preservado mesmo quando a decisão é superada
Figura 1.4-a. Proposto → aceito → vigente → substituído. Mesmo superado, o ADR permanece: o raciocínio histórico é um ativo, não lixo.
Por que isto funciona

O ADR transforma decisão tácita em conhecimento explícito e versionado. A próxima pessoa não herda um mistério — herda um raciocínio que pode confirmar, questionar com base, ou substituir conscientemente.

Tabela de decisão Com ADR vs sem ADR
SituaçãoSem ADRCom ADR
"Por que PostgreSQL?"Ninguém lembraADR explica o contexto
Pessoa-chave saiRaciocínio perdidoDecisões documentadas
Reconsiderar decisãoDiscussão do zeroParte do registro anterior
Trava "estranha" no códigoRemovida às cegasADR revela seu propósito

Estudo de Caso

Caso aplicado

O ADR que evitou desfazer uma lição

Contexto. A plataforma adota auditoria imutável (append-only, sem update/delete) — uma decisão registrada em ADR. Tempos depois, surgiu a tentação de "limpar" registros antigos da trilha para economizar espaço.

Sem o ADR
Lição desfeita por engano
Com o ADR
Decisão mantida com base
Valor
O porquê preservado

O papel do ADR. O registro explicava por que a imutabilidade existia: sem ela, a trilha não prova nada, e a conformidade (Cap VII) desmorona. A "otimização" teria destruído a garantia central da auditoria.

Resultado. A decisão foi mantida conscientemente, com a economia buscada por outro caminho (retenção e arquivamento). O ADR transformou o que seria um erro silencioso numa decisão informada.

Decisão Arquitetural

ADR-0004

Toda decisão arquitetural relevante é registrada como ADR

Aceito
Contexto

Decisões sem registro evaporam: o raciocínio vive na cabeça de quem decidiu e se perde quando essa pessoa sai. Decisões são rediscutidas ou revertidas sem conhecimento de suas razões.

Decisão

Toda decisão arquitetural relevante vira um ADR curto (contexto, decisão, alternativas, consequências), versionado junto ao código. Decisões superadas não são apagadas; são substituídas por novos ADRs.

Alternativas consideradas

Documentação livre em wiki — dispersa e desatualizada. Decisão só no código/commit — não captura alternativas nem o porquê. Não documentar — o estado atual, que causa o problema.

Consequências
PositivasO porquê sobrevive à rotatividade. Decisões são reconsideradas com base. Lições não são desfeitas por engano.
NegativasExige a disciplina de escrever o ADR no momento da decisão, não depois.
RiscosADRs desatualizados enganam; mitigado por substituí-los explicitamente e versioná-los.

Antipadrões

Decisão sem registro

O raciocínio vive só na memória de quem decidiu. Correção: ADR no momento da decisão.

Apagar ADR superado

Remover o registro de uma decisão antiga perde a trilha de raciocínio. Correção: substituir por novo ADR, preservando o histórico.

Exercício

Escreva o ADR que está faltando

  1. Identifique uma decisão arquitetural do seu sistema cujo "porquê" ninguém documentou. Pergunte na equipe: todos concordam sobre a razão?
  2. Escreva o ADR retroativo: qual era o contexto, que alternativas existiam, quais as consequências aceitas?
  3. Procure uma "trava estranha" no código — algo que parece desnecessário. Investigue: qual problema ela previne? Documente antes que alguém a remova.
  4. Estabeleça a regra: a próxima decisão arquitetural da equipe nasce com seu ADR, no mesmo dia.
Meta: transformar pelo menos uma decisão tácita do seu sistema em conhecimento explícito e versionado.
✓ Checklist do tópico
  • Toda decisão arquitetural relevante tem um ADR
  • O ADR registra contexto, decisão, alternativas e consequências
  • ADRs são versionados junto ao código
  • Decisões superadas são substituídas por novos ADRs, não apagadas
  • O ADR é escrito no momento da decisão, não reconstruído depois
Meta Dados · Engenharia de Plataformas · Capítulo I · §1.4Próximo → 1.5 Modularidade e Portfólio
Capítulo I · Fundamentos, Visão e Estratégia
1.5

Modularidade e Portfólio

Comece como monólito modular com fronteiras de verdade. Extraia um serviço só quando a dor justificar — não porque microserviços estão na moda.

Pré-requisitos · 1.3 CapacidadesConecta com · Cap II Dependência · Cap VI IntegraçãoTempo de leitura · ~12 min

O Problema

A plataforma cresce em produtos e módulos. Sem disciplina de fronteira, eles se entrelaçam: o faturamento lê direto a tabela do cadastro, o rastreamento chama um método interno das rotas. Quando tudo conhece tudo, nada pode mudar sem quebrar outra coisa — o monólito modular degenera na "grande bola de lama".

Do outro extremo está a moda dos microserviços adotada cedo demais: quebrar um sistema jovem em vinte serviços distribuídos antes de entender seus limites reais. O resultado é complexidade operacional brutal — rede, latência, consistência distribuída — para resolver um problema de escala que ainda não existe. Ambos os erros nascem da mesma falta: ausência de disciplina sobre quando e como separar.

Os dois extremos perigosos

De um lado, a bola de lama (tudo acoplado). Do outro, microserviços prematuros (tudo distribuído sem motivo). O caminho: monólito modular com fronteiras de verdade, extraindo serviços só quando a dor justifica.

A Decisão

Monólito modular com fronteiras explícitas. Extração para serviço independente só quando uma razão concreta o exige.

A plataforma começa como um monólito modular: um único processo, mas com módulos de fronteiras nítidas. Cada módulo expõe um contrato e esconde sua implementação; ninguém lê a tabela do vizinho. Quando — e só quando — surge uma razão concreta (um módulo precisa escalar sozinho, tem um time dedicado, ou um ritmo de release próprio), ele é extraído para um serviço independente, conversando com o resto pelo mesmo contrato que já existia.

Arquitetura física Do monólito modular ao serviço independente
MONÓLITO MODULAR Cadastro Faturas Rotas Rastreio fronteiras claras, mesmo processo extrai sóquando dói SERVIÇO INDEPENDENTE Rastreio (próprio processo) Restante do monólitoconversa via contrato, não tabela
Figura 1.5-a. As fronteiras já existem dentro do monólito. A extração só promove uma fronteira lógica a física — sem reescrever o contrato.
Tabela de decisão Manter no monólito ou extrair?
Sinal observadoManter modularExtrair serviço
Mesmo ritmo de releaseSimNão
Precisa escalar sozinhoNãoSim
Time dedicado e autônomoNãoSim
"Microserviço está na moda"Ignore a modaRazão inválida
Acoplamento alto entre módulosConserte a fronteira primeiroDistribuir não resolve

Estudo de Caso

Caso aplicado

parceiro white-label: white-label sem reescrever o núcleo

Contexto. A parceria white-label da parceiro white-label precisou rodar sob o mesmo núcleo da plataforma (plataforma), com sua própria identidade e domínio, sem que isso virasse um fork do sistema.

Sem fronteiras
Fork do sistema
Com fronteiras
Configuração + provisionamento
Ganho
Parceiro novo, núcleo intacto

Decisão. Tratar o white-label como configuração e fronteira, não como cópia. A parceiro white-label recebeu seus subdomínios (CNAMEs apontando para o balanceador), credenciais próprias e um período de carência — tudo sobre o mesmo núcleo modular.

Resultado. Um parceiro inteiro foi onboarded sem reescrever o núcleo. As fronteiras modulares permitiram que "mais um cliente white-label" fosse uma questão de configuração e provisionamento, não de novo código.

Incidente Real

Severidade · Média

O módulo que lia a tabela do vizinho

  • Antes Um módulo, por atalho, lia diretamente a tabela de outro em vez de usar seu contrato. Funcionava — até a tabela mudar.
  • Sintoma Uma alteração de estrutura num módulo quebrou silenciosamente outro que dependia da forma interna da tabela alheia.
  • Diagnóstico A fronteira existia no papel, mas fora violada na prática. O acoplamento escondido só apareceu na quebra.
  • Correção O acesso direto foi substituído pelo contrato do módulo dono dos dados. A fronteira voltou a ser real.
  • Kaizen A regra de dependência passa a ser verificada automaticamente no CI (Cap II): violar fronteira não compila.

Decisão Arquitetural

ADR-0005

Monólito modular primeiro; extrair serviços sob demanda

Aceito
Contexto

O crescimento em módulos sem disciplina de fronteira leva à bola de lama; a adoção prematura de microserviços impõe complexidade distribuída sem resolver escala real.

Decisão

Começar como monólito modular com fronteiras explícitas (contratos, não acesso a tabelas alheias). Extrair um módulo para serviço independente apenas quando há razão concreta: escala, time ou ritmo de release próprios.

Alternativas consideradas

Microserviços desde o início — complexidade operacional sem ganho proporcional. Monólito sem fronteiras — vira bola de lama. Extração por moda — distribui problemas sem resolvê-los.

Consequências
PositivasSimplicidade operacional no início. Fronteiras prontas permitem extrair quando necessário, sem reescrever contratos.
NegativasExige disciplina para manter fronteiras reais dentro do mesmo processo (verificada no CI).
RiscosFronteiras violadas escondem acoplamento; mitigado pela regra de dependência automatizada (Cap II).

Antipadrões

Ler a tabela do módulo vizinho

Atalho que cria acoplamento escondido. Correção: usar o contrato do módulo dono dos dados.

Microserviços por moda

Distribuir um sistema jovem sem razão de escala. Correção: monólito modular; extrair só com motivo concreto.

Exercício

Audite suas fronteiras

  1. Liste os módulos do seu sistema. Para cada par, pergunte: um acessa dados internos do outro diretamente, ou só pelo contrato?
  2. Identifique a pior violação de fronteira — o acoplamento escondido mais perigoso. O que aconteceria se a estrutura interna mudasse?
  3. Se você tem microserviços, questione cada um: foi extraído por razão concreta (escala, time, release) ou por moda?
  4. Escolha uma fronteira frágil e desenhe o contrato que a tornaria explícita — o que um módulo precisa expor para o outro parar de espiar.
Meta: tornar reais as fronteiras que hoje existem só no diagrama, e questionar separações feitas sem motivo.
✓ Checklist do tópico
  • O sistema começa como monólito modular, não microserviços
  • Cada módulo expõe um contrato e esconde sua implementação
  • Nenhum módulo lê diretamente os dados internos de outro
  • Extração para serviço só ocorre por razão concreta (escala/time/release)
  • A regra de dependência é verificada automaticamente (Cap II)
Meta Dados · Engenharia de Plataformas · Capítulo I · §1.5Próximo → 1.6 Roadmap e Discovery
Capítulo I · Fundamentos, Visão e Estratégia
1.6

Roadmap e Discovery

Construir a coisa certa é mais difícil que construir certo a coisa. Validar uma hipótese custa horas; construir a feature errada custa semanas — e a confiança do usuário.

Pré-requisitos · 1.1 TeseConecta com · 1.7 EconomiaTempo de leitura · ~11 min

O Problema

Construir a coisa certa é mais difícil que construir certo a coisa. Times caem na armadilha de transformar pedidos diretamente em features, sem validar se resolvem o problema real — e descobrem tarde, com o código já escrito, que ninguém precisava daquilo. Em uma operação enxuta, esse desperdício é especialmente caro.

O pedido do usuário é uma pista, não uma especificação. Quando alguém pede "um botão de exportar", o problema real pode ser "preciso levar estes dados para o meu contador" — e a melhor solução talvez nem seja um botão. Tratar o pedido como ordem direta pula a etapa mais importante: entender o problema por trás dele.

Pedido não é especificação

"O cliente pediu X" não significa "construa X". Correção: descubra o problema por trás do pedido e valide a solução antes de construí-la.

A Decisão

Todo pedido vira uma hipótese sobre um problema do usuário — e a hipótese é validada pelo menor teste possível antes de virar código.

O discovery inverte a ordem usual. Em vez de pedido → feature, faz-se problema → hipótese → validação → construção. O problema é observado no usuário real (Genba, §1.2). A hipótese é uma aposta de solução. A validação é o menor experimento que confirma ou refuta a aposta — uma conversa, um protótipo, uma medição. Só o que sobrevive à validação vira código.

Fluxograma operacional O laço de discovery
Problemado usuário, observado Hipótesesolução que talvez resolva Validaçãomenor teste possível Confirma → constrói Refuta → descarta barato
Figura 1.6-a. A validação é a porta: hipótese confirmada vira construção; hipótese refutada é descartada antes de custar semanas de desenvolvimento.
Por que isto economiza

Validar uma hipótese custa horas. Construir a feature errada custa semanas — mais o custo de oportunidade do que não se construiu, mais a erosão da confiança quando o usuário vê algo que não resolve seu problema.

Tabela de decisão Pedido recebido → como tratar
Pedido do usuárioReação ingênuaReação por discovery
"Quero um botão de exportar"Constrói o botãoPor quê? → "Levar dados ao contador" → qual formato ele aceita?
"Preciso de mais relatórios"Adiciona relatóriosQue decisão você toma com eles? → talvez um só resolva
"O sistema está lento"Otimiza tudoOnde, especificamente? → mede antes de agir (Genba)
"Quero integração com X"Integra com XQue problema X resolve? → validar o valor antes do esforço

Estudo de Caso

Caso aplicado

plataforma de dados empresariais: do que se pediu ao que se precisava

Contexto. O plataforma de dados empresariais nasceu como ferramenta de consulta de CNPJ. O caminho óbvio seria empilhar mais campos de consulta. Mas o problema real dos usuários não era "consultar um CNPJ" — era encontrar e qualificar clientes potenciais.

Pedido aparente
Mais consulta de CNPJ
Problema real
Encontrar e qualificar leads
Pivô
Inteligência de prospecção B2B

Discovery. Uma análise estratégica questionou o posicionamento: o valor defensável não estava na consulta (commodity), mas na inteligência de prospecção B2B — o grafo societário (mapa de relações entre empresas) e a exportação de listas segmentadas de leads.

Resultado. A recomendação foi pivotar de "consulta de CNPJ" para "inteligência de prospecção", mirando o problema real. O pedido aparente (mais consulta) teria reforçado uma commodity; o discovery revelou onde estava o valor defensável.

Incidente Real

Severidade · Baixa

A feature construída que ninguém usou

  • Antes Um pedido recorrente virou feature diretamente, sem validação. Semanas de desenvolvimento foram investidas.
  • Sintoma Após o lançamento, a adoção foi quase nula. O pedido existia, mas a solução não atacava o problema de verdade.
  • Diagnóstico (Hansei) O problema por trás do pedido nunca foi investigado. Construiu-se a solução aparente, não a necessária.
  • Correção A feature foi repensada a partir do problema real, validada com usuários antes de qualquer novo código.
  • Kaizen Vira processo: nenhum pedido grande vira código sem uma hipótese validada pelo menor teste possível.

Decisão Arquitetural

ADR-0006

Pedidos viram hipóteses validadas antes de virar código

Aceito
Contexto

Transformar pedidos diretamente em features leva a construir o que ninguém precisa — desperdício caro em operação enxuta. O pedido é pista do problema, não especificação da solução.

Decisão

Todo pedido relevante é convertido numa hipótese sobre um problema do usuário e validado pelo menor experimento possível (conversa, protótipo, medição) antes de virar desenvolvimento.

Alternativas consideradas

Pedido → feature direto — o estado que causa desperdício. Construir tudo e medir depois — caro e lento. Confiar só na intuição do time — sem contato com o usuário real (anti-Genba).

Consequências
PositivasEsforço dirigido ao problema real. Desperdício reduzido. Decisões de roadmap baseadas em evidência.
NegativasExige disciplina para resistir a construir o "óbvio" e investir tempo em validação.
RiscosValidação mal feita gera falsa confiança; mitigada por testes pequenos e contato real com usuários.

Antipadrões

Pedido vira feature direto

Pular a validação do problema. Correção: pedido → hipótese → validação → construção.

Validar com opinião interna

"Achamos que o usuário quer" sem falar com o usuário. Correção: Genba — observar o problema no usuário real.

Exercício

Transforme um pedido em hipótese

  1. Pegue um pedido recente de usuário. Escreva o problema por trás dele — não a solução pedida, mas a dor que a motiva. Você sabe qual é, ou está supondo?
  2. Formule a hipótese: "Acreditamos que [solução] resolve [problema] para [usuário]". Ela é testável?
  3. Desenhe o menor teste possível que confirmaria ou refutaria a hipótese sem escrever código de produção.
  4. Revise seu backlog: quantos itens são soluções pedidas que nunca tiveram o problema validado?
Meta: substituir "construir o que pediram" por "validar o problema e a solução" em pelo menos um item real do seu roadmap.
✓ Checklist do tópico
  • Pedidos são tratados como pistas de problemas, não como especificações
  • Cada item relevante vira uma hipótese testável
  • A hipótese é validada pelo menor experimento possível antes do código
  • O problema é observado no usuário real (Genba)
  • Só o que sobrevive à validação entra em desenvolvimento
Meta Dados · Engenharia de Plataformas · Capítulo I · §1.6Próximo → 1.7 Economia da Plataforma
Capítulo I · Fundamentos, Visão e Estratégia
1.7

Economia da Plataforma

Engenharia que não enxerga custo otimiza no escuro. Um cartão recusado não é "problema do financeiro" — é um sinal operacional que ameaça a continuidade do sistema.

Pré-requisitos · 1.1–1.6Conecta com · Cap VIII SLOs · error budgetTempo de leitura · ~11 min

O Problema

Engenharia que não enxerga custo otimiza no escuro. Um cartão recusado na serviço financeiro ou na provedor de nuvem, um pagamento do serviço de pagamento em atraso, um risco no serviço de conectividade — esses não são "problemas do financeiro". São sinais operacionais que afetam diretamente a continuidade da plataforma. Um sistema que não torna seu próprio custo visível não é operável de forma sustentável.

A separação tradicional entre "engenharia" e "custo" é uma ficção perigosa. Um serviço que escala lindamente mas cujo custo cresce mais rápido que o valor que entrega não é um sucesso técnico — é uma bomba-relógio. A viabilidade econômica não é um relatório que o financeiro emite depois; é uma propriedade do sistema, tão real quanto a latência ou a disponibilidade.

O custo invisível é risco operacional

Um cartão recusado pode derrubar um servidor virtual, uma assinatura em atraso pode cortar um serviço crítico. Correção: tratar custo como sinal operacional de primeira classe, visível e monitorado.

A Decisão

Viabilidade econômica é qualidade de engenharia. O custo é um sinal operacional visível — e a razão valor/custo é uma métrica que a plataforma acompanha.

A plataforma trata seu próprio custo como parte da operação. A relação fundamental é a razão entre valor entregue e custo operacional: ela precisa crescer, ou ao menos manter-se saudável, conforme o sistema escala. Custos — infraestrutura (os ~40 servidores virtuais), licenças, pagamentos recorrentes — são tornados visíveis, e sinais de risco (um pagamento que vai falhar) são tratados com a mesma seriedade de um alerta de produção.

Diagrama conceitual A economia da plataforma: razão valor/custo
VALOR ENTREGUEo que o cliente recebe CUSTO OPERACIONALinfra, licenças, pagamentos VIABILIDADErazão que deve crescer Custo visívelé sinal operacional
Figura 1.7-a. Valor sobre custo define a viabilidade; e o custo, tornado visível, vira sinal operacional — não surpresa no fim do mês.
Tabela de decisão Sinal de custo → tratamento
Sinal observadoVisão tradicionalVisão da plataforma
Cartão recusado (provedor de nuvem)Problema do financeiroRisco operacional: servidores virtuais em jogo
Custo de infra crescendoVê-se na fatura, tardeMétrica acompanhada; razão valor/custo
Assinatura crítica em atrasoResolve quando cortaAlerta antecipado, como um SLO
Serviço caro e pouco usadoNinguém percebeVisível: custo por valor entregue

Estudo de Caso

Caso aplicado

Quando o custo vira sinal de continuidade

Contexto. A operação convive com sinais financeiros que são, na verdade, operacionais: recusas de cartão na serviço financeiro e na provedor de nuvem, um pagamento do serviço de pagamento em arraso, um risco no serviço de conectividade. Tratados como "assunto do financeiro", eles só apareceriam quando um serviço caísse.

Visão tradicional
Custo é do financeiro
Visão da plataforma
Custo é sinal operacional
Ganho
Continuidade vigiada

Decisão. Reconhecê-los como sinais operacionais de primeira classe. Um cartão que vai recusar na provedor de nuvem ameaça os servidores virtuais que sustentam a plataforma — é tão crítico quanto um disco enchendo.

Resultado. O custo deixa de ser surpresa de fatura e vira parte do quadro operacional. A continuidade do sistema passa a depender não só de uptime técnico, mas de viabilidade econômica vigiada — fechando o ciclo entre engenharia e sustentação.

Incidente Real

Severidade · Alta

O pagamento que ameaçou a infraestrutura

  • Sinal Uma recusa de cartão em serviço de infraestrutura (do tipo serviço financeiro / provedor de nuvem) sinaliza que recursos críticos podem ser suspensos.
  • Risco Sem tratamento, a recusa levaria à suspensão de servidores virtuais — uma queda operacional originada de um problema "financeiro".
  • Leitura tradicional "Isso é com o financeiro" — a engenharia só saberia quando o serviço já tivesse caído.
  • Correção O sinal de pagamento passa a ser tratado como alerta operacional, com antecedência suficiente para agir antes da suspensão.
  • Kaizen Riscos de pagamento de serviços críticos entram no quadro operacional, com a mesma seriedade de um alerta de produção.

Decisão Arquitetural

ADR-0007

Custo é sinal operacional; viabilidade é qualidade de engenharia

Aceito
Contexto

A separação entre engenharia e custo esconde riscos: sinais financeiros (cartão recusado, assinatura em atraso) são, na verdade, operacionais, e ameaçam a continuidade. Custo invisível leva a otimizar no escuro.

Decisão

A plataforma torna seu custo visível e trata a razão valor/custo como métrica acompanhada. Sinais de risco de pagamento de serviços críticos são tratados como alertas operacionais, com antecedência.

Alternativas consideradas

Custo só no relatório financeiro — chega tarde, desconectado da operação. Ignorar custo na engenharia — otimização no escuro e bombas-relógio. Monitorar só uptime técnico — ignora a viabilidade econômica.

Consequências
PositivasCusto deixa de ser surpresa. Riscos de continuidade são antecipados. Decisões técnicas consideram viabilidade.
NegativasExige integrar sinais financeiros ao quadro operacional e definir o que é "saudável" na razão valor/custo.
RiscosMétrica de custo mal definida engana; mitigada por acompanhamento contínuo e contexto operacional.

Antipadrões

"Custo é problema do financeiro"

Separa engenharia de viabilidade; o risco só aparece na queda. Correção: custo é sinal operacional de primeira classe.

Otimizar sem ver custo

Melhorar performance sem saber o custo que ela gera. Correção: tornar a razão valor/custo visível e acompanhada.

Exercício

Torne seu custo visível

  1. Liste os custos recorrentes que sustentam seu sistema (infra, licenças, assinaturas críticas). Quais, se falhassem o pagamento, derrubariam um serviço?
  2. Para cada um, pergunte: existe um alerta antecipado de risco de pagamento, ou você só saberia quando o serviço caísse?
  3. Esboce a razão valor/custo do seu produto: o valor entregue cresce mais rápido que o custo, conforme você escala?
  4. Identifique um serviço caro e pouco usado. O custo dele por valor entregue está visível para alguém que decide?
Meta: tornar visível pelo menos um custo crítico que hoje só apareceria como surpresa — e tratá-lo como o sinal operacional que é.
✓ Checklist do tópico
  • O custo operacional do sistema é visível, não surpresa de fatura
  • A razão valor/custo é acompanhada conforme a plataforma escala
  • Sinais de risco de pagamento de serviços críticos viram alertas operacionais
  • Decisões técnicas consideram a viabilidade econômica
  • Custo é tratado como qualidade de engenharia, não assunto à parte
Meta Dados · Engenharia de Plataformas · Capítulo I · §1.7Fim do Capítulo I · voltar ao início
Meta Dados · Engenharia de Plataformas
Capítulo II

Arquitetura
de Software e
Core Framework

Como vinte engines coexistem sem um kernel-deus: a regra de dependência, a anatomia de uma engine, composição por injeção, resiliência, eventos e versionamento de contratos.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo II
Capítulo II · Arquitetura de Software e Core Framework
2.1

A Regra de Dependência

Um sistema com vinte engines não precisa de um kernel-deus que tudo conhece. Precisa de uma única regra inviolável: a dependência sempre aponta para dentro.

Pré-requisitos · Cap I · arquitetura por capacidadesConecta com · 2.2 Engine · 2.3 ComposiçãoTempo de leitura · ~13 min

O Problema

Uma plataforma com muitas engines corre um risco arquitetural específico: o surgimento de um kernel-deus — um núcleo central que conhece todas as engines e do qual todas dependem. Quando ele existe, qualquer mudança nele ameaça o sistema inteiro, e nenhuma engine pode evoluir sozinha.

O acoplamento na direção errada é o vetor desse desastre. Quando o domínio — onde vivem as regras de negócio — depende diretamente do banco de dados, da biblioteca de e-mail, do framework web, ele fica refém de detalhes que deveriam ser substituíveis. Trocar o banco vira cirurgia de coração aberto; testar uma regra exige subir a infraestrutura inteira.

O sintoma do acoplamento invertido

Se você não consegue testar uma regra de negócio sem conectar ao banco, a dependência aponta para o lado errado. O domínio deveria ser testável em isolamento absoluto.

A Decisão

A dependência sempre aponta para dentro. O domínio não conhece ninguém; cada camada externa depende apenas da camada imediatamente interna.

A arquitetura se organiza em camadas concêntricas. No centro, o domínio — entidades e regras puras, sem nenhuma dependência externa. Em volta, a aplicação — casos de uso que orquestram o domínio. Na borda, a infraestrutura — banco, rede, provedores. A regra inviolável: a seta de dependência sempre aponta para dentro. O domínio declara o que precisa (uma interface, uma "porta"); a infraestrutura implementa, dependendo do domínio, nunca o contrário.

Arquitetura lógica A regra de dependência em camadas concêntricas
INFRAESTRUTURA · banco, rede, provedores APLICAÇÃO · casos de uso DOMÍNIO regras a dependência SEMPRE aponta para dentro
Figura 2.1-a. A inversão de dependência: a infraestrutura (externa) depende do domínio (interno), permitindo trocar banco, e-mail ou framework sem tocar nas regras.

Isto é a Inversão de Dependência aplicada com rigor. A consequência prática é profunda: o domínio torna-se testável sem nenhuma infraestrutura, e os detalhes externos tornam-se substituíveis sem abalar o núcleo. Não há kernel-deus porque não há centro do qual tudo dependa — há apenas a direção da seta.

A porta no domínio, o adaptador na borda

O domínio declara uma interface — o que ele precisa, em seus próprios termos. A infraestrutura fornece a implementação concreta.

RepositorioDeClientes.phpPHP
// DOMÍNIO: declara o que precisa. Não sabe que existe PostgreSQL.
namespace plataforma\Core\Cliente\Dominio;

interface RepositorioDeClientes
{
 public function porId(ClienteId $id): ?Cliente;
 public function salvar(Cliente $cliente): void;
}
A porta vive no domínio e fala a língua do negócio — nenhum vestígio de SQL ou de conexão.
RepositorioDeClientesPostgres.phpPHP
// INFRAESTRUTURA: implementa a porta. DEPENDE do domínio, não o contrário.
namespace plataforma\Infra\Persistencia;

use plataforma\Core\Cliente\Dominio\RepositorioDeClientes;

final class RepositorioDeClientesPostgres implements RepositorioDeClientes
{
 public function __construct(private readonly PDO $conexao) {}

 public function porId(ClienteId $id): ?Cliente
 {
 // O detalhe de persistência vive AQUI, na borda — substituível.
 $linha = $this->conexao->query('...')->fetch();
 return $linha ? Cliente::deLinha($linha) : null;
 }
 // ...
}
Trocar PostgreSQL por outro banco é escrever outra implementação desta porta. O domínio nem fica sabendo.

A Verificação

A regra não é confiada à boa vontade — é imposta pelo CI. O Deptrac mapeia as camadas e falha o build se uma dependência apontar para fora.

deptrac.yamlYAML
# Deptrac: a regra de dependência vira lei verificável no CI.
layers:
 - name: Dominio
 collectors: [{ type: directory, value: Core/.*/Dominio/.* }]
 - name: Infra
 collectors: [{ type: directory, value: Infra/.* }]
ruleset:
 Dominio: [] # o domínio NÃO pode depender de nada
 Infra: [Dominio] # a infra só pode depender do domínio
Se alguém fizer o domínio importar a infraestrutura, o build falha. A arquitetura se defende sozinha.
Tabela de decisão A dependência aponta para onde?
CenárioDireção erradaDireção certa
Regra de negócio usa o bancoDomínio → PDODomínio declara porta; infra implementa
Caso de uso envia e-mailApp → biblioteca SMTPApp usa interface; infra adapta
Testar uma regraExige subir o bancoDomínio testável isolado
Trocar de provedorCirurgia no núcleoNova implementação da porta

Estudo de Caso

Caso aplicado

Vinte engines, nenhum kernel-deus

Contexto. A plataforma tem muitas engines — permissão, metadado, auditoria, notificação, e assim por diante. A tentação natural seria um núcleo central que as conhecesse e coordenasse todas; mas esse núcleo viraria o gargalo e o ponto único de quebra.

Com kernel-deus
1 centro, tudo depende dele
Com a regra
N engines, setas para dentro
Ganho
Evolução independente

Decisão. Nenhuma engine depende de um centro. Cada uma declara suas portas e depende apenas de abstrações. A composição (§2.3) as conecta na borda, sem que nenhuma precise conhecer a implementação da outra.

Resultado. As engines coexistem e evoluem independentemente. Não há kernel-deus porque a regra de dependência elimina a necessidade de um centro — a coordenação acontece por contratos, não por um coordenador onisciente.

Incidente Real

Severidade · Média

A troca de detalhe que virou cirurgia

  • Antes Em um módulo legado, a regra de negócio chamava a biblioteca de persistência diretamente — a seta apontava para fora.
  • Sintoma Uma necessidade de trocar o mecanismo de acesso a dados exigiu alterar a própria lógica de negócio, com risco de regressão.
  • Diagnóstico O acoplamento invertido transformou um detalhe substituível numa dependência cravada no núcleo.
  • Correção Introdução de uma porta no domínio e de um adaptador na infraestrutura. A regra voltou a ser independente do mecanismo.
  • Kaizen O Deptrac passa a verificar a regra de dependência no CI: acoplamento invertido não compila mais.

Decisão Arquitetural

ADR-0008

A dependência sempre aponta para dentro (inversão de dependência)

Aceito
Contexto

Uma plataforma com muitas engines arrisca criar um kernel-deus ou acoplar o domínio a detalhes de infraestrutura, tornando regras impossíveis de testar isoladamente e detalhes impossíveis de trocar.

Decisão

A arquitetura se organiza em camadas concêntricas com a dependência apontando sempre para dentro. O domínio declara portas; a infraestrutura as implementa. A regra é verificada automaticamente no CI (Deptrac).

Alternativas consideradas

Núcleo central que tudo conhece — vira kernel-deus e gargalo. Domínio acoplado à infra — intestável e rígido. Confiar na disciplina manual — erode sem verificação automática.

Consequências
PositivasDomínio testável sem infraestrutura. Detalhes substituíveis. Engines evoluem sem um centro. Sem kernel-deus.
NegativasExige projetar portas e adaptadores e manter a verificação no CI.
RiscosAbstração excessiva pode complicar; mitigada pela disciplina Kanso (Cap I) — porta só quando há razão.

Antipadrões

Domínio dependendo da infraestrutura

A regra de negócio importa o PDO, a biblioteca SMTP, o framework. Correção: porta no domínio, adaptador na borda; a seta aponta para dentro.

Kernel-deus

Um núcleo central do qual toda engine depende. Correção: engines dependem de abstrações, não de um centro; coordenação por contrato.

Exercício

Encontre a seta invertida

  1. Escolha uma regra de negócio do seu sistema. Tente testá-la sem conectar ao banco. Conseguiu, ou a infraestrutura é inseparável?
  2. Procure no domínio qualquer import de banco, HTTP, e-mail ou framework. Cada um é uma seta apontando para o lado errado.
  3. Escolha o acoplamento mais perigoso e desenhe a porta que o corrigiria: o que o domínio precisa declarar para a infra implementar?
  4. Configure uma verificação automática (Deptrac ou equivalente) que falhe o build se o domínio depender da infraestrutura.
Meta: tornar pelo menos uma regra de negócio testável em isolamento, invertendo uma dependência que hoje aponta para fora.
✓ Checklist do tópico
  • A dependência sempre aponta para dentro (domínio no centro)
  • O domínio não importa banco, rede, e-mail ou framework
  • O domínio declara portas; a infraestrutura as implementa
  • O domínio é testável sem nenhuma infraestrutura
  • A regra de dependência é verificada automaticamente no CI
Meta Dados · Engenharia de Plataformas · Capítulo II · §2.1Próximo → 2.2 Anatomia de uma Engine
Capítulo II · Arquitetura de Software e Core Framework
2.2

Anatomia de uma Engine

Uma engine é a célula do núcleo: um contrato público por fora, regras próprias por dentro, e implementação totalmente escondida. Quem usa, não enxerga o interior.

Pré-requisitos · 2.1 Regra de dependênciaConecta com · 2.3 Composição · Cap III MetadadosTempo de leitura · ~12 min

O Problema

Se cada engine expusesse suas tripas — suas tabelas, suas classes internas, seus detalhes —, as outras engines acabariam dependendo desses detalhes. O resultado seria o acoplamento total: nada poderia mudar dentro de uma engine sem quebrar quem espiava seu interior.

A questão é de fronteira e segredo. Para que muitas engines coexistam, cada uma precisa ser uma caixa com uma interface clara por fora e liberdade total por dentro. Sem essa disciplina, a "modularidade" é só uma ilusão de pastas separadas que na prática se entrelaçam.

Vazamento de implementação

Quando uma engine acessa a tabela interna de outra, ou chama sua classe privada, a fronteira foi violada. Correção: só o contrato público é tocável; o resto é segredo da engine.

A Decisão

Cada engine expõe um contrato público e esconde tudo o mais. O domínio interno e a implementação são segredos; o exterior só conhece a interface.

Uma engine tem quatro partes. O contrato público — a interface que outras engines usam. O domínio interno — suas regras próprias, invisíveis de fora. As portas de saída — o que ela precisa de outras engines ou da infraestrutura, declarado como interfaces (seguindo §2.1). E a implementação privada — como o contrato é cumprido, totalmente escondida. Quem usa a engine toca apenas o contrato; o interior pode ser reescrito sem que ninguém perceba.

Diagrama conceitual A anatomia de uma engine
UMA ENGINE CONTRATO PÚBLICO · interface que outras engines usam DOMÍNIO internoregras próprias, escondidas PORTAS de saídao que a engine precisa de fora IMPLEMENTAÇÃO privada · invisível para quem usa a engine
Figura 2.2-a. O contrato público é a única superfície de contato. Domínio, portas e implementação ficam dentro da caixa — substituíveis sem afetar quem consome a engine.

O contrato como única superfície

A engine de permissão expõe uma pergunta — "este sujeito pode esta ação?" — e nada mais. Como ela decide é assunto interno.

PermissionEngine.phpPHP
// CONTRATO PÚBLICO: tudo o que outras engines conhecem da permissão.
namespace plataforma\Core\Permission\Publico;

interface PermissionEngine
{
 // A única pergunta que o exterior faz. O "como" é segredo da engine.
 public function permite(
 Sujeito $sujeito,
 string $acao,
 Recurso $recurso,
 ): Decisao;
}
O contrato é minúsculo e estável. RBAC, ABAC, cache — tudo isso é interno e pode mudar sem tocar quem pergunta.
PermissionEnginePadrao.phpPHP
// IMPLEMENTAÇÃO PRIVADA: como a engine cumpre o contrato. Invisível de fora.
namespace plataforma\Core\Permission\Interno;

final class PermissionEnginePadrao implements PermissionEngine
{
 public function permite(Sujeito $s, string $acao, Recurso $r): Decisao
 {
 // Combina papéis (RBAC) e atributos (ABAC) — detalhe interno.
 if ($this->papeis->concede($s, $acao, $r)) {
 return Decisao::permitido();
 }
 return $this->atributos->avaliar($s, $acao, $r);
 }
}
Esta classe pode ser totalmente reescrita — outra estratégia, outro cache — e nenhuma outra engine precisa saber.

A Verificação

O teste exercita a engine pelo seu contrato, exatamente como outra engine faria — provando que o contrato basta e que o interior é irrelevante para quem consome.

PermissionEngineTest.phpPHP
final class PermissionEngineTest extends TestCase
{
 public function test_consome_apenas_pelo_contrato(): void
 {
 // O teste só conhece a interface pública — como outra engine.
 PermissionEngine $engine = $this->engineComPapel('gestor');

 $decisao = $engine->permite(
 $this->sujeito('gestor'), 'cliente.editar', $this->recurso(),
 );

 $this->assertTrue($decisao->permitido);
 }
}
O teste nunca toca o interior. Se ele passa usando só o contrato, a fronteira da engine está sã.
Tabela de decisão O que é público, o que é segredo
Parte da engineVisível de fora?Pode mudar livremente?
Contrato público (interface)SimSó com versionamento (§2.6)
Domínio internoNãoSim, é segredo
Implementação privadaNãoSim, livremente
Portas de saídaNão (são internas)Sim

Estudo de Caso

Caso aplicado

Reescrever o interior sem ninguém perceber

Contexto. A engine de auditoria precisava mudar como armazenava os registros internamente, por razões de performance. Dezenas de pontos do sistema a usavam para registrar eventos.

Sem encapsulamento
Mudar interior toca todos
Com encapsulamento
Mudança contida na engine
Ganho
Alteração local, não sistêmica

O papel da anatomia. Como todos consumiam apenas o contrato público ("registrar evento"), a mudança ficou contida no interior da engine. Nenhum dos consumidores precisou ser tocado.

Resultado. A implementação foi reescrita, a performance melhorou, e o resto do sistema permaneceu inalterado. O encapsulamento transformou uma mudança potencialmente sistêmica numa alteração local.

Incidente Real

Severidade · Baixa

A engine que expôs sua tabela

  • Antes Uma engine, por conveniência, deixou outras lerem diretamente sua tabela interna em vez de passar pelo contrato.
  • Sintoma Uma mudança na estrutura interna dessa tabela quebrou silenciosamente as engines que a espiavam.
  • Diagnóstico A fronteira fora violada: o que deveria ser segredo interno virou dependência externa.
  • Correção Os acessos diretos foram substituídos pelo contrato público da engine. O interior voltou a ser privado.
  • Kaizen A regra de dependência no CI passa a barrar acesso ao interior de uma engine por outra.

Decisão Arquitetural

ADR-0009

Cada engine expõe só um contrato; o interior é segredo

Aceito
Contexto

Se engines expõem seus detalhes internos, outras passam a depender deles, gerando acoplamento total. Modularidade vira ilusão de pastas separadas que se entrelaçam.

Decisão

Cada engine tem um contrato público (interface), um domínio interno e uma implementação privada. O exterior só toca o contrato; domínio e implementação são segredos, substituíveis sem afetar consumidores.

Alternativas consideradas

Expor tabelas/classes internas — acoplamento total. Contrato gigante que vaza detalhes — engessa a engine. Sem fronteira formal — modularidade ilusória.

Consequências
PositivasInterior reescrevível sem afetar consumidores. Engines como caixas compreensíveis. Modularidade real.
NegativasExige projetar contratos enxutos e resistir à tentação de espiar o interior alheio.
RiscosContrato mal projetado limita a engine; mitigado por revisão e versionamento (§2.6).

Antipadrões

Espiar o interior de outra engine

Ler a tabela interna ou chamar a classe privada de outra engine. Correção: usar só o contrato público.

Contrato que vaza detalhes

A interface pública expõe tipos internos da implementação. Correção: contrato fala a língua do negócio, não da implementação.

Exercício

Desenhe a fronteira de uma engine

  1. Escolha uma engine (ou módulo) do seu sistema. Liste tudo o que outras partes acessam dela. Quanto disso é contrato público e quanto é interior espiado?
  2. Identifique o pior vazamento: onde alguém depende de um detalhe interno que deveria ser segredo.
  3. Desenhe o contrato público enxuto: qual é a menor interface que atende todos os consumidores legítimos?
  4. Verifique se o contrato fala a língua do negócio — ou se ele vaza nomes e tipos da implementação interna.
Meta: definir, para uma engine real, a fronteira entre o contrato público e o segredo interno — e fechar pelo menos um vazamento.
✓ Checklist do tópico
  • Cada engine tem um contrato público enxuto
  • O domínio interno e a implementação são segredos da engine
  • Nenhuma engine acessa o interior de outra
  • O contrato fala a língua do negócio, não da implementação
  • O interior pode ser reescrito sem afetar consumidores
Meta Dados · Engenharia de Plataformas · Capítulo II · §2.2Próximo → 2.3 Composição de Engines
Capítulo II · Arquitetura de Software e Core Framework
2.3

Composição de Engines

As engines não se conhecem nem se procuram. Um único lugar — a raiz de composição — monta o sistema inteiro, injetando em cada engine exatamente o que ela declarou precisar.

Pré-requisitos · 2.1 Dependência · 2.2 EngineConecta com · 2.4 Resiliência · Cap VI IntegraçãoTempo de leitura · ~12 min

O Problema

Se cada engine criasse suas próprias dependências — instanciando o banco, o cliente HTTP, as outras engines de que precisa — o acoplamento voltaria pela porta dos fundos. A engine de permissão "saberia" como construir a de auditoria, e trocar uma implementação exigiria caçar instanciações espalhadas por todo o código.

O localizador de serviços global (um service locator que qualquer engine consulta) parece resolver, mas só esconde o problema: a dependência passa a ser oculta — não se vê, lendo o construtor, do que a engine realmente precisa. O acoplamento vira invisível, e invisível é pior que explícito.

A dependência escondida

Quando uma engine busca o que precisa num localizador global, suas dependências reais ficam ocultas. Correção: a engine declara tudo no construtor; quem monta é a raiz.

A Decisão

As engines declaram suas dependências no construtor e nada criam por conta própria. Um único composition root monta o sistema inteiro.

A composição segue a injeção de dependência por construtor. Cada engine recebe, ao ser construída, as implementações concretas das portas que declarou (§2.2) — ela não as cria nem as procura. Existe um único lugar, o composition root, que conhece todas as implementações concretas e monta o grafo inteiro de objetos. É o "SDK do Core": o ponto onde as peças se encaixam. Em todo o resto do sistema, só há abstrações.

Arquitetura lógica A composição na raiz
COMPOSITION ROOTo único lugar que monta tudo Permission Enginerecebe suas portas Audit Enginerecebe suas portas Notify Enginerecebe suas portas as engines não se constroem nem se procuram — a raiz as injeta
Figura 2.3-a. O composition root é o único ponto que conhece implementações concretas. As engines recebem suas dependências e ignoram quem as forneceu.

A engine declara; a raiz fornece

A engine de auditoria precisa de um repositório e de um relógio. Ela os declara no construtor — e nunca os instancia.

AuditEngine.phpPHP
// A engine DECLARA o que precisa. Não cria, não busca.
final class AuditEnginePadrao implements AuditEngine
{
 public function __construct(
 private readonly RepositorioDeAuditoria $repo, // porta
 private readonly Relogio $relogio, // porta
 ) {}

 public function registrar(EventoDeAuditoria $e): void
 {
 $this->repo->acrescentar($e->em($this->relogio->agora()));
 }
}
Lendo o construtor, vê-se exatamente do que a engine depende. Nada é oculto, nada é criado internamente.
CoreContainer.phpPHP
// COMPOSITION ROOT: o ÚNICO lugar que conhece implementações concretas.
final class CoreContainer
{
 public function auditEngine(): AuditEngine
 {
 // A raiz injeta as implementações nas portas da engine.
 return new AuditEnginePadrao(
 $this->repositorioDeAuditoria(), // implementação concreta
 $this->relogioDoSistema(),
 );
 }

 public function permissionEngine(): PermissionEngine
 {
 // Trocar uma implementação = mudar UMA linha, aqui.
 return new PermissionEnginePadrao($this->repositorioDePapeis());
 }
}
Todo o cabeamento mora aqui. Trocar uma implementação é mexer numa linha da raiz — em nenhum outro lugar.

A Verificação

Como as dependências são injetadas, o teste fornece dublês triviais — provando que a engine funciona com qualquer implementação das suas portas.

AuditEngineTest.phpPHP
final class AuditEngineTest extends TestCase
{
 public function test_engine_funciona_com_dubles_injetados(): void
 {
 // A injeção torna o teste trivial: passo dublês pelas portas.
 $repo = new RepositorioDeAuditoriaEmMemoria();
 $engine = new AuditEnginePadrao($repo, new RelogioFixo());

 $engine->registrar(EventoDeAuditoria::de('cliente.criado'));

 $this->assertCount(1, $repo->registros());
 }
}
Sem injeção, este teste precisaria de banco e relógio reais. Com ela, é instantâneo e determinístico.
Tabela de decisão Como a engine obtém o que precisa
AbordagemDependência visível?Troca de implementação
Injeção por construtorSim, explícitaUma linha na raiz
Engine cria a própria dependênciaAcoplada à concretaCaçar instanciações
Service locator globalOcultaMudança invisível e arriscada
Variável/singleton globalOculta e compartilhadaEfeitos colaterais

Estudo de Caso

Caso aplicado

Trocar um provedor numa linha

Contexto. Um adaptador de integração com um parceiro externo (Cap VI) precisou ser substituído por outro provedor. Várias engines disparavam operações que, no fundo, usavam esse adaptador.

Sem injeção
Caçar usos pelo código
Com injeção
Uma linha na raiz
Ganho
Troca localizada e segura

O papel da composição. Como as engines recebiam o adaptador por injeção e o composition root era o único lugar que o construía, a troca foi uma alteração de uma linha na raiz: trocar a implementação concreta fornecida à porta.

Resultado. O provedor mudou sem que nenhuma engine fosse tocada. A composição centralizada transformou uma substituição potencialmente espalhada numa edição única e localizada.

Incidente Real

Severidade · Média

O singleton global que guardava estado

  • Antes Uma dependência era obtida de uma variável global compartilhada, que acabou acumulando estado entre operações.
  • Sintoma Comportamentos intermitentes e difíceis de reproduzir: uma operação interferia em outra através do estado global.
  • Diagnóstico A dependência oculta e compartilhada criava acoplamento invisível e efeitos colaterais entre engines.
  • Correção A dependência passou a ser injetada por construtor, com instância controlada pelo composition root.
  • Kaizen Vira regra: nada de estado global compartilhado; toda dependência é declarada e injetada.

Decisão Arquitetural

ADR-0010

Injeção por construtor; um único composition root

Aceito
Contexto

Se engines criam ou buscam suas próprias dependências, o acoplamento retorna de forma oculta e trocar implementações exige caçar instanciações espalhadas. Service locators e globais escondem dependências.

Decisão

Cada engine declara suas dependências no construtor e nada cria por conta própria. Um único composition root conhece as implementações concretas e monta o grafo inteiro. No resto do sistema, só abstrações.

Alternativas consideradas

Engine instancia o que usa — acoplamento à concreta. Service locator global — dependências ocultas. Singletons/globais — estado compartilhado e efeitos colaterais.

Consequências
PositivasDependências explícitas e visíveis. Troca de implementação numa linha. Testes triviais com dublês.
NegativasExige manter o composition root e resistir a atalhos de instanciação local.
RiscosComposition root pode crescer; mitigado por organizá-lo por engine e mantê-lo como único ponto de cabeamento.

Antipadrões

Service locator global

Engine busca dependências num registro global, escondendo do que depende. Correção: declarar no construtor; a raiz injeta.

Engine instancia a própria dependência

new RepositorioConcreto() dentro da engine. Correção: receber a porta por injeção; só a raiz conhece a concreta.

Exercício

Centralize o cabeamento

  1. Escolha uma engine ou serviço. Lendo só o construtor, dá para saber do que ela depende? Ou há dependências criadas/buscadas internamente?
  2. Procure usos de variáveis globais, singletons ou service locators. Cada um é uma dependência oculta a tornar explícita.
  3. Mova a criação das dependências para um único ponto de composição. Quantos lugares hoje instanciam implementações concretas?
  4. Escreva um teste que injete dublês pelas portas da engine. Ele ficou trivial — ou ainda exige infraestrutura real?
Meta: tornar as dependências de uma engine explícitas no construtor e mover seu cabeamento para um único composition root.
✓ Checklist do tópico
  • Cada engine declara suas dependências no construtor
  • Nenhuma engine cria ou busca as próprias dependências
  • Existe um único composition root que monta o sistema
  • Só o composition root conhece implementações concretas
  • Não há estado global compartilhado entre engines
Meta Dados · Engenharia de Plataformas · Capítulo II · §2.3Próximo → 2.4 Resiliência
Capítulo II · Arquitetura de Software e Core Framework
2.4

Resiliência

Toda chamada externa pode falhar, travar ou demorar. Um sistema resiliente não evita a falha — ele a contém, para que o problema de um parceiro não vire o problema de todos.

Pré-requisitos · 2.3 ComposiçãoConecta com · Cap VI Integração · Cap VIII OperaçõesTempo de leitura · ~14 min

O Problema

Toda chamada a um sistema externo é uma promessa que pode ser quebrada: o parceiro pode falhar, travar, ou responder devagar demais. Quando o código assume que a chamada sempre retorna rápido e com sucesso, uma única dependência lenta consegue travar threads, esgotar recursos e derrubar o sistema inteiro — um problema alheio virando um problema seu.

O caso é concreto na operação da plataforma: as conexões órfãs no Firebird, originadas de processos do sistema de rastreamento que não as fechavam, acumulavam até degradar tudo. Bater repetidamente numa integração que está saturada não ajuda — só agrava. A resiliência é a disciplina de conter a falha externa antes que ela se espalhe.

Uma dependência lenta derruba tudo

Sem timeout, uma chamada externa que trava prende a thread que a chamou — e, em volume, esgota o sistema. Correção: toda chamada externa tem timeout, retry e circuit breaker.

A Decisão

Toda chamada externa é protegida por três mecanismos: timeout, retry com recuo, e circuit breaker. A falha externa é contida, não propagada.

O timeout impede a espera infinita: passou do limite, a chamada falha de forma controlada. O retry tenta de novo o que é transitório, com recuo entre tentativas para não martelar o parceiro. O circuit breaker é o mais importante: após muitas falhas, ele "abre" e passa a falhar imediatamente, sem nem tentar a chamada — dando ao parceiro tempo de se recuperar e protegendo o próprio sistema de desperdiçar recursos numa porta que não abre.

Diagrama conceitual Os três estados do circuit breaker
FECHADOdeixa passar ABERTOfalha rápido MEIO-ABERTOtesta um pedido muitas falhas após pausa teste ok → fecha teste falha → reabre
Figura 2.4-a. Fechado (passa), aberto (falha rápido), meio-aberto (testa um pedido). O ciclo protege tanto o parceiro saturado quanto o sistema que o chama.

A chamada protegida pelos três mecanismos

ChamadaResiliente.phpPHP
// Toda chamada externa passa pelos três mecanismos.
final class ChamadaResiliente
{
 public function executar(string $servico, callable $chamada): mixed
 {
 // 1) Circuit breaker: se está aberto, falha JÁ — nem tenta.
 if ($this->breaker->estaAberto($servico)) {
 throw new CircuitoAberto($servico);
 }

 // 2) Retry com recuo para falhas transitórias.
 return $this->retry->tentar(fn() =>
 // 3) Timeout: não espera além do limite.
 $this->comTimeout($servico, $chamada)
 );
 }
}
Os três compõem-se: o breaker evita o desperdício, o retry absorve o transitório, o timeout limita a espera.
CircuitBreaker.phpPHP
// O breaker abre após muitas falhas e protege parceiro e sistema.
final class CircuitBreaker
{
 public function registrarResultado(string $servico, bool $sucesso): void
 {
 if ($sucesso) {
 $this->estado->fechar($servico); // volta ao normal
 return;
 }
 $falhas = $this->estado->incrementarFalhas($servico);
 if ($falhas >= $this->limite) {
 // Para de tentar: dá ao parceiro tempo de recuperar.
 $this->estado->abrir($servico, $this->pausaSegundos);
 }
 }
}
Exatamente o que faltava no episódio do Firebird: parar de tentar quando a porta está saturada.

A Verificação

CircuitBreakerTest.phpPHP
final class CircuitBreakerTest extends TestCase
{
 public function test_abre_apos_o_limite_de_falhas(): void
 {
 $breaker = $this->breaker(limite: 3);
 foreach ([false, false, false] as $r) {
 $breaker->registrarResultado('firebird', $r);
 }
 // Após 3 falhas, o circuito abre: não tenta mais.
 $this->assertTrue($breaker->estaAberto('firebird'));
 }

 public function test_sucesso_fecha_o_circuito(): void
 {
 $breaker = $this->breaker(limite: 3);
 $breaker->registrarResultado('firebird', true);
 $this->assertFalse($breaker->estaAberto('firebird'));
 }
}
O comportamento do breaker é testável de forma determinística, sem depender do parceiro real.
Tabela de decisão Qual mecanismo para qual falha
Tipo de falha externaMecanismoO que ele faz
Parceiro responde devagarTimeoutFalha controlada ao passar o limite
Falha transitória (rede oscilou)Retry com recuoTenta de novo, espaçando
Parceiro saturado (Firebird órfão)Circuit breakerPara de tentar; protege os dois lados
Parceiro fora por tempo prolongadoBreaker + fallbackFalha rápido; usa alternativa (Cap VI)

Estudo de Caso

Caso aplicado

O circuit breaker que conteve o Firebird

Contexto. Processos do sistema de rastreamento abriam conexões ao Firebird e não as fechavam. As conexões órfãs se acumulavam, saturando o banco e degradando operações que dependiam dele.

Sem breaker
Saturação se espalha
Com breaker
Falha contida localmente
Ganho
Parceiro recupera; plataforma vive

Decisão. Além de matar as conexões órfãs (contenção operacional, Cap VIII), introduzir um circuit breaker na integração: ao detectar saturação repetida, parar de enviar novas operações até o banco se recuperar.

Resultado. O breaker impediu que a saturação de um parceiro virasse a queda da plataforma. Em vez de martelar um banco já no limite, o sistema passou a falhar rápido e localmente, preservando o resto da operação.

Incidente Real

Severidade · Alta

A chamada sem timeout que travou threads

  • T+0 Uma integração externa começou a responder muito devagar — sem travar de vez, apenas demorando além do aceitável.
  • T+10min Cada chamada prendia uma thread à espera. Em volume, as threads se esgotaram e operações não relacionadas começaram a falhar.
  • Diagnóstico A ausência de timeout transformou a lentidão de um parceiro num esgotamento de recursos do sistema inteiro.
  • Correção Timeout em toda chamada externa, somado a circuit breaker para parar de tentar quando o parceiro está degradado.
  • Kaizen Vira regra de arquitetura: nenhuma chamada externa sem os três mecanismos de resiliência.

Decisão Arquitetural

ADR-0011

Toda chamada externa tem timeout, retry e circuit breaker

Aceito
Contexto

Chamadas externas podem falhar, travar ou demorar. Sem proteção, uma dependência lenta esgota recursos e derruba o sistema (caso Firebird/sistema de rastreamento; chamadas sem timeout travando threads).

Decisão

Toda chamada a sistema externo é protegida por timeout (limita a espera), retry com recuo (absorve o transitório) e circuit breaker (para de tentar quando o parceiro está saturado). A falha é contida, não propagada.

Alternativas consideradas

Confiar que a chamada sempre retorna — uma lentidão derruba tudo. Só retry, sem breaker — martela o parceiro saturado. Só timeout — não evita o desperdício de tentar uma porta fechada.

Consequências
PositivasFalha externa contida localmente. Parceiro saturado ganha tempo de recuperar. Recursos do sistema preservados.
NegativasExige configurar limites por serviço e compor os três mecanismos em toda integração.
RiscosLimites mal calibrados degradam a experiência; mitigados por observabilidade (Cap VIII) e ajuste.

Antipadrões

Chamada externa sem timeout

Esperar indefinidamente por um parceiro lento trava threads e esgota o sistema. Correção: timeout em toda chamada externa.

Retry sem circuit breaker

Tentar repetidamente um parceiro saturado só o afunda mais (caso Firebird). Correção: breaker que para de tentar e dá tempo de recuperação.

Exercício

Proteja uma chamada externa

  1. Liste as chamadas a sistemas externos no seu código. Quantas têm timeout explícito? As sem timeout são bombas-relógio.
  2. Para cada chamada crítica, pergunte: o que acontece se o parceiro ficar lento por dez minutos? As threads sobrevivem?
  3. Identifique uma integração que se beneficiaria de um circuit breaker — alguma que, ao saturar, hoje afeta o resto do sistema.
  4. Esboce os três mecanismos para essa chamada: qual o timeout, quantos retries com que recuo, e quantas falhas abrem o circuito?
Meta: tornar pelo menos uma chamada externa resiliente, contendo a falha do parceiro antes que ela se espalhe pelo sistema.
✓ Checklist do tópico
  • Toda chamada externa tem timeout explícito
  • Falhas transitórias usam retry com recuo entre tentativas
  • Integrações têm circuit breaker que abre após muitas falhas
  • A falha de um parceiro é contida, não propagada ao sistema
  • Os limites de resiliência são observáveis e ajustáveis (Cap VIII)
Meta Dados · Engenharia de Plataformas · Capítulo II · §2.4Próximo → 2.5 Eventos de Domínio
Capítulo II · Arquitetura de Software e Core Framework
2.5

Eventos de Domínio

Quando algo importante acontece, a engine que o causou não deveria saber quem se importa. Ela anuncia um fato; quem quiser, reage. É o desacoplamento que permite o ecossistema crescer.

Pré-requisitos · 2.2 Engine · 2.3 ComposiçãoConecta com · Cap IV Event Store · Cap VI WorkflowTempo de leitura · ~13 min

O Problema

Quando uma engine, ao concluir uma ação, precisa avisar todas as outras interessadas chamando-as diretamente, ela passa a conhecer cada uma delas. Criar um cliente vira "criar o cliente, depois notificar, depois auditar, depois atualizar o parceiro" — e a cada nova reação, é preciso reabrir e editar a engine que cria o cliente.

Esse acoplamento sufoca o crescimento. A engine que causa o fato acumula responsabilidades que não são dela e fica dependente de todas as outras. Pior: se o aviso a um interessado é feito numa transação separada da ação principal, abre-se a porta para inconsistência — o cliente é criado mas a notificação se perde, ou a notificação dispara para um cliente que, por um erro, não chegou a ser salvo.

Quem causa o fato não deveria conhecer quem reage

Se criar um cliente exige editar a engine para "também notificar", o acoplamento está cravado. Correção: a engine anuncia o fato; os interessados se inscrevem.

A Decisão

A engine que causa um fato publica um evento de domínio e nada mais. Quem se importa reage por inscrição — e o evento é gravado na mesma transação do fato (outbox).

Quando algo relevante acontece, a engine publica um evento de domínio — um fato no passado, como "ClienteCriado" — e segue em frente, sem saber quem reagirá. Outras engines se inscrevem nos eventos que lhes interessam e reagem de forma independente. Para garantir consistência, usa-se o padrão outbox: o evento é gravado na mesma transação de banco que persiste o fato. Ou ambos são salvos, ou nenhum — eliminando eventos perdidos e fatos fantasma.

Arquitetura lógica Eventos de domínio com o padrão outbox
Engine A"ClienteCriado" OUTBOXgrava na MESMA transação Barramento → reatoresB e C reagem, sem A saber Notifica Audita o outbox garante: ou grava o fato E o evento, ou nenhum dos dois
Figura 2.5-a. A engine grava fato e evento na mesma transação (outbox); o barramento entrega o evento aos reatores inscritos, que agem sem que a origem os conheça.

Publicar o fato, gravando na mesma transação

CriarCliente.phpPHP
// A engine anuncia o fato. Não sabe — nem precisa saber — quem reage.
final class CriarCliente
{
 public function executar(DadosDoCliente $dados): void
 {
 $this->transacao->executar(function () use ($dados) {
 $cliente = Cliente::criar($dados);
 $this->repo->salvar($cliente);

 // OUTBOX: o evento grava na MESMA transação do fato.
 // Ou os dois persistem, ou nenhum.
 $this->outbox->publicar(new ClienteCriado($cliente->id()));
 });
 }
}
A engine de criação termina aqui. Notificar, auditar, integrar — tudo isso é reação de outros, não responsabilidade dela.
ReagirAClienteCriado.phpPHP
// Um reator se INSCREVE no evento. A origem não o conhece.
final class EnviarBoasVindas implements Reator
{
 public function reageA(): string
 {
 return ClienteCriado::class; // inscrição
 }

 public function manipular(ClienteCriado $evento): void
 {
 // Reage de forma independente. Adicionar este reator
 // NÃO exigiu tocar a engine que cria o cliente.
 $this->mensageria->enviarBoasVindas($evento->clienteId);
 }
}
Para adicionar uma nova reação, escreve-se um novo reator. A engine de origem permanece intocada — extensão sem reabertura.

A Verificação

CriarClienteTest.phpPHP
final class CriarClienteTest extends TestCase
{
 public function test_publica_evento_na_mesma_transacao(): void
 {
 $outbox = new OutboxEmMemoria();
 $uc = $this->casoDeUsoCom($outbox);

 $uc->executar(DadosDoCliente::deExemplo());

 // O fato foi salvo E o evento foi publicado — juntos.
 $this->assertTrue($outbox->publicou(ClienteCriado::class));
 }

 public function test_falha_ao_salvar_nao_publica_evento(): void
 {
 // Se a transação falha, NEM fato NEM evento persistem.
 $uc = $this->casoDeUsoQueFalhaAoSalvar();
 $this->expectException(FalhaAoSalvar::class);
 $uc->executar(DadosDoCliente::deExemplo());
 }
}
O outbox garante a atomicidade: o teste prova que fato e evento são inseparáveis — juntos no sucesso, ausentes na falha.
Tabela de decisão Chamar direto vs publicar evento
AspectoChamada diretaEvento de domínio
Quem causa conhece quem reage?Sim, acopladoNão, desacoplado
Adicionar nova reaçãoEditar a origemInscrever um reator
Consistência fato/avisoRisco de divergirOutbox garante atomicidade
Crescimento do ecossistemaOrigem inchaReatores independentes

Estudo de Caso

Caso aplicado

Adicionar reações sem reabrir a origem

Contexto. O fato "documento fiscal importado" (Cap VI) precisava, com o tempo, disparar cada vez mais reações: notificar, atualizar um parceiro, alimentar a IA, registrar auditoria. Se a engine de importação chamasse cada uma, ela cresceria sem limite.

Chamada direta
Origem cresce a cada reação
Eventos
Reatores aditivos
Ganho
Crescimento sem reabertura

Decisão. A importação publica um único evento de domínio. Cada nova necessidade vira um reator inscrito, independente dos demais — a engine de origem nunca é tocada para acomodar uma nova reação.

Resultado. O conjunto de reações cresceu de forma aditiva. Adicionar "alimentar a IA com o documento" foi escrever um reator novo, não modificar a importação. O ecossistema cresce sem que o núcleo se reabra.

Incidente Real

Severidade · Média

O evento perdido por transação separada

  • Antes Um fato era salvo numa transação, e o aviso aos interessados era enviado logo depois, fora dela.
  • Sintoma Em falhas entre as duas etapas, surgiam inconsistências: o fato existia, mas a reação nunca acontecia — ou o contrário.
  • Diagnóstico Separar a persistência do fato da publicação do evento abria uma janela de inconsistência.
  • Correção Adoção do padrão outbox: o evento passa a ser gravado na mesma transação do fato — atomicidade garantida.
  • Kaizen Vira regra: eventos de domínio sempre via outbox; nada de publicar fora da transação do fato.

Decisão Arquitetural

ADR-0012

Eventos de domínio com padrão outbox para desacoplar engines

Aceito
Contexto

Se a engine que causa um fato chama diretamente todos os interessados, ela os conhece e incha a cada nova reação. Avisos em transação separada do fato geram inconsistência (evento perdido ou fato fantasma).

Decisão

A engine publica um evento de domínio e segue em frente; interessados reagem por inscrição. O evento é gravado na mesma transação do fato (outbox), garantindo que ambos persistam juntos ou nenhum.

Alternativas consideradas

Chamada direta aos interessados — acoplamento e origem inchada. Publicar fora da transação — janela de inconsistência. Fila externa sem outbox — evento pode perder-se se a transação falhar depois.

Consequências
PositivasOrigem desacoplada dos reatores. Novas reações são aditivas. Consistência fato/evento garantida.
NegativasExige a infraestrutura de outbox e um processo que entrega os eventos gravados ao barramento.
RiscosEntrega assíncrona traz "eventualmente consistente"; aceitável e tratado com idempotência (Cap VI).

Antipadrões

Origem chamando todos os interessados

Criar cliente "e também" notificar, auditar, integrar — tudo na engine de criação. Correção: publicar evento; interessados se inscrevem.

Publicar evento fora da transação do fato

Salvar o fato e depois, separadamente, publicar o evento. Correção: outbox — fato e evento na mesma transação.

Exercício

Transforme uma chamada direta em evento

  1. Encontre no seu sistema uma ação que, ao concluir, chama várias outras ("salvar e depois notificar e depois..."). Quantas responsabilidades alheias ela carrega?
  2. Identifique o fato central que essa ação produz. Qual seria o evento de domínio — o fato no passado — que ela deveria anunciar?
  3. Liste as reações atuais. Cada uma poderia virar um reator independente, inscrito nesse evento?
  4. Verifique a consistência: o aviso aos interessados acontece na mesma transação do fato, ou há uma janela onde podem divergir?
Meta: desacoplar uma ação real dos seus efeitos, publicando um evento de domínio e transformando as reações em reatores independentes.
✓ Checklist do tópico
  • A engine que causa um fato publica um evento, sem conhecer os reatores
  • Interessados reagem por inscrição, de forma independente
  • Novas reações são adicionadas sem editar a engine de origem
  • O evento é gravado na mesma transação do fato (outbox)
  • Não há janela de inconsistência entre o fato e o evento
Meta Dados · Engenharia de Plataformas · Capítulo II · §2.5Próximo → 2.6 Versionamento de Contratos
Capítulo II · Arquitetura de Software e Core Framework
2.6

Versionamento de Contratos

Um contrato publicado é uma promessa a quem o usa. Alterá-lo sob os pés dos consumidores quebra o sistema deles. A evolução acontece somando versões, não rompendo a atual.

Pré-requisitos · 2.2 EngineConecta com · Cap VI Integração · Cap IV EventosTempo de leitura · ~12 min

O Problema

Um contrato publicado — a interface de uma engine, o formato de um evento, a estrutura de uma resposta — é uma promessa àqueles que o consomem. No momento em que outra engine, outro serviço ou um parceiro passa a depender dele, alterá-lo deixa de ser uma decisão local: vira uma mudança que pode quebrar o sistema de quem confiou na promessa.

A tentação é "só ajustar" o contrato: renomear um campo, mudar seu tipo, remover algo que parece não ser mais usado. Cada um desses ajustes é uma ruptura silenciosa — funciona na engine que mudou, mas quebra, talvez em produção e sem aviso, todo consumidor que esperava a forma anterior. Em um ecossistema com muitos consumidores e parceiros, essa quebra se multiplica.

Alterar o contrato sob os pés de quem o usa

Renomear ou remover um campo de um contrato publicado quebra os consumidores que dependiam dele — frequentemente em produção, sem aviso. Correção: mudança que quebra exige nova versão.

A Decisão

Mudanças aditivas evoluem o contrato no lugar; mudanças que quebram exigem uma nova versão. As versões coexistem durante a migração — ninguém é forçado a migrar de imediato.

A evolução de contratos segue uma regra clara. Uma mudança aditiva — acrescentar um campo opcional, uma operação nova — não quebra ninguém e pode ser feita no próprio contrato. Uma mudança que quebra — remover ou renomear um campo, alterar um tipo, mudar o significado de algo — exige uma nova versão do contrato. Durante a transição, a versão antiga e a nova coexistem: os consumidores migram no seu ritmo, e só quando todos saíram da versão antiga ela pode ser aposentada.

Diagrama conceitual Versionamento de contratos sem ruptura
Contrato v1consumidores antigos Contrato v2consumidores novos A ENGINE atende AMBOSv1 e v2 coexistem durante a migração mudança que quebra exige v2 — nunca alterar o v1 sob os pés de quem o usa
Figura 2.6-a. A engine atende v1 e v2 simultaneamente durante a migração. A versão antiga só é removida quando nenhum consumidor depende mais dela.

Distinguir o aditivo do que quebra

EventoVersionado.phpPHP
// Evento versionado: a v2 NÃO altera a v1 — coexiste com ela.
final class ClienteCriadoV1
{
 public function __construct(
 public readonly string $clienteId,
 ) {}
}

final class ClienteCriadoV2
{
 public function __construct(
 public readonly string $clienteId,
 public readonly string $tenantId, // campo novo
 ) {}
}
Como a v2 é uma classe própria, os consumidores da v1 continuam funcionando exatamente como antes.
PublicarComCompatibilidade.phpPHP
// Durante a migração, a engine publica AMBAS as versões.
final class PublicarClienteCriado
{
 public function publicar(Cliente $c): void
 {
 // Consumidores da v1 e da v2 são atendidos ao mesmo tempo.
 $this->outbox->publicar(new ClienteCriadoV1($c->id()));
 $this->outbox->publicar(new ClienteCriadoV2($c->id(), $c->tenantId()));
 // A v1 só é removida quando NENHUM consumidor depende mais dela.
 }
}
A coexistência dá tempo: cada consumidor migra da v1 para a v2 no seu ritmo, sem quebra forçada.

A Verificação

CompatibilidadeTest.phpPHP
final class CompatibilidadeDeContratoTest extends TestCase
{
 public function test_consumidor_v1_continua_funcionando(): void
 {
 // Um consumidor que só conhece a v1 não pode ser afetado pela v2.
 $consumidorV1 = new ReatorQueEsperaV1();
 $evento = new ClienteCriadoV1('cli-1');

 $consumidorV1->manipular($evento); // funciona como sempre

 $this->assertTrue($consumidorV1->processou('cli-1'));
 }

 public function test_mudanca_aditiva_nao_quebra_v1(): void
 {
 // Adicionar campo opcional à v1 não pode quebrar quem já a usa.
 $this->assertConsumidorV1Intacto();
 }
}
Um teste de compatibilidade guarda a promessa: a evolução não pode quebrar quem depende da versão anterior.
Tabela de decisão Mudança aditiva ou ruptura?
Mudança no contratoTipoComo proceder
Adicionar campo opcionalAditivaNo próprio contrato
Adicionar operação novaAditivaNo próprio contrato
Renomear um campoRupturaNova versão (v2)
Mudar o tipo de um campoRupturaNova versão (v2)
Remover um campoRupturaNova versão; aposenta a antiga ao fim

Estudo de Caso

Caso aplicado

Evoluir um contrato com parceiros externos

Contexto. Um contrato consumido por integrações externas (Cap VI) precisava ganhar informação nova. Os parceiros não migram instantaneamente — alguns levam semanas para atualizar seus sistemas.

Alterar no lugar
Quebra parceiros sem aviso
Nova versão
Coexistência ordenada
Ganho
Evolução sem ruptura

Decisão. A informação foi adicionada numa nova versão do contrato, mantida em paralelo à anterior. A engine passou a atender as duas versões, e os parceiros foram avisados para migrar no seu tempo.

Resultado. Nenhum parceiro foi quebrado pela evolução. A versão antiga só foi aposentada quando o último consumidor migrou — a mudança que poderia ter sido uma ruptura em massa virou uma transição ordenada.

Incidente Real

Severidade · Alta

O campo renomeado que quebrou consumidores

  • Antes Um campo de um contrato publicado foi renomeado diretamente, por parecer uma melhoria inofensiva de clareza.
  • Sintoma Consumidores que esperavam o nome antigo passaram a falhar — alguns em produção, sem aviso prévio.
  • Diagnóstico A renomeação era uma mudança que quebra, tratada como se fosse aditiva. A promessa do contrato foi rompida sob os pés de quem dependia dela.
  • Correção Reversão e reintrodução da mudança como nova versão, com as duas coexistindo durante a migração dos consumidores.
  • Kaizen Vira regra com teste de compatibilidade no CI: mudança que quebra exige nova versão; nunca alterar o contrato publicado no lugar.

Decisão Arquitetural

ADR-0013

Mudança que quebra exige nova versão; versões coexistem

Aceito
Contexto

Um contrato publicado é uma promessa aos consumidores. Alterá-lo no lugar (renomear, remover, mudar tipo) quebra quem depende dele, frequentemente em produção e sem aviso — multiplicado por muitos consumidores e parceiros.

Decisão

Mudanças aditivas evoluem o contrato no lugar; mudanças que quebram exigem nova versão. Durante a migração, as versões coexistem, e a antiga só é aposentada quando nenhum consumidor depende mais dela. Compatibilidade é verificada no CI.

Alternativas consideradas

Alterar o contrato no lugar — quebra consumidores sem aviso. Forçar todos a migrar de uma vez — inviável com parceiros externos. Nunca evoluir — o contrato apodrece.

Consequências
PositivasConsumidores nunca são quebrados pela evolução. Migração no ritmo de cada um. Ecossistema evolui com segurança.
NegativasExige manter versões em paralelo por um tempo e um processo para aposentar a antiga com segurança.
RiscosCoexistência de versões adiciona manutenção; mitigada por aposentar a antiga assim que possível.

Antipadrões

Renomear ou remover campo de contrato publicado

Ruptura silenciosa que quebra consumidores. Correção: nova versão; a antiga coexiste até todos migrarem.

Forçar migração instantânea

Exigir que todos os consumidores mudem no mesmo instante. Correção: coexistência de versões; cada um migra no seu ritmo.

Exercício

Classifique uma mudança de contrato

  1. Pegue uma mudança que você precisa fazer num contrato (interface, evento, resposta). Ela é aditiva (campo/opção nova) ou quebra (renomeia, remove, muda tipo)?
  2. Se é aditiva, confirme: ela realmente não afeta nenhum consumidor que espera a forma atual?
  3. Se quebra, desenhe a nova versão e o plano de coexistência: por quanto tempo v1 e v2 conviverão? Como você saberá que ninguém usa mais a v1?
  4. Esboce um teste de compatibilidade que falhe o build se um consumidor da versão anterior for quebrado.
Meta: tratar uma mudança real de contrato pela regra certa — evoluindo no lugar se for aditiva, ou versionando com coexistência se quebrar.
✓ Checklist do tópico
  • Contratos publicados são tratados como promessas aos consumidores
  • Mudanças aditivas evoluem o contrato no lugar
  • Mudanças que quebram exigem uma nova versão
  • Versões antiga e nova coexistem durante a migração
  • A compatibilidade é verificada por teste no CI
Meta Dados · Engenharia de Plataformas · Capítulo II · §2.6Fim do Capítulo II · voltar ao início
Meta Dados · Engenharia de Plataformas
Capítulo III

Plataforma
Orientada por
Metadados

O diferencial da plataforma sem cair na linguagem de programação acidental: o metamodelo, as ações nomeadas, o Business Rules Engine e o estúdio governado de edição.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo III
Capítulo III · Plataforma Orientada por Metadados
3.1

O Metamodelo

O poder de uma plataforma orientada por metadados é também sua maior armadilha: dar tanta liberdade ao dado que ele vira, sem querer, uma linguagem de programação que ninguém projetou.

Pré-requisitos · Cap I Tese · Cap II EnginesConecta com · 3.2 Ações · 3.4 EnginesTempo de leitura · ~13 min

O Problema

Uma plataforma orientada por metadados promete algo sedutor: descrever o sistema como dado, sem programar. Um formulário novo vira um registro; uma regra, uma linha de configuração. Mas há uma armadilha à espreita, e ela é traiçoeira justamente porque começa parecendo uma vitória.

A armadilha é a linguagem de programação acidental. Conforme o metadado ganha poder — primeiro um campo, depois uma condição, depois um laço, depois uma expressão aninhada — a configuração silenciosamente se transforma numa linguagem de programação completa. Só que uma linguagem sem tipos, sem testes, sem depurador, sem documentação, e que ninguém jamais projetou conscientemente. O que deveria ser dado vira código disfarçado, e o pior tipo de código: o que finge não ser código.

O sintoma da linguagem acidental

Quando você vê condições aninhadas, laços ou expressões complexas dentro da configuração, o metadado deixou de descrever e passou a programar. Correção: a lógica pertence ao código (a ações nomeadas, §3.2), não ao dado.

A Decisão

Metadado descreve estrutura; código executa algoritmo. A fronteira é inviolável: o dado nunca contém lógica, e o código nunca contém regra de negócio variável.

O metamodelo é a descrição declarativa do sistema — o que existe, não como funciona. Uma entidade "cliente" é descrita por seus campos, seus tipos, suas relações: estrutura. Como o cliente é validado, salvo, ou cobrado é algoritmo, e algoritmo é código. A regra que mantém a sanidade: o metadado descreve, o código executa, e a fronteira entre os dois nunca é cruzada. Quando surge a tentação de colocar uma decisão no metadado, essa decisão vira uma ação nomeada (§3.2) — um ponto de código referenciado pelo dado, não lógica embutida nele.

Diagrama conceitual Metadado descreve, código executa
METADADO · descreve "o cliente tem nome, CNPJ e limite" estrutura, não algoritmo CÓDIGO · executa o motor que lê a descrição e a realiza — testado, estável lê A FRONTEIRA INVIOLÁVEL metadado nunca contém lógica; código nunca contém regra de negócio variável
Figura 3.1-a. A fronteira que define a plataforma: o metadado descreve a estrutura, o motor de código a realiza. Cruzar essa linha é o caminho para a linguagem acidental.

Descrever uma entidade como dado

O metamodelo guarda a descrição — campos, tipos, obrigatoriedade. Note o que não está aqui: nenhuma lógica, nenhuma condição, nenhum algoritmo.

meta_entidade.sqlPostgreSQL
-- O metamodelo DESCREVE a estrutura. Nenhuma lógica aqui.
CREATE TABLE meta_entidade (
    id           BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    chave        TEXT NOT NULL,            -- 'cliente'
    rotulo       TEXT NOT NULL             -- 'Cliente'
);

CREATE TABLE meta_campo (
    id           BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    entidade_id  BIGINT NOT NULL REFERENCES meta_entidade,
    chave        TEXT NOT NULL,            -- 'cnpj'
    tipo         TEXT NOT NULL,            -- 'texto','numero','data'
    obrigatorio  BOOLEAN NOT NULL DEFAULT false
    -- NÃO existe coluna 'logica' ou 'expressao'. Isso seria a armadilha.
);
A estrutura é dado puro: campos, tipos, obrigatoriedade. Nenhuma coluna guarda "como validar" — isso é código.
LeitorDeMetamodelo.phpPHP
// O CÓDIGO lê a descrição e a realiza. A lógica vive aqui, testável.
final class LeitorDeMetamodelo
{
    public function entidade(string $chave): DescricaoDeEntidade
    {
        // Carrega a DESCRIÇÃO (dado) e a entrega como um objeto rico.
        $campos = $this->repo->camposDe($chave);
        return new DescricaoDeEntidade($chave, $campos);
        // Como validar cada tipo é lógica DESTE código, não do metadado.
    }
}
O motor interpreta a descrição. Toda decisão — como validar um CNPJ, por exemplo — é código testável, não dado solto.

A Verificação

MetamodeloTest.phpPHP
final class MetamodeloTest extends TestCase
{
    public function test_descreve_estrutura_sem_logica(): void
    {
        $entidade = $this->leitor->entidade('cliente');

        // O metadado entrega ESTRUTURA: campos e tipos.
        $this->assertTrue($entidade->temCampo('cnpj'));
        $this->assertSame('texto', $entidade->tipoDe('cnpj'));
    }

    public function test_metamodelo_nao_contem_expressoes(): void
    {
        // Garante a fronteira: nenhum campo guarda lógica/expressão.
        $this->assertNenhumCampoContemLogica('cliente');
    }
}
O segundo teste é a guarda da fronteira: ele falha se alguém tentar embutir lógica no metadado.
Tabela de decisão Estrutura (dado) ou algoritmo (código)?
O que se quer expressarÉ...Onde mora
"Cliente tem um campo CNPJ"EstruturaMetamodelo (dado)
"CNPJ deve ter 14 dígitos válidos"AlgoritmoCódigo (validação)
"Pedido tem itens"EstruturaMetamodelo (relação)
"Se total > X, aplicar desconto Y"AlgoritmoRegra nomeada (§3.3)
"Campo obrigatório"EstruturaMetamodelo (atributo)

Estudo de Caso

Caso aplicado

O formulário que nasceu sem deploy

Contexto. O negócio precisava de um novo cadastro — uma entidade com uma dezena de campos, alguns obrigatórios. No modelo tradicional, isso seria uma nova tabela, uma nova tela, novo código, novo deploy: dias de trabalho.

Modelo tradicional
Nova tabela + tela + deploy
Metamodelo
Descrição como dado
Ganho
Cadastro sem deploy, fronteira intacta

Decisão. Descrever a entidade no metamodelo — campos, tipos, obrigatoriedade — como dado. Os engines dirigidos por metadados (§3.4) leem essa descrição e geram a tela, a validação estrutural e a persistência automaticamente.

Resultado. O cadastro novo nasceu como configuração, sem uma linha de código de aplicação nem deploy. E — crucialmente — sem nenhuma lógica embutida no dado: as validações de regra de negócio, quando necessárias, viraram ações nomeadas. A fronteira foi respeitada, e o ganho de velocidade veio sem o custo da linguagem acidental.

Incidente Real

Severidade · Média

A configuração que virou código disfarçado

  • Antes Para evitar um deploy, alguém embutiu uma condição na configuração — "se o campo A, então exija o campo B". Funcionou. Então veio outra condição. E outra.
  • Sintoma A configuração acumulou condições aninhadas e dependências entre campos — uma lógica complexa, sem testes, que ninguém conseguia mais entender ou depurar.
  • Diagnóstico O metadado havia se tornado uma linguagem de programação acidental: poder de código, sem nenhuma das proteções do código.
  • Correção A lógica embutida foi extraída para ações e regras nomeadas (§3.2, §3.3) — código testável referenciado pelo dado. O metadado voltou a só descrever.
  • Kaizen Vira regra com verificação: o metamodelo não aceita expressões nem condições embutidas; lógica só por referência a código nomeado.

Decisão Arquitetural

ADR-0014

Metadado descreve estrutura; lógica nunca é embutida no dado

Aceito
Contexto

Plataformas orientadas por metadados tendem a uma "linguagem de programação acidental": o dado acumula condições, laços e expressões, virando código sem tipos, testes ou depurador — o pior tipo de código.

Decisão

O metamodelo descreve apenas estrutura (entidades, campos, tipos, relações). Algoritmos e regras de negócio variáveis vivem em código nomeado (ações §3.2, regras §3.3), referenciado pelo metadado, nunca embutido nele. A fronteira é verificada.

Alternativas consideradas

Liberdade total no metadado — leva à linguagem acidental. Tudo em código — perde o ganho de descrever sem deploy (Cap I). Expressões "só um pouco" no dado — a porta de entrada da degradação.

Consequências
PositivasVelocidade de descrever sem deploy, sem o custo da linguagem acidental. Lógica testável e depurável. Metadado legível.
NegativasExige disciplina para resistir a embutir "só uma condiçãozinha" no dado, e o mecanismo de ações nomeadas.
RiscosA fronteira pode ser burlada; mitigada por verificação que rejeita lógica embutida no metamodelo.

Antipadrões

Lógica embutida no metadado

Condições, laços ou expressões dentro da configuração. Correção: a lógica vira ação ou regra nomeada (código), referenciada pelo dado.

Metadado que descreve "como", não "o quê"

O dado tentando expressar algoritmo em vez de estrutura. Correção: metadado descreve o que existe; código realiza como funciona.

Exercício

Encontre a lógica escondida no seu dado

  1. Olhe a configuração do seu sistema (arquivos de config, tabelas de metadados, regras em banco). Há condições, laços ou expressões ali?
  2. Para cada uma, pergunte: isto descreve estrutura (o quê) ou expressa algoritmo (como)? O segundo caso é linguagem acidental nascendo.
  3. Escolha o pior caso — a lógica mais complexa embutida no dado. Como você a extrairia para código nomeado, deixando no dado apenas a referência?
  4. Esboce uma verificação que detecte lógica embutida no seu metamodelo, antes que ela se espalhe.
Meta: identificar onde seu próprio sistema já começou a transformar dado em código disfarçado — e planejar a extração para código nomeado.
✓ Checklist do tópico
  • O metamodelo descreve apenas estrutura (entidades, campos, tipos, relações)
  • Nenhuma lógica, condição ou expressão é embutida no metadado
  • Algoritmos e regras vivem em código nomeado, referenciado pelo dado
  • A fronteira metadado/código é verificada automaticamente
  • Descrever uma entidade nova não exige deploy nem lógica no dado
Meta Dados · Engenharia de Plataformas · Capítulo III · §3.1Próximo → 3.2 Ações Nomeadas
Capítulo III · Plataforma Orientada por Metadados
3.2

Ações Nomeadas

Se o metadado não pode conter lógica, como ele dispara comportamento? Pela ponte das ações nomeadas: o dado refere um nome, e o nome resolve para código real — testável, trocável, seguro.

Pré-requisitos · 3.1 MetamodeloConecta com · 3.3 BRE · Cap VI WorkflowTempo de leitura · ~12 min

O Problema

O tópico anterior estabeleceu uma fronteira rígida: metadado descreve, não programa. Mas isso levanta uma pergunta imediata. Se o dado não pode conter lógica, como um formulário descrito como dado dispara uma validação de negócio? Como uma regra configurável executa uma ação? Sem uma resposta, a fronteira se torna uma prisão — e a tentação de burlá-la, embutindo "só uma lógica" no dado, volta com força.

O risco, sem um mecanismo claro, é o pior dos dois mundos: ou o metadado vira código disfarçado (a linguagem acidental do §3.1), ou o sistema fica engessado, incapaz de associar comportamento à sua descrição declarativa. Precisamos de uma ponte que conecte o dado ao código sem que um contamine o outro.

A pergunta que este tópico responde

Como o metadado, proibido de conter lógica, dispara comportamento real? A resposta é a indireção: o dado refere um nome, e o nome resolve para código. A ponte mantém os dois lados puros.

A Decisão

O comportamento é encapsulado em ações nomeadas: o metadado refere uma ação pelo nome; um registro resolve o nome para uma classe de código real. O dado conhece o nome, nunca a lógica.

Uma ação nomeada é um pedaço de comportamento encapsulado numa classe de código, identificado por um nome estável — "aprovar_pedido", "calcular_frete", "validar_cnpj". O metadado refere esse nome; ele não sabe (nem precisa saber) como a ação é implementada. Um registro de ações faz a ponte: dado um nome, ele resolve a classe de código correspondente. Assim, o dado permanece declarativo (refere nomes), o código permanece testável (implementa ações), e a fronteira do §3.1 é honrada — a lógica vive no código, referenciada pelo dado.

Arquitetura lógica A ponte das ações nomeadas
METADADOrefere: "aprovar_pedido" REGISTRO DE AÇÕESnome → classe de códigoa ponte CÓDIGOAprovarPedido::executar() o dado conhece só o NOME; a implementação é código testável, trocável
Figura 3.2-a. A indireção pelo nome é a ponte: o metadado refere "aprovar_pedido", o registro resolve para a classe, e a implementação real fica isolada no código.

Definir e registrar uma ação

Uma ação é uma classe com um contrato único — executar. O nome a torna referenciável pelo dado.

Acao.phpPHP
// Toda ação nomeada cumpre este contrato. O nome a torna referenciável.
interface Acao
{
    public function nome(): string;            // 'aprovar_pedido'
    public function executar(Contexto $ctx): Resultado;
}

final class AprovarPedido implements Acao
{
    public function nome(): string { return 'aprovar_pedido'; }

    public function executar(Contexto $ctx): Resultado
    {
        // A lógica REAL vive aqui — código testável, tipado.
        $pedido = $ctx->pedido();
        $pedido->aprovar();
        return Resultado::ok();
    }
}
A ação é código pleno: tipado, testável, com acesso ao domínio. O dado só precisará do seu nome.
RegistroDeAcoes.phpPHP
// A PONTE: resolve um nome (dado) para uma ação (código).
final class RegistroDeAcoes
{
    /** @var array<string, Acao> nome → ação */
    public function __construct(private readonly array $acoes) {}

    public function resolver(string $nome): Acao
    {
        // O metadado passou só o NOME. Aqui ele vira código executável.
        return $this->acoes[$nome]
            ?? throw new AcaoNaoRegistrada($nome);
    }
}
O registro é a única peça que conhece os dois lados: recebe o nome do dado, devolve a classe de código.
uso_no_metadado.sqlPostgreSQL
-- O metadado REFERE a ação pelo nome. Nenhuma lógica embutida.
INSERT INTO meta_botao (entidade, rotulo, acao)
VALUES ('pedido', 'Aprovar', 'aprovar_pedido');
--                                          ^^^^^^^^^^^^^^^
-- Só o NOME. A implementação é código, resolvida pelo registro.
No dado, apenas o nome da ação. Trocar a implementação não toca o metadado; ele continua referindo o mesmo nome.

A Verificação

AcaoNomeadaTest.phpPHP
final class AcaoNomeadaTest extends TestCase
{
    public function test_registro_resolve_nome_para_acao(): void
    {
        $registro = new RegistroDeAcoes(['aprovar_pedido' => new AprovarPedido()]);

        $acao = $registro->resolver('aprovar_pedido');

        $this->assertInstanceOf(AprovarPedido::class, $acao);
    }

    public function test_nome_desconhecido_falha_alto(): void
    {
        // Um nome não registrado falha cedo e claro — não silenciosamente.
        $this->expectException(AcaoNaoRegistrada::class);
        (new RegistroDeAcoes([]))->resolver('inexistente');
    }
}
A ação é testada como código comum. E o registro falha alto para um nome desconhecido — nunca um comportamento silencioso.
Tabela de decisão O que é nome (dado) e o que é implementação (código)
ElementoNaturezaMuda com
"aprovar_pedido" no metadadoDado (nome)Configuração, sem deploy
Classe AprovarPedidoCódigoDeploy, com testes
Mapeamento nome → classeCódigo (registro)Deploy
Qual ação um botão disparaDado (referência)Configuração, sem deploy

Estudo de Caso

Caso aplicado

Uma ação, muitos pontos de uso

Contexto. A validação de um documento de identificação empresarial era necessária em vários lugares: no cadastro de cliente, na importação em lote, numa regra de negócio, num passo de workflow. Reimplementá-la em cada lugar seria duplicação e risco de divergência.

Sem ação nomeada
Lógica duplicada em N lugares
Com ação nomeada
Uma implementação, N referências
Ganho
Correção única, reúso total

Decisão. Encapsular a validação numa única ação nomeada ("validar_documento"), registrada uma vez. Cada ponto — formulário, importador, regra (§3.3), workflow (Cap VI) — passa a referenciá-la pelo nome.

Resultado. A lógica de validação existe em um só lugar de código, testada uma vez, e é reutilizada por referência em todos os contextos. Uma correção na validação beneficia todos os pontos simultaneamente, sem tocar nenhuma configuração.

Incidente Real

Severidade · Baixa

A ação referenciada que não existia

  • Antes Uma configuração passou a referenciar uma ação por um nome que, por um erro de digitação, não correspondia a nenhuma ação registrada.
  • Sintoma No modelo ingênuo, a referência inválida poderia falhar silenciosamente — o comportamento simplesmente não acontecia, sem erro claro.
  • Diagnóstico Faltava a validação de que todo nome referenciado no metadado corresponde a uma ação efetivamente registrada.
  • Correção O registro passou a falhar alto e claro para nomes desconhecidos, e uma verificação no CI confere que todas as referências do metadado resolvem.
  • Kaizen Vira regra: nenhuma referência a ação fica órfã; a integridade nome→código é verificada antes do uso.

Decisão Arquitetural

ADR-0015

Comportamento via ações nomeadas: o dado refere, o código implementa

Aceito
Contexto

A fronteira do §3.1 proíbe lógica no metadado, mas o dado precisa disparar comportamento. Sem um mecanismo, ou o metadado vira código disfarçado, ou o sistema engessa.

Decisão

O comportamento é encapsulado em ações nomeadas (classes de código com nome estável). O metadado refere a ação pelo nome; um registro resolve o nome para a classe. O dado conhece o nome, nunca a implementação. Referências órfãs falham alto e são verificadas.

Alternativas consideradas

Lógica embutida no dado — a linguagem acidental (§3.1). Comportamento fixo no código — perde a flexibilidade declarativa. Referência sem validação — falhas silenciosas por nome inválido.

Consequências
PositivasDado declarativo e código testável coexistem. Reúso de uma ação em muitos contextos. Trocar implementação sem tocar o dado.
NegativasExige manter o registro de ações e verificar a integridade das referências.
RiscosNome mal referenciado quebra comportamento; mitigado por falha alta e verificação no CI.

Antipadrões

Referência a ação sem validação

O metadado refere um nome que não resolve, falhando em silêncio. Correção: falha alta para nome desconhecido + verificação de integridade no CI.

Lógica na "cola" entre dado e ação

Colocar decisões no mecanismo que conecta dado e ação. Correção: a ponte só resolve nomes; a lógica vive dentro da ação.

Exercício

Transforme comportamento em ação nomeada

  1. Encontre uma lógica de negócio que aparece em mais de um lugar do seu sistema. Ela está duplicada, ou centralizada?
  2. Encapsule-a numa ação nomeada: uma classe com um nome estável e um método executar. O que ela precisa do contexto?
  3. Identifique os pontos que a usariam (formulários, regras, workflows). Cada um poderia referenciá-la apenas pelo nome?
  4. Garanta a integridade: como você detectaria, antes do uso, uma referência a uma ação que não existe?
Meta: centralizar uma lógica duplicada numa ação nomeada, reutilizável por referência — e proteger contra referências órfãs.
✓ Checklist do tópico
  • O comportamento é encapsulado em ações nomeadas (código)
  • O metadado refere ações pelo nome, nunca contém a lógica
  • Um registro resolve nomes para classes de código
  • A mesma ação é reutilizada por referência em vários contextos
  • Referências a nomes inexistentes falham alto e são verificadas no CI
Meta Dados · Engenharia de Plataformas · Capítulo III · §3.2Próximo → 3.3 Business Rules Engine
Capítulo III · Plataforma Orientada por Metadados
3.3

Business Rules Engine

Regras de negócio mudam mais que qualquer outra coisa no sistema. Cravá-las no código condena o negócio a esperar por um deploy a cada ajuste. Mas regras como dado precisam de uma estrutura rígida — ou viram a linguagem acidental.

Pré-requisitos · 3.1 Metamodelo · 3.2 AçõesConecta com · 3.5 Studio · Cap VI WorkflowTempo de leitura · ~14 min

O Problema

Regras de negócio são a parte mais volátil de qualquer sistema. "Clientes acima de tal porte exigem aprovação", "pedidos de tal tipo têm desconto", "operações de tal valor disparam revisão" — essas regras mudam com o mercado, com a estratégia, com a regulação. Quando estão cravadas no código, cada mudança de regra é um pedido ao time de desenvolvimento, um ciclo, um deploy. O negócio fica refém da agenda da engenharia para ajustar o que é, essencialmente, uma decisão de negócio.

A solução óbvia — colocar as regras como dado, editáveis sem deploy — esbarra na armadilha do §3.1. Uma regra é, afinal, lógica condicional. Se a deixarmos livre no dado, ela acumula condições aninhadas, operadores arbitrários, expressões complexas — e vira exatamente a linguagem de programação acidental que queremos evitar. Precisamos de regras como dado, mas com uma forma tão rígida que não há espaço para a degradação.

A tensão central do BRE

Regras precisam ser dado (para mudar sem deploy), mas regras são lógica (que tende à linguagem acidental). A saída: uma forma fixa e rígida — SE condição declarativa ENTÃO ação nomeada — que dá flexibilidade sem dar liberdade ilimitada.

A Decisão

Toda regra tem a forma fixa SE condição ENTÃO ação. A condição é declarativa (atributo, operador, valor); a ação é nomeada (§3.2). A forma rígida dá flexibilidade sem virar linguagem acidental.

O Business Rules Engine avalia regras com uma estrutura deliberadamente restrita. Cada regra é um par condição → ação. A condição é declarativa e tem forma fixa: um atributo, um operador de um conjunto fechado (igual, maior, contém...), e um valor. Nada de aninhamento arbitrário, nada de expressões livres. A ação é uma ação nomeada (§3.2): a regra refere o que fazer, sem conter a lógica. O motor avalia as condições contra os dados e, quando satisfeitas, dispara as ações — registrando cada execução para auditoria. A rigidez da forma é o que mantém a regra como dado legível, e não como código disfarçado.

Diagrama conceitual A estrutura de uma regra de negócio
REGRA (dado): SE condição ENTÃO ação"se cliente é transportadora E frota > 50 → exigir aprovação" CONDIÇÃO (declarativa)atributo · operador · valor AÇÃO (nomeada, §3.2)refere código, não o contém O MOTOR avalia e executae registra cada execução (auditável)
Figura 3.3-a. A forma fixa: condição declarativa (atributo/operador/valor) e ação nomeada. O motor avalia e executa, registrando tudo — flexibilidade contida pela estrutura.

As regras como dado estruturado

bre_rules.sqlPostgreSQL
-- Regras como DADO, em forma fixa. Condição e ação separadas.
CREATE TABLE bre_rule (
    id          BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    chave       TEXT NOT NULL,        -- 'aprovacao_grande_frota'
    entidade    TEXT NOT NULL,        -- 'cliente'
    ativa       BOOLEAN NOT NULL DEFAULT true
);

-- Condição: forma FIXA. atributo + operador + valor. Sem aninhamento livre.
CREATE TABLE bre_condition (
    id          BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    rule_id     BIGINT NOT NULL REFERENCES bre_rule,
    atributo    TEXT NOT NULL,        -- 'frota'
    operador    TEXT NOT NULL,        -- conjunto FECHADO: '>', '=', 'contém'
    valor       TEXT NOT NULL         -- '50'
);

-- Ação: referência a ação NOMEADA (§3.2). Não contém lógica.
CREATE TABLE bre_action (
    id          BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    rule_id     BIGINT NOT NULL REFERENCES bre_rule,
    acao        TEXT NOT NULL         -- 'exigir_aprovacao' (nome, §3.2)
);
A condição tem três colunas fixas — não há onde aninhar lógica. O operador vem de um conjunto fechado. A ação é só um nome.
AvaliadorDeRegras.phpPHP
// O motor avalia a forma fixa e dispara ações nomeadas.
final class AvaliadorDeRegras
{
    public function avaliar(string $entidade, Dados $dados): void
    {
        foreach ($this->repo->regrasAtivas($entidade) as $regra) {
            if ($this->condicoesSatisfeitas($regra, $dados)) {
                // Resolve a ação NOMEADA (§3.2) e a executa.
                foreach ($regra->acoes() as $nomeAcao) {
                    $this->acoes->resolver($nomeAcao)->executar($dados->contexto());
                }
                $this->registro->execucao($regra, $dados);  // auditável
            }
        }
    }

    private function condicoesSatisfeitas(Regra $r, Dados $d): bool
    {
        // Avalia atributo/operador/valor — operadores de conjunto fechado.
        foreach ($r->condicoes() as $c) {
            if (! $this->operadores->aplicar($c->operador, $d->valor($c->atributo), $c->valor)) {
                return false;
            }
        }
        return true;
    }
}
O motor é o único código; as regras são dado. Os operadores vêm de um conjunto fechado — não há expressão arbitrária a interpretar.

A Verificação

BusinessRulesTest.phpPHP
final class BusinessRulesTest extends TestCase
{
    public function test_regra_dispara_quando_condicao_satisfeita(): void
    {
        // Regra: SE frota > 50 ENTÃO exigir_aprovacao. Dado: frota = 80.
        $acaoEspia = new AcaoEspia('exigir_aprovacao');
        $avaliador = $this->avaliadorCom($acaoEspia, regra: 'frota > 50 → exigir_aprovacao');

        $avaliador->avaliar('cliente', $this->dadosComFrota(80));

        $this->assertTrue($acaoEspia->foiExecutada());
    }

    public function test_regra_nao_dispara_quando_condicao_falsa(): void
    {
        // Mesma regra, dado: frota = 20 (não satisfaz).
        $acaoEspia = new AcaoEspia('exigir_aprovacao');
        $avaliador = $this->avaliadorCom($acaoEspia, regra: 'frota > 50 → exigir_aprovacao');

        $avaliador->avaliar('cliente', $this->dadosComFrota(20));

        $this->assertFalse($acaoEspia->foiExecutada());
    }
}
As regras são testadas como qualquer comportamento. A condição satisfeita dispara a ação; a falsa, não — comportamento determinístico.
Tabela de decisão Regra no código vs no BRE
AspectoRegra no códigoRegra no BRE (dado)
Mudar a regraDeployConfiguração
Quem alteraDesenvolvedorAnalista de negócio
Risco de linguagem acidental—Contido pela forma fixa
Auditoria da execuçãoAd hocRegistrada pelo motor
Lógica da açãoMisturadaEm ação nomeada (§3.2)

Estudo de Caso

Caso aplicado

A regra de aprovação que o negócio ajusta sozinho

Contexto. Uma política de negócio definia que clientes de certo perfil — por exemplo, transportadoras com frota acima de um limite — exigiam aprovação manual antes de prosseguir. Esse limite mudava conforme a estratégia comercial, às vezes mensalmente.

Regra no código
Cada ajuste = deploy
Regra no BRE
Analista ajusta o valor
Ganho
Política ágil, forma contida

Decisão. Expressar a política como regra no BRE: condição declarativa (tipo de cliente é transportadora E frota maior que o limite) e ação nomeada (exigir aprovação). O limite vira um valor na condição, editável pelo analista.

Resultado. Quando a estratégia muda o limite, o analista de negócio ajusta o valor na regra — sem deploy, sem ticket para a engenharia. A lógica de "exigir aprovação" permanece como ação nomeada, testada e estável; só o gatilho declarativo muda. Flexibilidade real, sem a linguagem acidental.

Incidente Real

Severidade · Média

A regra livre que virou um labirinto

  • Antes Numa primeira versão sem forma fixa, as regras permitiam condições aninhadas livremente. Para cobrir um caso complexo, criou-se uma regra com muitos níveis de aninhamento.
  • Sintoma As regras tornaram-se ilegíveis e imprevisíveis — um emaranhado de condições dentro de condições que ninguém conseguia auditar ou prever o resultado.
  • Diagnóstico A liberdade na forma da condição reproduziu a linguagem acidental do §3.1, agora dentro do próprio BRE.
  • Correção A forma da condição foi restringida a atributo/operador/valor, com operadores de conjunto fechado. Casos complexos passam a combinar regras simples ou a usar uma ação nomeada dedicada.
  • Kaizen Vira regra: a condição do BRE tem forma fixa; complexidade que não cabe nela pertence a uma ação nomeada (código testável), não ao dado.

Decisão Arquitetural

ADR-0016

Regras de negócio como dado em forma fixa: condição declarativa + ação nomeada

Aceito
Contexto

Regras de negócio são a parte mais volátil do sistema; no código, cada mudança exige deploy. Como dado livre, viram a linguagem acidental (§3.1) — condições aninhadas e expressões ilegíveis.

Decisão

Toda regra tem a forma fixa SE condição ENTÃO ação. A condição é declarativa (atributo, operador de conjunto fechado, valor), sem aninhamento livre; a ação é nomeada (§3.2). O motor avalia e registra cada execução. Complexidade que não cabe na forma vira ação nomeada.

Alternativas consideradas

Regras no código — deploy a cada ajuste. Regras como dado livre — linguagem acidental dentro do BRE. Linguagem de expressão própria — reinventa uma linguagem sem ferramentas.

Consequências
PositivasNegócio ajusta regras sem deploy. Forma fixa impede a degradação. Execução auditável. Ação reutiliza código testado.
NegativasExige restringir a forma da condição e direcionar complexidade para ações nomeadas.
RiscosCasos muito complexos pressionam a forma; mitigado por compor regras simples ou usar ação nomeada dedicada.

Antipadrões

Condição de regra com aninhamento livre

Condições dentro de condições, sem forma fixa — a linguagem acidental no BRE. Correção: forma fixa atributo/operador/valor; complexidade vai para ação nomeada.

Lógica da ação dentro da regra

A regra contendo o que fazer, não só referindo. Correção: a ação é nomeada (§3.2); a regra só a referencia.

Exercício

Expresse uma política como regra de forma fixa

  1. Pegue uma regra de negócio do seu sistema que muda com alguma frequência. Hoje, ela vive no código (exige deploy para mudar) ou como dado?
  2. Decomponha-a na forma fixa: qual é a condição (atributo, operador, valor) e qual é a ação? A condição cabe nessa forma simples?
  3. Se a condição parece exigir aninhamento, pergunte: isso é mesmo uma condição, ou é lógica que deveria virar uma ação nomeada?
  4. Pense na auditoria: quando a regra disparar, como você saberá que ela disparou, e por quê?
Meta: reescrever uma política de negócio real como uma regra de forma fixa — flexível para o negócio ajustar, sem abrir espaço para a linguagem acidental.
✓ Checklist do tópico
  • Toda regra tem a forma fixa SE condição ENTÃO ação
  • A condição é declarativa: atributo, operador de conjunto fechado, valor
  • Não há aninhamento livre nem expressões arbitrárias na condição
  • A ação é nomeada (§3.2); a regra a refere, não a contém
  • O motor registra cada execução de regra para auditoria
Meta Dados · Engenharia de Plataformas · Capítulo III · §3.3Próximo → 3.4 Engines Dirigidos por Metadados
Capítulo III · Plataforma Orientada por Metadados
3.4

Engines Dirigidos por Metadados

Quatro engines geram tudo a partir da descrição: formulário, tela, menu e grade. O perigo não é gerá-los — é cada um interpretar o metadado à sua maneira, criando quatro verdades que se contradizem.

Pré-requisitos · 3.1 Metamodelo · 3.2 AçõesConecta com · 3.5 Studio · Cap V DesignTempo de leitura · ~13 min

O Problema

A promessa é poderosa: a partir de uma única descrição no metamodelo, quatro engines geram a interface inteira — o Form Engine monta o formulário, o Screen Engine compõe a tela, o Menu Engine constrói a navegação, o Grid Engine renderiza as listagens. Descrever uma vez, gerar em quatro lugares. Mas há um risco prático que mina a promessa por dentro.

O risco é a duplicação da lógica de resolução. Antes de gerar qualquer coisa, cada engine precisa responder perguntas idênticas: qual versão do metadado vale agora? Há um override específico desta empresa que sobrepõe o padrão? Quais permissões do usuário escondem ou revelam campos? Se cada engine responde essas perguntas à sua maneira, surgem quatro implementações sutilmente diferentes — e elas divergem. O formulário mostra um campo que a grade esconde; o menu respeita um override que a tela ignora. "Dirigido por metadados" vira quatro verdades em conflito, e o usuário vê um sistema incoerente.

O sintoma das quatro verdades

Quando o formulário e a listagem discordam sobre quais campos existem ou quem pode vê-los, a lógica de resolução foi duplicada e divergiu. Correção: um caminho único de resolução que os quatro engines compartilham.

A Decisão

Existe um único caminho de resolução de metadados. Os quatro engines consomem a descrição já resolvida — nunca reimplementam versão, override ou permissão por conta própria.

A resolução do metadado — decidir a versão vigente, aplicar o override da empresa, filtrar pela permissão do usuário — acontece em um só lugar: o resolvedor. Ele recebe o contexto (empresa, usuário) e devolve a descrição resolvida: a verdade única sobre o que existe, o que está visível e como. Os quatro engines (Form, Screen, Menu, Grid) recebem essa descrição já pronta e apenas a renderizam, cada um à sua maneira visual. Eles nunca tocam na resolução; logo, não há como divergirem. Uma só verdade, quatro formas de apresentá-la.

Arquitetura lógica O caminho único de resolução
Form Engine Screen Engine Menu Engine Grid Engine RESOLVEDOR ÚNICO de metadados versão · override de empresa · permissão — resolvidos UMA vez DESCRIÇÃO RESOLVIDA · uma só verdade os quatro engines consomem o mesmo resultado
Figura 3.4-a. O resolvedor único centraliza versão, override e permissão. Os quatro engines consomem o mesmo resultado resolvido — impossível divergirem, porque não decidem nada sozinhos.

O resolvedor como única fonte de verdade

ResolvedorDeMetadados.phpPHP
// O ÚNICO lugar que resolve versão, override e permissão.
final class ResolvedorDeMetadados
{
    public function resolver(string $entidade, Contexto $ctx): DescricaoResolvida
    {
        // 1) versão vigente   2) override da empresa   3) permissão do usuário
        $base     = $this->versoes->vigente($entidade);
        $comOver  = $this->overrides->aplicar($base, $ctx->empresa());
        $visivel  = $this->permissao->filtrar($comOver, $ctx->usuario());

        // A verdade única que os quatro engines consumirão.
        return new DescricaoResolvida($visivel);
    }
}
Versão, override e permissão são resolvidos aqui, uma vez. O resultado é a única verdade que circula.
engines_consomem.phpPHP
// Os quatro engines RECEBEM a descrição resolvida. Não a calculam.
final class FormEngine
{
    public function renderizar(DescricaoResolvida $desc): Formulario
    {
        // Apenas apresenta os campos JÁ resolvidos. Zero lógica de versão/permissão.
        return Formulario::deCampos($desc->campos());
    }
}

final class GridEngine
{
    public function renderizar(DescricaoResolvida $desc): Grade
    {
        // MESMA descrição do FormEngine → impossível divergir dele.
        return Grade::deColunas($desc->campos());
    }
}
Form e Grid recebem a mesma descrição resolvida. Como nenhum decide nada, é impossível um mostrar o que o outro esconde.

A Verificação

ResolucaoUnicaTest.phpPHP
final class ResolucaoUnicaTest extends TestCase
{
    public function test_engines_veem_a_mesma_verdade(): void
    {
        // Uma resolução, consumida por dois engines diferentes.
        $desc = $this->resolvedor->resolver('cliente', $this->contexto());

        $campoForm = (new FormEngine())->renderizar($desc)->camposVisiveis();
        $colsGrid  = (new GridEngine())->renderizar($desc)->colunasVisiveis();

        // Form e Grid DEVEM concordar sobre os campos visíveis.
        $this->assertEquals($campoForm, $colsGrid);
    }
}
O teste prova a coerência: dois engines, uma resolução, mesmos campos visíveis. A divergência fica impossível por construção.
Tabela de decisão Resolução duplicada vs caminho único
Pergunta de resoluçãoDuplicada (em cada engine)Caminho único
Qual versão do metadado?Cada um decideResolvedor decide uma vez
Há override da empresa?Risco de divergirAplicado no resolvedor
Qual permissão aplica?Quatro filtros diferentesUm filtro, compartilhado
Form e Grid concordam?Nem sempreSempre, por construção

Estudo de Caso

Caso aplicado

Quando a tela e a listagem deixaram de brigar

Contexto. Numa primeira abordagem, o engine de formulário e o de listagem resolviam o metadado separadamente. Um override específico de uma empresa foi aplicado, e os dois passaram a discordar: um campo aparecia no formulário mas não na grade correspondente.

Resolução duplicada
Form e Grid divergiam
Resolução única
Uma verdade compartilhada
Ganho
Coerência por construção

Decisão. Extrair toda a resolução — versão, override, permissão — para um resolvedor único. Os engines deixaram de calcular qualquer coisa; passaram a receber a descrição já resolvida e apenas a renderizar.

Resultado. A incoerência desapareceu de vez. Como existe uma só verdade resolvida, o formulário e a listagem mostram exatamente o mesmo conjunto de campos visíveis para cada empresa e usuário. A consistência deixou de depender de disciplina e passou a ser garantida pela arquitetura.

Incidente Real

Severidade · Média

O override que só metade do sistema enxergou

  • Antes Um override de empresa foi configurado para ocultar certos campos. Mas só os engines que haviam reimplementado a lógica de override o respeitaram.
  • Sintoma Campos que deveriam estar ocultos para aquela empresa apareciam em alguns lugares e sumiam em outros — uma experiência incoerente e confusa.
  • Diagnóstico A lógica de aplicar overrides estava duplicada e divergente entre os engines; nem todos a implementavam igual.
  • Correção Centralização da resolução de overrides no resolvedor único. Todos os engines passaram a consumir a mesma descrição resolvida.
  • Kaizen Vira regra: nenhum engine resolve versão, override ou permissão; tudo vem do resolvedor único, verificado por teste de coerência.

Decisão Arquitetural

ADR-0017

Caminho único de resolução; engines apenas renderizam o resolvido

Aceito
Contexto

Quatro engines (Form, Screen, Menu, Grid) geram a UI a partir do metadado. Se cada um resolve versão, override e permissão por conta própria, as implementações divergem e o sistema mostra verdades contraditórias.

Decisão

A resolução de metadados (versão vigente, override de empresa, permissão de usuário) acontece em um resolvedor único, que produz a descrição resolvida. Os quatro engines consomem essa descrição e apenas a renderizam, sem reimplementar resolução. A coerência é verificada por teste.

Alternativas consideradas

Cada engine resolve sozinho — divergência e incoerência. Resolução copiada entre engines — duplicação que se desincroniza. Sem resolução explícita — comportamento imprevisível por contexto.

Consequências
PositivasCoerência entre engines por construção. Override e permissão aplicados uma vez. Engines simples (só renderizam).
NegativasExige projetar a descrição resolvida como contrato e manter a resolução fora dos engines.
RiscosO resolvedor concentra complexidade; mitigado por mantê-lo testado e os engines triviais.

Antipadrões

Cada engine resolvendo o metadado

Versão, override e permissão reimplementados em quatro lugares. Correção: um resolvedor único; engines só renderizam o resolvido.

Lógica de apresentação no resolvedor

O resolvedor decidindo cores, layout ou estilo. Correção: o resolvedor entrega a verdade dos dados; a apresentação é de cada engine (Cap V).

Exercício

Encontre a resolução duplicada

  1. No seu sistema, mais de um componente lê a mesma configuração ou metadado? Eles aplicam as mesmas regras (versão, permissão, contexto) para interpretá-la?
  2. Procure uma incoerência real: dois lugares que deveriam concordar sobre dados/permissões, mas às vezes discordam. A causa provável é resolução duplicada.
  3. Esboce um resolvedor único: o que ele receberia de contexto e que "verdade resolvida" entregaria aos consumidores?
  4. Escreva o teste de coerência: dois consumidores, uma resolução — eles concordam sobre o que está visível?
Meta: identificar onde seu sistema resolve a mesma configuração em lugares diferentes — e planejar um caminho único que elimine a divergência.
✓ Checklist do tópico
  • Existe um resolvedor único de metadados (versão, override, permissão)
  • Os engines consomem a descrição já resolvida
  • Nenhum engine reimplementa a lógica de resolução
  • Form, Screen, Menu e Grid concordam sobre o que está visível
  • A coerência entre engines é verificada por teste
Meta Dados · Engenharia de Plataformas · Capítulo III · §3.4Próximo → 3.5 Metadata Studio
Capítulo III · Plataforma Orientada por Metadados
3.5

Metadata Studio

O estúdio é o grande diferencial: criar formulários, menus e workflows sem programar. Mas parametrização sem governança é caos configurável — edição livre sem validação, versão ou trilha condena a plataforma à impossibilidade de depurar e reverter.

Pré-requisitos · 3.1–3.4 · Cap I governançaConecta com · Cap VII Auditoria · Cap VIII OperaçõesTempo de leitura · ~13 min

O Problema

O Metadata Studio é o ápice da plataforma orientada por metadados: a tela onde se criam formulários, campos, menus, relatórios e workflows sem escrever código, no espírito das grandes plataformas de baixo código. É o que permite ao negócio construir e ajustar o sistema na velocidade da necessidade. Mas justamente por ser tão poderoso, ele carrega um perigo que o Capítulo I já havia nomeado.

"Parametrização sem governança" é um antipadrão. Se qualquer pessoa edita o metamodelo livremente — sem validação que impeça uma configuração incoerente, sem versão que permita reverter, sem trilha que registre quem mudou o quê —, a plataforma se transforma num caos configurável. Uma alteração quebra algo em outro canto, e ninguém sabe quem a fez, quando, ou como desfazê-la. O poder de mudar tudo sem código vira o poder de quebrar tudo sem rastro. A flexibilidade, sem governança, devora a operabilidade.

O caos configurável

Edição livre do metamodelo, sem validação, versão ou auditoria, produz um sistema que ninguém consegue depurar nem reverter — uma mudança quebra algo, e não há como saber quem, quando ou como desfazer. Correção: governança em três travas.

A Decisão

A edição do metamodelo é governada por três travas: validação que rejeita o incoerente, versão imutável de cada mudança, e auditoria de quem alterou o quê. Toda mudança é reversível e rastreável.

O Metadata Studio oferece a flexibilidade do baixo código dentro de uma estrutura de governança. Validação: antes de uma alteração virar realidade, ela é checada — um campo que referencia uma ação inexistente, uma estrutura incoerente, é rejeitado na porta. Versão imutável: cada mudança publicada cria uma nova versão numerada do metamodelo; as versões anteriores não são apagadas, o que torna qualquer alteração reversível. Auditoria: toda edição registra quem a fez, quando, e o que mudou. Assim, o poder de configurar sem código convive com a capacidade de depurar, reverter e prestar contas — a flexibilidade ganha a operabilidade que o caos configurável destruiria.

Fluxograma operacional A edição governada do metamodelo
Ediçãoanalista altera Validaçãoé coerente? Nova versãoimutável, numerada Auditoriaquem, quando, o quê Reversível a qualquer versão validação rejeita o incoerente ANTES de virar versão
Figura 3.5-a. Toda edição passa pela validação antes de virar uma nova versão imutável; a auditoria registra tudo; e qualquer versão anterior pode ser restaurada. Flexibilidade com freios.

A publicação governada de uma mudança

PublicarAlteracao.phpPHP
// Toda alteração passa por validação, versão e auditoria. Sem exceção.
final class PublicarAlteracaoDeMetadados
{
    public function publicar(Alteracao $alteracao, Autor $autor): Versao
    {
        // 1) VALIDAÇÃO: rejeita o incoerente ANTES de publicar.
        $problemas = $this->validador->checar($alteracao);
        if ($problemas->existem()) {
            throw new AlteracaoInvalida($problemas);  // não passa
        }

        // 2) VERSÃO IMUTÁVEL: cria nova versão; as antigas permanecem.
        $versao = $this->versoes->criarNova($alteracao);

        // 3) AUDITORIA: quem, quando, o quê.
        $this->auditoria->registrar($autor, $alteracao, $versao);

        return $versao;
    }
}
As três travas em sequência: o que não valida não publica; o que publica vira versão; tudo é auditado. Nenhuma mudança escapa.
reverter.phpPHP
// Como as versões são imutáveis, reverter é restaurar uma anterior.
final class ReverterMetadados
{
    public function paraVersao(int $numero, Autor $autor): Versao
    {
        $anterior = $this->versoes->porNumero($numero);  // nunca foi apagada

        // Reverter é publicar a versão anterior como nova — também auditado.
        return $this->publicar->publicar(Alteracao::restaurar($anterior), $autor);
    }
}
A imutabilidade das versões é o que torna a reversão trivial e segura: a versão boa nunca deixou de existir.

A Verificação

GovernancaTest.phpPHP
final class GovernancaDoStudioTest extends TestCase
{
    public function test_alteracao_invalida_e_rejeitada(): void
    {
        // Uma alteração incoerente NÃO pode virar versão.
        $this->expectException(AlteracaoInvalida::class);
        $this->publicar->publicar($this->alteracaoIncoerente(), $this->autor());
    }

    public function test_toda_publicacao_e_auditada(): void
    {
        $versao = $this->publicar->publicar($this->alteracaoValida(), $this->autor('ana'));

        // O registro de auditoria DEVE existir, com autor e versão.
        $this->assertTrue($this->auditoria->temRegistro(autor: 'ana', versao: $versao));
    }
}
A governança é testada: o incoerente é barrado, e toda publicação deixa rastro de auditoria com autor e versão.
Tabela de decisão Edição livre vs edição governada
AspectoEdição livre (caos)Edição governada
Configuração incoerenteEntra e quebra algoRejeitada na validação
Desfazer uma mudançaSem como reverterRestaurar versão anterior
Quem mudou o quêSem rastroTrilha de auditoria
Depurar um problemaImpossívelVersão + auditoria mostram
FlexibilidadeTotal, mas perigosaPlena, com freios

Estudo de Caso

Caso aplicado

O diferencial de baixo código que continua operável

Contexto. A plataforma oferece, como grande diferencial, a criação de formulários, menus e workflows sem programação — poder comparável ao das maiores plataformas de baixo código do mercado. Mas esse mesmo poder, sem freios, ameaçava tornar o sistema impossível de operar.

Sem governança
Poder que quebra sem rastro
Com governança
Poder reversível e auditável
Ganho
Baixo código operável

Decisão. Envolver toda a edição nas três travas de governança: nenhuma alteração vira realidade sem passar pela validação; cada mudança publicada é uma nova versão imutável; e tudo é auditado, com autor e momento.

Resultado. O negócio mantém a velocidade de configurar sem código, mas o sistema continua operável: uma mudança problemática é barrada na validação ou revertida para a versão anterior em segundos, e a auditoria sempre responde quem mudou o quê. A flexibilidade do baixo código convive com a operabilidade — exatamente o que o Capítulo I exigiu.

Incidente Real

Severidade · Alta

A mudança de configuração que ninguém sabia reverter

  • Antes Numa fase sem governança plena, uma alteração no metamodelo quebrou um comportamento em produção. A mudança fora feita diretamente, sem versão nem registro.
  • Sintoma O sistema apresentou falha, mas não havia como saber exatamente o que mudara, quem mudara, nem como voltar ao estado anterior que funcionava.
  • Diagnóstico A ausência de versão imutável e de auditoria transformou uma simples alteração num incidente difícil de diagnosticar e reverter.
  • Correção Implementação das três travas: validação na entrada, versão imutável por mudança, auditoria de toda edição. Reverter passou a ser restaurar a versão anterior.
  • Kaizen Vira regra: nenhuma alteração de metamodelo sem validação, versão e auditoria; o estado bom anterior está sempre a um clique.

Decisão Arquitetural

ADR-0018

Edição do metamodelo é governada: validação, versão imutável e auditoria

Aceito
Contexto

O Metadata Studio dá poder de baixo código — criar formulários, menus e workflows sem programar. Sem governança, isso vira "parametrização sem governança" (antipadrão do Cap I): caos configurável, impossível de depurar ou reverter.

Decisão

Toda edição do metamodelo passa por três travas: validação que rejeita o incoerente antes de publicar; versão imutável e numerada de cada mudança (reversível); e auditoria registrando quem, quando e o quê. A governança é verificada por teste.

Alternativas consideradas

Edição livre — caos configurável. Só validação, sem versão — barra o incoerente mas não permite reverter. Bloquear a edição — mata o diferencial de baixo código.

Consequências
PositivasFlexibilidade de baixo código com operabilidade. Mudanças reversíveis. Auditoria responde quem mudou o quê. Incoerente barrado na porta.
NegativasExige construir validação, versionamento imutável e trilha de auditoria do metamodelo.
RiscosGovernança pode parecer atrito; mitigada por validação clara e reversão fácil que compensam o cuidado.

Antipadrões

Parametrização sem governança

Edição livre do metamodelo, sem validação, versão ou auditoria — o caos configurável do Cap I. Correção: as três travas de governança em toda edição.

Versão mutável do metamodelo

Sobrescrever a configuração no lugar, sem guardar a anterior. Correção: versão imutável; reverter é restaurar a versão boa que nunca foi apagada.

Exercício

Avalie a governança da sua configuração

  1. No seu sistema, quem pode alterar a configuração central (metadados, regras, parâmetros)? Há algo que impeça uma alteração incoerente de entrar?
  2. Se uma mudança de configuração quebrar algo em produção agora, você consegue: (a) saber quem a fez e quando, e (b) reverter ao estado anterior? Se não, falta governança.
  3. Esboce as três travas para sua configuração: que validação rejeitaria o incoerente? Como cada mudança viraria uma versão reversível? Como auditar quem mudou o quê?
  4. Pense no equilíbrio: como dar flexibilidade a quem configura sem abrir mão da capacidade de depurar e reverter?
Meta: avaliar honestamente se a configuração do seu sistema é governada — e desenhar as travas que faltam para que flexibilidade não custe a operabilidade.
✓ Checklist do tópico
  • A edição do metamodelo passa por validação que rejeita o incoerente
  • Cada mudança publicada cria uma versão imutável e numerada
  • Qualquer versão anterior pode ser restaurada (reversão)
  • Toda edição é auditada: quem, quando, o quê
  • A flexibilidade de baixo código convive com depuração e reversão
Meta Dados · Engenharia de Plataformas · Capítulo III · §3.5Fim do Capítulo III · voltar ao início
Meta Dados · Engenharia de Plataformas
Capítulo IV

Dados, Bancos
e Modelagem
em Escala

A camada onde a plataforma vive ou morre: convenções canônicas, isolamento multiempresa, histórico imutável, o fato como fonte de verdade e a separação entre operar e analisar.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo IV
Capítulo IV · Dados, Bancos e Modelagem em Escala
4.1

Convenções Canônicas

Uma plataforma com centenas de tabelas, cada uma com suas próprias colunas de controle, é uma plataforma onde nada é genérico. O contrato estrutural uniforme é o que torna possível tratar todas as tabelas com um só código.

Pré-requisitos · Cap I Capacidades · Cap II EnginesConecta com · 4.2 Tenant · 4.3 Soft deleteTempo de leitura · ~13 min

O Problema

Uma plataforma cresce em tabelas — dezenas, centenas. Se cada uma é modelada à sua maneira, com sua própria coluna de identidade aqui, seu próprio jeito de marcar exclusão ali, seu padrão particular de registrar quando algo mudou, então não há nada de genérico no sistema. Cada engine que precisa tratar tabelas de forma uniforme — auditoria, permissão, soft delete — esbarra na diversidade e acaba precisando de código específico para cada caso.

O resultado é a antítese de uma plataforma: um amontoado de tabelas que só se parecem por acaso, exigindo lógica sob medida para cada uma. O que deveria ser uma operação genérica ("marque esta linha como excluída", "diga a que empresa ela pertence") vira uma exceção por tabela. A falta de um contrato estrutural comum mina a própria ideia de um núcleo que serve a tudo.

O sintoma da falta de contrato

Quando cada tabela tem seu próprio jeito de marcar exclusão, identidade ou empresa, nenhuma engine consegue tratá-las de forma genérica. Correção: um contrato estrutural que toda tabela herda.

A Decisão

Toda tabela herda o mesmo contrato estrutural: id, tenant_id, created_at, updated_at, deleted_at. A uniformidade é o que torna o código genérico possível.

Cada tabela da plataforma segue convenções canônicas fixas. Uma coluna id de identidade estável, gerada pelo banco. Uma coluna tenant_id que diz a qual empresa a linha pertence (a base do isolamento multiempresa, §4.2). Colunas created_at e updated_at que registram quando a linha nasceu e mudou. Uma coluna deleted_at que implementa o soft delete (§4.3): nula significa viva. Esse contrato uniforme não é burocracia — é o que permite que um único código genérico trate qualquer tabela: filtrar por empresa, ocultar excluídos, ordenar por criação, tudo sem saber qual tabela específica está manipulando.

Diagrama conceitual O contrato estrutural de toda tabela
TODA TABELA herda o mesmo contrato ididentidade estável, gerada pelo banco tenant_ida qual empresa pertence (§4.2) created_at / updated_atquando nasceu e mudou deleted_atsoft delete: nulo = vivo (§4.3)
Figura 4.1-a. As colunas de controle que toda tabela herda. É essa repetição disciplinada que permite ao núcleo tratar centenas de tabelas com o mesmo código genérico.

O contrato como base de toda tabela

tabela_canonica.sqlPostgreSQL
-- Toda tabela de negócio herda este contrato estrutural.
CREATE TABLE cliente (
    -- ── contrato canônico (idêntico em TODA tabela) ──
    id          BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    tenant_id   BIGINT      NOT NULL,                  -- §4.2
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    deleted_at  TIMESTAMPTZ,                            -- nulo = vivo (§4.3)

    -- ── campos específicos do negócio ──
    nome        TEXT        NOT NULL,
    documento   TEXT        NOT NULL
);

-- O índice de empresa+vivo serve quase toda consulta (§4.2, §4.3).
CREATE INDEX ix_cliente_tenant_vivo
    ON cliente (tenant_id) WHERE deleted_at IS NULL;
As cinco colunas de controle vêm primeiro, idênticas em toda tabela; os campos de negócio seguem. O índice parcial reflete o padrão de acesso dominante.
RepositorioGenerico.phpPHP
// Porque o contrato é uniforme, UM repositório serve QUALQUER tabela.
abstract class RepositorioCanonico
{
    abstract protected function tabela(): string;

    public function vivosDoTenant(int $tenantId): array
    {
        // Filtra por empresa e oculta excluídos — genérico, sem saber a tabela.
        return $this->conexao->buscar(
            "SELECT * FROM {$this->tabela()}
              WHERE tenant_id = :t AND deleted_at IS NULL",
            ['t' => $tenantId],
        );
    }
}
Um único repositório base implementa "vivos de uma empresa" para toda tabela. Sem o contrato uniforme, isso seria código por tabela.

A Verificação

ContratoCanonicoTest.phpPHP
final class ContratoCanonicoTest extends TestCase
{
    /** @dataProvider todasAsTabelas */
    public function test_toda_tabela_segue_o_contrato(string $tabela): void
    {
        $colunas = $this->colunasDe($tabela);

        // Nenhuma tabela escapa do contrato estrutural.
        foreach (['id', 'tenant_id', 'created_at', 'updated_at', 'deleted_at'] as $c) {
            $this->assertContains($c, $colunas, "{$tabela} sem coluna canônica {$c}");
        }
    }
}
Um teste varre todas as tabelas e garante o contrato. Uma tabela que esqueça uma coluna canônica falha o build.
Tabela de decisão Cada tabela à sua maneira vs contrato canônico
Operação genéricaSem contratoCom contrato canônico
Ocultar excluídosCódigo por tabeladeleted_at IS NULL, genérico
Filtrar por empresaColuna varia por tabelatenant_id, sempre igual
Ordenar por criaçãoPadrão inconsistentecreated_at, uniforme
Tratar N tabelasN implementaçõesUm repositório genérico

Estudo de Caso

Caso aplicado

212 milhões de linhas em menos de 36 minutos

Contexto. Importar a base pública de dados empresariais — empresas, estabelecimentos, sócios e dados do regime simplificado — significava processar mais de 212 milhões de registros. Uma modelagem heterogênea, com cada tabela tratada como caso especial, tornaria a importação lenta e o resultado difícil de consultar.

Volume
212M+ linhas
Tempo
< 36 minutos
Causa
Contrato canônico uniforme

Decisão. Aplicar o contrato canônico uniforme a todas as tabelas de destino. A uniformidade permitiu que o pipeline de ETL (em Python) tratasse cada tabela com a mesma lógica genérica de carga, paralelizando e indexando de forma consistente.

Resultado. A importação completa rodou em menos de 36 minutos. A escala não veio de força bruta, mas da disciplina estrutural: tabelas uniformes permitem código de carga uniforme, e código uniforme escala de forma previsível. O contrato canônico foi o que tornou a façanha possível.

Incidente Real

Severidade · Média

A tabela rebelde que quebrou o código genérico

  • Antes Uma tabela foi criada sem seguir o contrato canônico — usava um nome diferente para a coluna de exclusão, por hábito de outro projeto.
  • Sintoma O código genérico que oculta registros excluídos falhou para essa tabela: registros que deveriam estar invisíveis apareciam, pois a coluna esperada não existia.
  • Diagnóstico A tabela fora da convenção quebrou a premissa de uniformidade da qual o código genérico depende.
  • Correção A tabela foi alinhada ao contrato canônico, com a coluna padrão deleted_at. O código genérico voltou a funcionar sem exceção.
  • Kaizen Vira regra com teste: toda tabela é verificada contra o contrato canônico no CI; nenhuma escapa.

Decisão Arquitetural

ADR-0019

Toda tabela herda um contrato estrutural canônico

Aceito
Contexto

Uma plataforma com centenas de tabelas modeladas de formas diferentes força código específico por tabela para operações genéricas (ocultar excluídos, filtrar por empresa), minando a ideia de um núcleo que serve a tudo.

Decisão

Toda tabela de negócio herda o mesmo contrato: id, tenant_id, created_at, updated_at, deleted_at. A uniformidade permite que um único código genérico trate qualquer tabela. O contrato é verificado no CI.

Alternativas consideradas

Cada tabela à sua maneira — código específico por tabela. Contrato parcial (só algumas colunas) — operações genéricas ainda esbarram em exceções. Herança via ORM mágico — esconde o contrato e dificulta o SQL direto.

Consequências
PositivasCódigo genérico trata qualquer tabela. Escala previsível (212M linhas). Engines uniformes (auditoria, permissão, soft delete).
NegativasExige disciplina para que toda tabela siga o contrato, verificada por teste.
RiscosTabela fora do padrão quebra o genérico; mitigada pela verificação automática no CI.

Antipadrões

Tabela fora do contrato canônico

Uma tabela com colunas de controle próprias quebra o código genérico. Correção: toda tabela herda o mesmo contrato, verificado no CI.

Convenção sem verificação

Confiar que todos seguirão o padrão por disciplina. Correção: teste que varre todas as tabelas e falha se alguma escapar.

Exercício

Audite o contrato das suas tabelas

  1. Liste as tabelas do seu sistema. Elas compartilham as mesmas colunas de controle (identidade, empresa/dono, timestamps, exclusão)? Ou cada uma tem seu jeito?
  2. Para uma operação genérica — "ocultar registros excluídos" —, você consegue um só código que sirva a todas? Ou precisa de exceções por tabela?
  3. Defina o contrato canônico do seu sistema: qual o conjunto mínimo de colunas que toda tabela deveria herdar?
  4. Esboce um teste que verifique o contrato em todas as tabelas, falhando se alguma não o seguir.
Meta: estabelecer (e verificar) um contrato estrutural uniforme que permita ao seu sistema tratar todas as tabelas com um só código genérico.
✓ Checklist do tópico
  • Toda tabela herda o mesmo contrato estrutural canônico
  • O contrato inclui identidade, empresa, timestamps e marca de exclusão
  • Um código genérico trata qualquer tabela (filtrar, ocultar, ordenar)
  • As colunas de controle vêm antes dos campos de negócio
  • O contrato é verificado automaticamente em todas as tabelas
Meta Dados · Engenharia de Plataformas · Capítulo IV · §4.1Próximo → 4.2 Isolamento Multiempresa
Capítulo IV · Dados, Bancos e Modelagem em Escala
4.2

Isolamento Multiempresa

Numa plataforma multiempresa, o pior incidente imaginável é uma empresa ver os dados de outra. O isolamento não pode depender de o desenvolvedor lembrar de filtrar — precisa ser garantido por construção, em camadas.

Pré-requisitos · 4.1 Contrato canônicoConecta com · Cap VII Segurança · Cap I CapacidadesTempo de leitura · ~14 min

O Problema

Numa plataforma que serve muitas empresas sobre a mesma base de dados, existe um incidente que supera todos os outros em gravidade: uma empresa enxergar os dados de outra. Não é um bug comum — é uma quebra de confiança, de contrato comercial e, frequentemente, da legislação de proteção de dados. Uma única consulta que esqueça de filtrar pela empresa certa pode expor informações sigilosas de um cliente a outro.

O perigo é que, na abordagem ingênua, o isolamento depende inteiramente de o desenvolvedor lembrar de adicionar a cláusula de filtro por empresa em toda consulta. Centenas de consultas, escritas por pessoas diferentes ao longo de anos — basta uma esquecer o filtro para abrir o vazamento. Confiar a segurança multiempresa à memória humana, consulta por consulta, é construir sobre areia. O isolamento precisa ser garantido por algo mais forte que a disciplina.

O vazamento entre empresas

Uma consulta sem o filtro de empresa pode expor dados de um cliente a outro — a falha mais grave de uma plataforma multiempresa. Correção: isolamento em camadas, garantido pelo banco, não só pela aplicação.

A Decisão

O isolamento multiempresa é garantido em camadas: a aplicação injeta o tenant, o banco força o filtro (Row-Level Security), e um teste prova que um tenant jamais vê o outro.

A separação entre empresas usa defesa em profundidade — três travas independentes. A primeira, na aplicação: todo acesso a dados injeta automaticamente o tenant_id do contexto, sem depender de o desenvolvedor lembrar. A segunda, e mais importante, no banco: o Row-Level Security (RLS) do PostgreSQL força o filtro por empresa no nível do próprio banco — mesmo que uma consulta esqueça a cláusula, o banco recusa devolver linhas de outra empresa. A terceira, no teste: provas automatizadas tentam ativamente acessar dados de outra empresa e verificam que isso é impossível. Nenhuma trava sozinha é suficiente; juntas, tornam o vazamento improvável até diante de um erro humano.

Arquitetura lógica As três travas do isolamento por tenant
1ª trava · APLICAÇÃO injeta o tenant em toda consulta 2ª trava · BANCO força o filtro (Row-Level Security) 3ª trava · TESTE prova que um tenant nunca vê o outro Empresa Avê só os seus dados Empresa Bisolada de A, sempre
Figura 4.2-a. Defesa em profundidade: a aplicação injeta, o banco força (RLS), o teste prova. Mesmo que a primeira camada falhe por esquecimento, a segunda impede o vazamento.

A trava do banco: Row-Level Security

rls_tenant.sqlPostgreSQL
-- A 2ª trava: o BANCO força o isolamento, não só a aplicação.
ALTER TABLE cliente ENABLE ROW LEVEL SECURITY;

-- Política: só enxerga linhas da empresa do contexto atual.
CREATE POLICY isolamento_tenant ON cliente
    USING (tenant_id = current_setting('app.tenant_id')::BIGINT);

-- Mesmo um SELECT sem WHERE tenant_id devolve APENAS a empresa atual.
-- Esquecer o filtro na aplicação NÃO vaza dados: o banco recusa.
Esta é a trava decisiva: o filtro vive no banco. Uma consulta que esqueça o WHERE da empresa ainda assim não vê outra — o RLS impede.
ContextoDeTenant.phpPHP
// A 1ª trava: a aplicação fixa o tenant do contexto a cada requisição.
final class ContextoDeTenant
{
    public function ativar(int $tenantId, Conexao $conexao): void
    {
        // Informa ao banco qual empresa é a do contexto (alimenta o RLS).
        $conexao->executar(
            "SET app.tenant_id = :t", ['t' => $tenantId],
        );
        // A partir daqui, TODA consulta nesta conexão é isolada pelo banco.
    }
}
A aplicação só precisa dizer ao banco qual é a empresa atual. O RLS faz o resto, em toda consulta, automaticamente.

A Verificação

IsolamentoTenantTest.phpPHP
final class IsolamentoTenantTest extends TestCase
{
    public function test_tenant_nao_ve_dados_de_outro(): void
    {
        // Empresa A cria um cliente.
        $this->comoTenant(1)->criarCliente('Cliente da Empresa A');

        // Empresa B tenta listar — e NÃO pode ver o cliente de A.
        $clientesDeB = $this->comoTenant(2)->listarClientes();

        $this->assertEmpty($clientesDeB, 'VAZAMENTO: B viu dados de A!');
    }

    public function test_consulta_sem_filtro_ainda_isola(): void
    {
        // Mesmo uma consulta "esquecida" (sem WHERE tenant) deve isolar.
        $this->comoTenant(1)->criarCliente('A');
        $todos = $this->comoTenant(2)->queryBruta('SELECT * FROM cliente');
        $this->assertEmpty($todos, 'RLS falhou: filtro do banco não isolou');
    }
}
O segundo teste é o mais importante: ele simula o erro humano (consulta sem filtro) e prova que o banco ainda isola. A trava decisiva é testada.
Tabela de decisão Onde o isolamento é garantido
CamadaO que fazResiste a...
AplicaçãoInjeta o tenant do contextoUso normal, mas não a esquecimento
Banco (RLS)Força o filtro por empresaConsulta sem filtro, erro humano
TesteTenta vazar e prova que falhaRegressões futuras
Só a aplicação (ingênuo)Depende de lembrar o WHERENada — uma falha vaza

Estudo de Caso

Caso aplicado

O isolamento que sobrevive ao erro humano

Contexto. A plataforma serve múltiplas empresas sobre a mesma base. Confiar o isolamento apenas à disciplina de filtrar em cada consulta era um risco inaceitável: bastaria uma consulta esquecida, entre centenas, para vazar dados entre clientes.

Só aplicação
Um esquecimento vaza tudo
RLS no banco
Banco isola, sempre
Ganho
Isolamento à prova de erro humano

Decisão. Implementar a defesa em camadas, com o Row-Level Security do PostgreSQL como trava central. A aplicação informa a empresa do contexto; o banco força o filtro em toda consulta; testes provam que o vazamento é impossível mesmo sem a cláusula na aplicação.

Resultado. O isolamento deixou de depender de memória humana. Uma consulta que, por engano, esqueça o filtro por empresa simplesmente não vê dados alheios — o banco recusa. A falha mais grave de uma plataforma multiempresa passou a ser barrada por construção, não por vigilância.

Incidente Real

Severidade · Crítica

A consulta esquecida que poderia ter vazado

  • Cenário Numa fase inicial sem RLS, uma nova consulta foi escrita sem a cláusula de filtro por empresa — um esquecimento entre muitas consultas.
  • Risco Sem a trava do banco, essa única consulta poderia ter exposto dados de uma empresa a outra: o vazamento multiempresa, o pior dos incidentes.
  • Diagnóstico O isolamento dependia só da aplicação — da memória de cada desenvolvedor de filtrar em toda consulta. Frágil por natureza.
  • Correção Ativação do Row-Level Security no banco como trava central. A partir daí, esquecer o filtro na aplicação deixou de poder vazar dados.
  • Kaizen Vira regra: toda tabela multiempresa tem RLS ativo, e um teste prova que uma consulta sem filtro ainda isola. A segurança não depende de lembrar.

Decisão Arquitetural

ADR-0020

Isolamento multiempresa garantido em camadas, com RLS no banco

Aceito
Contexto

O vazamento de dados entre empresas é a falha mais grave de uma plataforma multiempresa. Depender de o desenvolvedor lembrar de filtrar por empresa em toda consulta é frágil: um esquecimento, entre centenas, vaza dados.

Decisão

O isolamento usa defesa em profundidade: a aplicação injeta o tenant do contexto; o Row-Level Security do PostgreSQL força o filtro no banco (resistindo a consultas sem cláusula); e testes provam que um tenant nunca vê outro, inclusive simulando o erro humano.

Alternativas consideradas

Filtrar só na aplicação — um esquecimento vaza. Banco por empresa — isola, mas custa e complica a escala de muitas empresas. Confiar na revisão de código — humanos erram; não é garantia.

Consequências
PositivasVazamento barrado por construção. Isolamento resiste a erro humano. Confiança e conformidade preservadas. Escala com base única.
NegativasExige configurar RLS e propagar o tenant do contexto ao banco em cada conexão.
RiscosRLS mal configurado falha aberto; mitigado por testes que simulam o vazamento e provam o isolamento.

Antipadrões

Isolamento só na aplicação

Confiar em lembrar o filtro por empresa em toda consulta. Um esquecimento vaza dados. Correção: RLS no banco força o isolamento, independente da aplicação.

Isolamento sem teste de vazamento

Assumir que o filtro funciona sem prová-lo. Correção: teste que tenta ativamente ver dados de outra empresa e exige que falhe.

Exercício

Teste o isolamento do seu sistema

  1. Se o seu sistema é multiempresa, onde o isolamento é garantido? Só na aplicação (no código de cada consulta), ou também no banco?
  2. Faça o teste mental do esquecimento: se uma consulta sua esquecesse o filtro por empresa, ela vazaria dados? Se sim, o isolamento é frágil.
  3. Investigue se o seu banco oferece um mecanismo de isolamento por linha (como o RLS do PostgreSQL) que force o filtro independentemente da aplicação.
  4. Escreva o teste de vazamento: crie um dado como empresa A, tente lê-lo como empresa B (inclusive com consulta bruta), e exija que retorne vazio.
Meta: verificar se o isolamento multiempresa do seu sistema resiste ao erro humano — e, se não, planejar a trava no banco que o tornaria garantido por construção.
✓ Checklist do tópico
  • O isolamento multiempresa usa defesa em camadas, não uma só
  • O banco força o filtro por empresa (ex.: Row-Level Security)
  • Uma consulta que esqueça o filtro ainda assim não vaza dados
  • A aplicação injeta o tenant do contexto automaticamente
  • Um teste tenta ver dados de outra empresa e prova que é impossível
Meta Dados · Engenharia de Plataformas · Capítulo IV · §4.2Próximo → 4.3 Soft Delete e Histórico
Capítulo IV · Dados, Bancos e Modelagem em Escala
4.3

Soft Delete e Histórico

Em uma plataforma de negócio, apagar dados de verdade é quase sempre um erro. O cliente "excluído" precisa voltar; a alteração "perdida" precisa ser auditada. Nada se perde — tudo se marca e se rastreia.

Pré-requisitos · 4.1 Contrato canônicoConecta com · 4.4 Event Store · Cap VII AuditoriaTempo de leitura · ~13 min

O Problema

Apagar dados de verdade — o DELETE físico que remove a linha do banco para sempre — é, numa plataforma de negócio, quase sempre um erro disfarçado de operação rotineira. O cliente que foi "excluído" por engano precisa voltar. A operação que "sumiu" precisa ser auditada por uma questão fiscal. A alteração que alguém fez ontem precisa ser comparada com a de hoje. Dado apagado fisicamente não volta, não se audita, não se compara — ele simplesmente deixou de existir, levando consigo qualquer resposta sobre o passado.

E não é só sobre exclusão. O próprio estado atual de um registro conta apenas metade da história. Saber que um limite de crédito é hoje um certo valor não diz quem o alterou, quando, nem qual era antes. Em um sistema de negócio, o histórico das mudanças é tão importante quanto o estado presente — para auditoria, para resolver disputas, para entender como se chegou aqui. Um sistema que só guarda o agora é um sistema com amnésia.

O DELETE que apaga a resposta

Um DELETE físico remove o dado e, com ele, qualquer possibilidade de recuperá-lo ou auditá-lo. Em negócio, isso quase sempre é um erro. Correção: soft delete (marcar, não apagar) e histórico de mudanças.

A Decisão

Nada é apagado fisicamente: a exclusão marca deleted_at (soft delete). E cada mudança é registrada num histórico — quem alterou o quê, quando. O passado nunca é perdido.

Duas decisões preservam a memória do sistema. O soft delete: excluir um registro não o remove do banco; apenas preenche a coluna deleted_at do contrato canônico (§4.1). O dado desaparece das telas e das consultas normais (que filtram por deleted_at IS NULL), mas continua existindo — recuperável com um gesto, auditável a qualquer momento. O histórico de mudanças: cada alteração relevante de um registro é registrada — o valor anterior, o novo, quem mudou, quando. Assim, o sistema guarda não só o estado atual, mas a trilha que levou a ele. Juntas, essas decisões garantem que nada se perca: nem o que foi excluído, nem o que foi alterado.

Diagrama conceitual Soft delete e histórico de mudanças
DELETE físicodado some — irreversível SOFT deletemarca deleted_at — recuperável deleted_at IS NULL → vivo · preenchido → excluído HISTÓRICO de mudanças · cada alteração registradaquem mudou o quê, quando — nada se perde
Figura 4.3-a. O soft delete preserva o que foi excluído; o histórico preserva o que foi alterado. O sistema lembra tanto do estado atual quanto do caminho até ele.

Excluir sem apagar; consultar só os vivos

soft_delete.sqlPostgreSQL
-- "Excluir" é marcar a data, não remover a linha.
UPDATE cliente SET deleted_at = now()
WHERE id = 42 AND deleted_at IS NULL;

-- O dado continua no banco. Pode voltar a qualquer momento:
UPDATE cliente SET deleted_at = NULL WHERE id = 42;  -- "ressuscita"

-- As consultas normais veem apenas os vivos (e o índice parcial os serve).
SELECT * FROM cliente
WHERE tenant_id = 7 AND deleted_at IS NULL;
Excluir e recuperar são apenas mudanças na coluna deleted_at. O dado nunca sai do banco; só muda de estado.
historico_mudancas.sqlPostgreSQL
-- O histórico registra CADA mudança relevante de um registro.
CREATE TABLE historico_alteracao (
    id           BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    tenant_id    BIGINT      NOT NULL,
    tabela       TEXT        NOT NULL,        -- 'cliente'
    registro_id  BIGINT      NOT NULL,        -- 42
    campo        TEXT        NOT NULL,        -- 'limite_credito'
    valor_antigo TEXT,                            -- '1000'
    valor_novo   TEXT,                            -- '5000'
    autor_id     BIGINT      NOT NULL,        -- quem mudou
    em           TIMESTAMPTZ NOT NULL DEFAULT now()  -- quando
);
-- Estado atual vive em 'cliente'; a TRILHA até ele vive aqui.
O histórico guarda o antes, o depois, o autor e o momento de cada mudança — a memória que o estado atual sozinho não tem.

A Verificação

SoftDeleteTest.phpPHP
final class SoftDeleteTest extends TestCase
{
    public function test_excluido_some_das_consultas_mas_existe(): void
    {
        $id = $this->repo->criar('Cliente');
        $this->repo->excluir($id);  // soft delete

        // Some das consultas normais...
        $this->assertNull($this->repo->buscarVivo($id));
        // ...mas continua existindo no banco.
        $this->assertNotNull($this->repo->buscarIncluindoExcluidos($id));
    }

    public function test_pode_ser_recuperado(): void
    {
        $id = $this->repo->criar('Cliente');
        $this->repo->excluir($id);
        $this->repo->recuperar($id);  // volta à vida

        $this->assertNotNull($this->repo->buscarVivo($id));
    }
}
Os testes provam as duas garantias do soft delete: o excluído some das telas mas persiste, e pode ser recuperado intacto.
Tabela de decisão DELETE físico vs soft delete
SituaçãoDELETE físicoSoft delete
Exclusão por enganoDado perdidoRecuperável
Auditoria do excluídoImpossívelDado ainda existe
Some das telasSimSim (filtro deleted_at)
Histórico de mudançasSem trilhaQuem, o quê, quando
Conformidade fiscal/legalRiscoRastreável

Estudo de Caso

Caso aplicado

O cadastro excluído que precisou voltar

Contexto. Um cadastro importante foi excluído — seja por engano operacional, seja por uma decisão depois revertida. Num sistema com exclusão física, esse dado e todo o seu histórico simplesmente deixariam de existir, e a recuperação exigiria restaurar backups, com perda de tudo o que mudou desde então.

DELETE físico
Restaurar backup, perder dados
Soft delete
Limpar a marca, tudo volta
Ganho
Recuperação trivial e íntegra

Decisão. Com o soft delete em vigor, a exclusão apenas marcou a data; o registro permaneceu no banco, invisível nas telas, mas íntegro. A recuperação foi limpar a marca de exclusão — um gesto, não uma operação de desastre.

Resultado. O cadastro voltou instantaneamente, com todo o seu histórico de mudanças preservado. O que num sistema de exclusão física seria um incidente grave (restaurar backup, perder dados recentes) virou uma operação trivial e sem perdas. Nada se perdeu porque nada havia sido apagado.

Incidente Real

Severidade · Alta

A alteração sem rastro que virou disputa

  • Antes Um campo crítico de um registro — um valor de limite, por exemplo — foi alterado. O sistema guardava apenas o estado atual, sem histórico.
  • Sintoma Surgiu uma disputa sobre o valor anterior e quem o havia mudado. Sem histórico, não havia como responder: o sistema só sabia o valor de agora.
  • Diagnóstico A ausência de histórico de mudanças deixou o sistema com amnésia: o estado atual não contava como se chegou a ele.
  • Correção Implementação do histórico de alterações: cada mudança passa a registrar valor antigo, novo, autor e momento. A trilha do dado ficou completa.
  • Kaizen Vira regra: campos sensíveis têm histórico de mudanças; o sistema guarda a trilha, não só o estado.

Decisão Arquitetural

ADR-0021

Soft delete e histórico de mudanças: nada se perde

Aceito
Contexto

O DELETE físico é irreversível e quase sempre um erro em negócio: o excluído precisa voltar ou ser auditado. E o estado atual sozinho não conta quem mudou o quê, quando — o sistema fica com amnésia.

Decisão

Nada é apagado fisicamente: a exclusão marca deleted_at (soft delete), e as consultas normais filtram por deleted_at IS NULL. Cada mudança relevante é registrada num histórico (valor antigo, novo, autor, momento). O passado é sempre preservado.

Alternativas consideradas

DELETE físico — perde o dado e a auditoria. Soft delete sem histórico — preserva o excluído mas não a trilha de mudanças. Histórico só em log de texto — não estruturado, difícil de consultar.

Consequências
PositivasExcluído é recuperável e auditável. Trilha completa de mudanças. Conformidade e resolução de disputas. Memória preservada.
NegativasExige filtrar deleted_at em toda consulta (resolvido pelo código genérico, §4.1) e manter o histórico.
RiscosDados crescem ao não apagar; mitigado por retenção e arquivamento conscientes (não por exclusão silenciosa).

Antipadrões

DELETE físico de dados de negócio

Remover a linha para sempre, sem recuperação nem auditoria. Correção: soft delete — marcar deleted_at, manter o dado.

Guardar só o estado atual

Sistema com amnésia: sabe o valor de agora, não como se chegou a ele. Correção: histórico de mudanças com antes, depois, autor e momento.

Exercício

Avalie a memória do seu sistema

  1. Quando algo é "excluído" no seu sistema, o dado some de verdade do banco, ou apenas fica marcado como excluído? Se some, como você o recuperaria?
  2. Faça o teste do arrependimento: se um registro importante fosse excluído por engano agora, qual seria o esforço para trazê-lo de volta?
  3. Para um campo crítico (um valor, um status), o seu sistema sabe quem o alterou pela última vez e qual era o valor anterior? Ou só conhece o estado atual?
  4. Esboce um histórico de mudanças para um campo sensível: o que você registraria a cada alteração?
Meta: avaliar se o seu sistema preserva o passado — o que foi excluído e o que foi alterado — ou se sofre de amnésia, e planejar as estruturas que dariam memória a ele.
✓ Checklist do tópico
  • A exclusão é lógica (soft delete), não física
  • Registros excluídos somem das consultas normais mas persistem no banco
  • Um registro excluído pode ser recuperado sem restaurar backup
  • Mudanças relevantes são registradas num histórico (antes, depois, autor, momento)
  • O sistema preserva tanto o estado atual quanto a trilha até ele
Meta Dados · Engenharia de Plataformas · Capítulo IV · §4.3Próximo → 4.4 Event Store
Capítulo IV · Dados, Bancos e Modelagem em Escala
4.4

Event Store

Tudo o que acontece de relevante na plataforma é um fato: cliente criado, pedido emitido, documento importado. Se cada consumidor — auditoria, IA, integração — reconstrói esses fatos à sua maneira, surgem várias versões da verdade. O Event Store é a única.

Pré-requisitos · Cap II Eventos · 4.3 HistóricoConecta com · Cap VI Integração · Cap IX IATempo de leitura · ~13 min

O Problema

Tudo o que importa numa plataforma é, em essência, um fato que aconteceu: um cliente foi criado, um pedido foi emitido, um documento fiscal eletrônico foi importado. O Capítulo II estabeleceu como esses fatos são anunciados — os eventos de domínio e o outbox transacional. Mas falta uma peça: onde esses fatos vivem de forma permanente, e quem é o dono da verdade sobre o que aconteceu.

Sem um repositório único de fatos, cada consumidor acaba reconstruindo a história à sua maneira. A auditoria monta sua própria visão do que ocorreu; as integrações inferem os fatos a partir do estado atual; a camada de IA reconstrói uma sequência a partir de outra fonte. O resultado são várias versões da verdade, sutilmente divergentes — a auditoria diz uma coisa, a integração diz outra, e ninguém sabe qual está certa. Quando há mais de uma fonte de verdade sobre os fatos, não há nenhuma.

O sintoma das várias verdades

Quando auditoria, integrações e IA reconstroem o histórico cada uma à sua maneira, surgem versões divergentes do que aconteceu. Correção: um Event Store como fonte única de verdade, do qual todos derivam.

A Decisão

O Event Store é a fonte de verdade dos fatos: um repositório imutável e ordenado de eventos. Auditoria, integrações e IA derivam dele — nenhuma reconstrói a história por conta própria.

O Event Store é um repositório que persiste todos os eventos de domínio (§Cap II) como fatos imutáveis, em ordem. Um evento, uma vez registrado, nunca é alterado nem apagado — ele é o registro definitivo de algo que aconteceu. Esse repositório é a fonte de verdade dos fatos: a auditoria, as integrações e a camada de IA/analytics todas derivam da mesma sequência de eventos, em vez de cada uma reconstruir o histórico de sua própria fonte. Com uma única verdade sobre o que aconteceu, as divergências desaparecem — todos contam a mesma história, porque todos leem o mesmo registro.

Arquitetura lógica O Event Store como fonte de verdade
EVENT STOREos fatos, em ordem, imutáveis Auditoriaderiva dos fatos Integraçõesderivam dos fatos IA / Analyticsderivam dos fatos uma fonte de verdade; nenhum consumidor reconstrói a história à sua maneira
Figura 4.4-a. Os fatos vivem em um só lugar, imutáveis e ordenados. Auditoria, integrações e IA derivam todos da mesma sequência — uma verdade, muitos consumidores.

Persistir o fato de forma imutável

event_store.sqlPostgreSQL
-- Os fatos: imutáveis, ordenados, a fonte de verdade.
CREATE TABLE event_store (
    sequencia    BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    tenant_id    BIGINT      NOT NULL,
    tipo         TEXT        NOT NULL,        -- 'PedidoCriado'
    agregado     TEXT        NOT NULL,        -- 'pedido'
    agregado_id  BIGINT      NOT NULL,        -- 1001
    dados        JSONB       NOT NULL,        -- o fato em si
    ocorrido_em  TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- IMUTÁVEL: sem UPDATE, sem DELETE. Um fato registrado é definitivo.
REVOKE UPDATE, DELETE ON event_store FROM app_role;
A sequência dá ordem; o JSONB guarda o fato; e a revogação de UPDATE/DELETE garante a imutabilidade no próprio banco.
EventStore.phpPHP
// Só duas operações: registrar um fato e ler a sequência de fatos.
final class EventStore
{
    public function registrar(EventoDeDominio $evento): void
    {
        // Acrescenta o fato. Nunca atualiza nem remove — append-only.
        $this->conexao->inserir('event_store', [
            'tenant_id'   => $evento->tenantId(),
            'tipo'        => $evento->tipo(),
            'agregado'    => $evento->agregado(),
            'agregado_id' => $evento->agregadoId(),
            'dados'       => $evento->paraJson(),
        ]);
    }

    /** A sequência de fatos de um agregado — a verdade, em ordem. */
    public function fatosDe(string $agregado, int $id): array
    {
        return $this->conexao->buscar(
            "SELECT * FROM event_store
              WHERE agregado = :a AND agregado_id = :id
              ORDER BY sequencia",
            ['a' => $agregado, 'id' => $id],
        );
    }
}
Registrar e ler em ordem — só isso. A simplicidade do contrato é o que faz o Event Store ser uma fonte de verdade confiável.

A Verificação

EventStoreTest.phpPHP
final class EventStoreTest extends TestCase
{
    public function test_fatos_sao_lidos_na_ordem_em_que_ocorreram(): void
    {
        $this->store->registrar(new PedidoCriado(1001));
        $this->store->registrar(new PedidoAprovado(1001));

        $fatos = $this->store->fatosDe('pedido', 1001);

        // A ordem dos fatos é a verdade da história do agregado.
        $this->assertSame('PedidoCriado', $fatos[0]->tipo);
        $this->assertSame('PedidoAprovado', $fatos[1]->tipo);
    }
}
O teste garante a propriedade essencial: os fatos são lidos na ordem em que ocorreram — a sequência é a verdade.
Tabela de decisão Cada um reconstrói vs Event Store
AspectoCada consumidor reconstróiEvent Store (fonte única)
Versões da verdadeVárias, divergentesUma, definitiva
AuditoriaVisão própriaDeriva dos fatos
IA / analyticsReconstrói de outra fonteLê os mesmos fatos
Um fato registradoPode divergir entre fontesImutável, igual para todos

Estudo de Caso

Caso aplicado

Uma verdade para auditoria, integração e IA

Contexto. A plataforma precisava que auditoria, integrações com sistemas parceiros e a futura camada de IA todas soubessem o que havia acontecido — quais pedidos foram criados, quais documentos importados, em que ordem. O risco era cada uma montar sua própria versão dos fatos.

Sem Event Store
Cada área, sua verdade
Com Event Store
Todos derivam dos fatos
Ganho
Uma verdade, consumidores coerentes

Decisão. Persistir todos os eventos de domínio num Event Store imutável, como fonte única de verdade. Auditoria, integrações e IA passam a derivar da mesma sequência de fatos, em vez de reconstruí-la independentemente.

Resultado. As três áreas contam a mesma história, porque leem o mesmo registro. Uma divergência entre o que a auditoria afirma e o que uma integração processou tornou-se impossível — há uma só verdade sobre os fatos, e todos derivam dela. O Event Store virou a espinha dorsal factual da plataforma.

Incidente Real

Severidade · Média

As duas verdades que não batiam

  • Antes Numa fase sem Event Store, a auditoria reconstruía o histórico de uma fonte, e uma integração inferia os mesmos fatos de outra.
  • Sintoma As duas visões divergiram: a auditoria registrava uma sequência de eventos que não batia com o que a integração havia processado. Qual estava certa?
  • Diagnóstico Sem uma fonte única de verdade dos fatos, cada consumidor construiu uma versão sutilmente diferente da história.
  • Correção Adoção do Event Store como fonte única. Auditoria e integração passaram a derivar da mesma sequência imutável de eventos.
  • Kaizen Vira regra: os fatos vivem no Event Store; nenhum consumidor reconstrói a história de fonte própria — todos derivam da única verdade.

Decisão Arquitetural

ADR-0022

Event Store como fonte de verdade imutável dos fatos

Aceito
Contexto

Os eventos de domínio (Cap II) precisam de persistência permanente. Sem um repositório único, auditoria, integrações e IA reconstroem o histórico cada uma à sua maneira, gerando versões divergentes da verdade.

Decisão

Todos os eventos de domínio são persistidos num Event Store imutável e ordenado (append-only, sem update/delete). Ele é a fonte de verdade dos fatos; auditoria, integrações e IA derivam da mesma sequência, sem reconstruir o histórico por conta própria.

Alternativas consideradas

Cada consumidor reconstrói — versões divergentes. Estado atual como verdade — perde a sequência e o "como se chegou aqui". Log de eventos mutável — não confiável como fonte de verdade.

Consequências
PositivasUma só verdade dos fatos. Consumidores coerentes. Base sólida para auditoria, integração e IA. História preservada em ordem.
NegativasExige persistir eventos de forma imutável e modelar os consumidores como derivações dos fatos.
RiscosO volume de eventos cresce; mitigado por particionamento e por projeções que materializam visões (§4.6).

Antipadrões

Cada consumidor reconstruindo os fatos

Auditoria, integração e IA montam versões próprias da história. Correção: Event Store como fonte única; todos derivam dele.

Event Store mutável

Permitir alterar ou apagar eventos já registrados. Correção: append-only; um fato registrado é imutável e definitivo.

Exercício

Mapeie a verdade dos fatos no seu sistema

  1. No seu sistema, mais de uma área precisa saber "o que aconteceu" (auditoria, integrações, relatórios)? Cada uma deriva da mesma fonte, ou reconstrói o histórico à sua maneira?
  2. Procure uma divergência: dois lugares que deveriam concordar sobre uma sequência de eventos, mas às vezes não concordam. A causa provável é a falta de uma fonte única.
  3. Liste os fatos relevantes do seu domínio (X criado, Y aprovado, Z importado). Eles são registrados em algum lugar imutável e ordenado?
  4. Esboce um Event Store: que fatos guardaria, e quais consumidores passariam a derivar dele em vez de reconstruir a história?
Meta: identificar onde seu sistema corre o risco de ter várias versões da verdade sobre os fatos — e planejar um Event Store que sirva de fonte única.
✓ Checklist do tópico
  • Os fatos relevantes do domínio são persistidos como eventos
  • O Event Store é imutável (append-only) e ordenado
  • Ele é a fonte única de verdade sobre o que aconteceu
  • Auditoria, integrações e IA derivam dos mesmos fatos
  • Nenhum consumidor reconstrói o histórico de fonte própria
Meta Dados · Engenharia de Plataformas · Capítulo IV · §4.4Próximo → 4.5 O Catálogo de Tabelas-Base
Capítulo IV · Dados, Bancos e Modelagem em Escala
4.5

O Catálogo de Tabelas-Base

Uma plataforma madura tem cento e tantas tabelas. Sem um mapa que diga o que cada uma faz e a qual família pertence, o desenvolvedor não sabe se a tabela de que precisa já existe — e cria uma duplicada.

Pré-requisitos · 4.1–4.4Conecta com · Cap II Engines · Cap III MetadadosTempo de leitura · ~11 min

O Problema

Uma plataforma madura acumula tabelas — facilmente cento e cinquenta. Elas nascem em momentos diferentes, descritas em documentos diferentes, por pessoas diferentes. Sem um mapa que as organize, esse conjunto vira um território desconhecido: o desenvolvedor que precisa de uma tabela para guardar certo tipo de dado não tem como saber se ela já existe, em que família, com que nome. Na dúvida, ele cria uma nova.

E a tabela duplicada é o início de uma incoerência cara. Agora há dois lugares guardando a mesma coisa, que divergem com o tempo: um é atualizado, o outro não; uma engine usa um, outra usa o outro. O que era para ser uma plataforma coesa fragmenta-se em ilhas de dados redundantes e contraditórios. O problema não é técnico — é de descoberta: sem saber o que existe, reinventa-se o que já estava lá.

O sintoma da falta de mapa

Quando o desenvolvedor cria uma tabela nova porque não sabia que uma equivalente já existia, o catálogo está faltando. Correção: um catálogo único que agrupa as tabelas em famílias, com a responsabilidade de cada uma.

A Decisão

Existe um catálogo único das tabelas-base, agrupadas em famílias coerentes, cada uma com sua responsabilidade declarada. É a primeira parada antes de criar qualquer tabela.

O catálogo de tabelas-base é o mapa do território de dados. Ele agrupa as ~100-150 tabelas em famílias coerentes por responsabilidade: identidade (usuários, empresas, papéis), metamodelo (a descrição declarativa do Cap III), regras (o Business Rules Engine), fatos (o Event Store, o histórico), negócio (as tabelas específicas do domínio) e operação (jobs, integrações, observabilidade). Cada tabela tem, no catálogo, sua responsabilidade declarada — o que ela guarda e por quê. Antes de criar qualquer tabela, consulta-se o catálogo: a que você precisa talvez já exista, numa família que você não havia pensado em olhar. O catálogo transforma um amontoado em um mapa navegável.

Diagrama conceitual O catálogo em famílias coerentes
~100-150 TABELAS · AGRUPADAS EM FAMÍLIAS IDENTIDADEcore_users, core_companiespapéis, permissões METAMODELOmeta_entidade, meta_campometa_form, meta_menu REGRASbre_rule, bre_conditionbre_action FATOSevent_storehistorico_alteracao NEGÓCIOcliente, pedido, produto(específicas do domínio) OPERAÇÃOjobs, integraçõesobservabilidade o mapa que evita criar tabela duplicada
Figura 4.5-a. As tabelas agrupadas por responsabilidade. O mapa permite ao desenvolvedor encontrar o que precisa — e perceber quando o que precisa já existe.

O catálogo como documento navegável

catalogo.sqlPostgreSQL
-- O catálogo é, ele próprio, dado: cada tabela e sua família.
CREATE TABLE catalogo_tabela (
    nome           TEXT PRIMARY KEY,        -- 'core_users'
    familia        TEXT NOT NULL,           -- 'identidade'
    responsabilidade TEXT NOT NULL          -- 'usuários do sistema'
);

-- Antes de criar uma tabela, pergunta-se ao catálogo:
-- "já existe algo nesta família que guarda este dado?"
SELECT nome, responsabilidade FROM catalogo_tabela
WHERE familia = 'identidade';
O catálogo é dado consultável: agrupa por família e declara a responsabilidade. Antes de criar, pergunta-se a ele.
GuardaDeDuplicacao.phpPHP
// O catálogo pode até barrar a criação de tabela duplicada.
final class VerificadorDeCatalogo
{
    public function avisarSeSimilarExiste(string $familia, string $proposito): array
    {
        // Busca tabelas da mesma família com propósito parecido.
        $candidatas = $this->catalogo->daFamilia($familia);

        // Retorna as que talvez já atendam — evita a reinvenção.
        return array_filter(
            $candidatas,
            fn($t) => $this->pareceAtender($t, $proposito),
        );
    }
}
Mais que documentação: o catálogo pode ativamente avisar "já existe algo assim" antes que uma duplicata nasça.
Tabela de decisão As famílias do catálogo
FamíliaGuardaExemplos
IdentidadeQuem acessa e suas permissõescore_users, core_companies, papéis
MetamodeloA descrição declarativa (Cap III)meta_entidade, meta_form, meta_menu
RegrasAs regras de negócio (Cap III)bre_rule, bre_condition, bre_action
FatosO que aconteceu (§4.4)event_store, historico_alteracao
NegócioAs entidades do domíniocliente, pedido, produto
OperaçãoJobs, integrações, observabilidadefilas, integrações, métricas

Estudo de Caso

Caso aplicado

A tabela que não precisou ser criada

Contexto. Um desenvolvedor precisava guardar um certo tipo de informação de controle. Sem um mapa das tabelas existentes, o caminho natural seria criar uma tabela nova — e, com ela, mais uma fonte de dados a manter e sincronizar.

Sem catálogo
Cria tabela duplicada
Com catálogo
Descobre que já existe
Ganho
Reúso, sem ilha redundante

Decisão. Antes de criar, consultar o catálogo de tabelas-base, filtrando pela família correspondente à natureza do dado. O catálogo revelou que já existia uma tabela com exatamente aquela responsabilidade, numa família que o desenvolvedor não havia pensado em olhar.

Resultado. Nenhuma tabela nova foi criada; a existente foi reutilizada. A plataforma evitou mais uma ilha de dados redundante, e o desenvolvedor economizou o trabalho de modelar, criar e manter algo que já existia. O catálogo cumpriu seu papel: evitar a reinvenção pela falta de descoberta.

Incidente Real

Severidade · Baixa

As duas tabelas para a mesma coisa

  • Antes Sem um catálogo, dois desenvolvedores, em momentos diferentes, criaram tabelas distintas para guardar essencialmente o mesmo tipo de dado.
  • Sintoma As duas tabelas divergiram: uma era atualizada por uma parte do sistema, a outra por outra. Relatórios baseados em fontes diferentes passaram a discordar.
  • Diagnóstico A ausência de um mapa das tabelas levou à duplicação por desconhecimento — ninguém sabia que a outra tabela já existia.
  • Correção Consolidação das duas numa só, e criação do catálogo de tabelas-base como mapa único e ponto de consulta obrigatório antes de criar tabelas.
  • Kaizen Vira regra: antes de criar uma tabela, consulta-se o catálogo; tabelas novas entram no catálogo com sua família e responsabilidade.

Decisão Arquitetural

ADR-0023

Catálogo único de tabelas-base, agrupadas em famílias

Aceito
Contexto

Uma plataforma com ~100-150 tabelas, descritas em documentos dispersos, deixa o desenvolvedor sem saber se a tabela de que precisa já existe — e ele cria duplicatas, gerando ilhas de dados redundantes e divergentes.

Decisão

Existe um catálogo único das tabelas-base, agrupadas em famílias coerentes (identidade, metamodelo, regras, fatos, negócio, operação), cada uma com sua responsabilidade declarada. É a primeira parada antes de criar qualquer tabela; tabelas novas são registradas nele.

Alternativas consideradas

Documentação dispersa — ninguém acha a tabela e duplica. Sem catálogo — o estado que causa a duplicação. Catálogo que ninguém atualiza — desatualiza e perde a confiança.

Consequências
PositivasO desenvolvedor descobre o que existe. Duplicação evitada. Plataforma coesa, sem ilhas redundantes. Onboarding mais rápido.
NegativasExige manter o catálogo atualizado conforme tabelas nascem e mudam de responsabilidade.
RiscosCatálogo desatualizado engana; mitigado por torná-lo parte do fluxo de criação de tabelas e verificá-lo.

Antipadrões

Criar tabela sem consultar o catálogo

Modelar uma nova tabela sem verificar se uma equivalente já existe. Correção: o catálogo é a primeira parada antes de criar.

Catálogo desatualizado

Um mapa que não reflete as tabelas reais engana mais do que ajuda. Correção: tabelas novas entram no catálogo; ele é parte do fluxo de criação.

Exercício

Esboce o catálogo do seu sistema

  1. Quantas tabelas o seu sistema tem? Você consegue listar todas e dizer a responsabilidade de cada uma, ou há tabelas cujo propósito ninguém lembra?
  2. Agrupe suas tabelas em famílias por responsabilidade (identidade, configuração, fatos, negócio, operação). Alguma tabela não se encaixa em nenhuma — ou se encaixa em duas?
  3. Procure duplicatas: há duas tabelas guardando essencialmente a mesma coisa? Como elas divergiram?
  4. Defina o ritual: antes de criar uma tabela nova, o que alguém deveria consultar para ter certeza de que ela não existe?
Meta: criar um mapa das tabelas do seu sistema, agrupadas por responsabilidade — para que ninguém mais reinvente uma tabela que já existe.
✓ Checklist do tópico
  • Existe um catálogo único das tabelas-base do sistema
  • As tabelas são agrupadas em famílias coerentes por responsabilidade
  • Cada tabela tem sua responsabilidade declarada no catálogo
  • O catálogo é consultado antes de criar qualquer tabela
  • Tabelas novas são registradas no catálogo com família e responsabilidade
Meta Dados · Engenharia de Plataformas · Capítulo IV · §4.5Próximo → 4.6 OLTP e OLAP
Capítulo IV · Dados, Bancos e Modelagem em Escala
4.6

OLTP e OLAP

Operação e análise têm necessidades opostas. Uma consulta de BI que varre milhões de linhas, rodando no banco operacional, trava as transações do dia a dia. A solução é não deixá-las brigar pela mesma máquina.

Pré-requisitos · 4.1–4.5Conecta com · Cap VIII Performance · Cap XI AnalyticsTempo de leitura · ~13 min

O Problema

Operação e análise são atividades de natureza oposta, e querem coisas opostas do banco de dados. A operação (OLTP — processamento transacional) é feita de muitas escritas e leituras pequenas e rápidas: criar um pedido, atualizar um cadastro, buscar um cliente. Ela vive de latência baixa. A análise (OLAP — processamento analítico) é o oposto: poucas consultas, mas que varrem milhões de linhas para somar, agrupar e cruzar — um dashboard de BI, um relatório com drill down. Ela vive de vazão.

O perigo é concreto e comum: deixar a análise pesada rodar no mesmo banco da operação. Uma única consulta de BI que varre milhões de linhas para montar um painel consome a máquina inteira — e, enquanto roda, as transações do dia a dia travam. O cliente que tentava salvar um pedido espera; a operação inteira engasga porque alguém pediu um relatório. Quando os clientes começarem a querer BI pesado, a operação será a vítima. Operação e análise, na mesma máquina, são inimigas.

A consulta de BI que trava a operação

Uma varredura analítica de milhões de linhas no banco operacional consome a máquina e congela as transações do dia a dia. Correção: separar OLTP (operação) de OLAP (análise) — cada um na sua máquina.

A Decisão

Operação e análise são separadas: PostgreSQL serve o OLTP (transações), ClickHouse ou DuckDB servem o OLAP (análise). A análise mais pesada nunca compete com a operação pela mesma máquina.

A plataforma separa fisicamente os dois mundos. O OLTP — a operação transacional — vive no PostgreSQL, otimizado para muitas transações pequenas e rápidas, onde a latência baixa é tudo. O OLAP — a análise — vive num banco colunar, ClickHouse ou DuckDB, projetado para varrer e agregar enormes volumes com eficiência que um banco operacional jamais teria. Os fatos fluem do OLTP para o OLAP (alimentados pelo Event Store, §4.4), mas as duas cargas nunca competem pela mesma máquina. O relatório mais brutal, a consulta de BI mais pesada, rodam no banco analítico — e a operação do dia a dia segue intocada, porque vive em outro lugar.

Arquitetura física A separação entre operação e análise
OLTP · operação PostgreSQL · transações do dia a dia muitas escritas pequenas e rápidas latência baixa é tudo OLAP · análise ClickHouse / DuckDB · BI pesado varreduras de milhões de linhas vazão importa mais que latência ✕ SEPARADOS · cada um na sua máquinaa análise pesada nunca trava a operação os fatos fluem do OLTP para o OLAP — sem competir pela mesma máquina
Figura 4.6-a. Necessidades opostas, bancos opostos. O PostgreSQL serve a operação; ClickHouse/DuckDB servem a análise. Separados, a carga de um nunca afeta o outro.

Cada carga no banco que a serve

oltp_postgres.sqlPostgreSQL
-- OLTP: a operação. Transações pequenas, rápidas, latência baixa.
-- PostgreSQL brilha aqui: ACID, índices, escritas concorrentes.
BEGIN;
  INSERT INTO pedido (tenant_id, cliente_id, valor)
  VALUES (7, 42, 1500.00);

  UPDATE cliente SET updated_at = now()
  WHERE id = 42 AND tenant_id = 7;
COMMIT;
-- Milissegundos. É o que o dia a dia precisa.
A operação no PostgreSQL: transações curtas e atômicas, no ritmo de milissegundos que o dia a dia exige.
olap_colunar.sqlClickHouse / DuckDB
-- OLAP: a análise. Varre milhões de linhas, agrega, cruza.
-- Banco COLUNAR (ClickHouse/DuckDB): feito para isto.
SELECT
    toStartOfMonth(ocorrido_em) AS mes,
    count()                     AS pedidos,
    sum(valor)                  AS faturamento,
    avg(valor)                  AS ticket_medio
FROM fatos_pedido             -- milhões de linhas
WHERE tenant_id = 7
GROUP BY mes
ORDER BY mes;
-- No banco colunar: segundos. No operacional: travaria tudo.
A mesma agregação que congelaria o banco operacional roda em segundos no colunar — porque ele foi feito para varrer colunas, não linhas.

Os fatos fluem do operacional para o analítico

fluxo_oltp_olap.pyPython
# Os fatos do Event Store (§4.4) alimentam o banco analítico.
def sincronizar_fatos_para_olap(desde_sequencia: int) -> int:
    # Lê os novos fatos do OLTP (append-only, ordenados).
    fatos = event_store.ler_desde(desde_sequencia)

    # Carrega em lote no colunar — sem tocar a operação.
    olap.inserir_em_lote('fatos_pedido', fatos)

    # A análise vive de uma cópia dos fatos, não da máquina operacional.
    return fatos[-1].sequencia if fatos else desde_sequencia
Um pipeline em Python move os fatos do OLTP para o OLAP em lote. A análise consome essa cópia — a operação nunca é tocada.
Tabela de decisão OLTP vs OLAP: necessidades opostas
DimensãoOLTP (operação)OLAP (análise)
Carga típicaMuitas transações pequenasPoucas consultas enormes
O que importaLatência baixaVazão alta
BancoPostgreSQL (linhas)ClickHouse/DuckDB (colunar)
ExemploSalvar um pedidoFaturamento por mês, milhões de linhas
Na mesma máquinaA análise trava a operaçãoSeparados, não competem

Estudo de Caso

Caso aplicado

Quando o BI pesado deixou de ameaçar a operação

Contexto. Os clientes da plataforma passaram a querer relatórios analíticos cada vez mais pesados — painéis com drill down, pivôs, indicadores que cruzam milhões de registros. Rodar essas consultas no banco operacional ameaçava travar as transações do dia a dia toda vez que alguém abrisse um dashboard.

Mesma máquina
BI pesado trava a operação
OLTP/OLAP separados
Cada carga na sua máquina
Ganho
BI pesado sem risco à operação

Decisão. Separar OLTP de OLAP: manter a operação no PostgreSQL e mover a análise para um banco colunar (ClickHouse/DuckDB). Os fatos passam a fluir do operacional para o analítico, e as consultas de BI rodam exclusivamente neste último.

Resultado. A análise mais pesada deixou de afetar a operação. Um cliente pode abrir o relatório mais brutal sem que outro sinta lentidão ao salvar um pedido, porque as duas cargas vivem em máquinas diferentes. A plataforma passou a oferecer BI pesado como recurso, em vez de temê-lo como ameaça à operação.

Incidente Real

Severidade · Alta

O relatório que congelou o dia a dia

  • T+0 Numa fase sem separação, uma consulta analítica pesada — varrendo milhões de linhas para um painel — foi disparada no banco operacional.
  • T+segundos A consulta consumiu os recursos do banco. As transações do dia a dia — salvar pedidos, atualizar cadastros — começaram a travar e a estourar tempo.
  • Diagnóstico A carga analítica e a operacional competiam pela mesma máquina; a análise pesada venceu, e a operação foi a vítima.
  • Correção Separação de OLTP e OLAP: a operação permaneceu no PostgreSQL, e a análise migrou para um banco colunar dedicado. As cargas deixaram de competir.
  • Kaizen Vira regra: nenhuma consulta analítica pesada roda no banco operacional; a análise vive no colunar, alimentado pelos fatos.

Decisão Arquitetural

ADR-0024

Separar OLTP (PostgreSQL) de OLAP (colunar): operação e análise não competem

Aceito
Contexto

Operação (OLTP) e análise (OLAP) têm necessidades opostas — latência baixa vs vazão alta. Uma consulta de BI pesada no banco operacional consome a máquina e trava as transações do dia a dia. Com clientes querendo BI pesado, a operação vira a vítima.

Decisão

A operação transacional vive no PostgreSQL (OLTP); a análise vive em banco colunar — ClickHouse ou DuckDB (OLAP). Os fatos fluem do OLTP para o OLAP (via Event Store). As consultas analíticas pesadas rodam exclusivamente no colunar, nunca competindo com a operação.

Alternativas consideradas

Tudo no banco operacional — BI pesado trava a operação. Réplica de leitura do mesmo banco — ajuda, mas não tem a eficiência colunar para agregações enormes. Análise em tempo real no OLTP — inviável em escala.

Consequências
PositivasBI pesado sem risco à operação. Cada banco na sua força. Agregações enormes viáveis e rápidas. Operação sempre responsiva.
NegativasExige manter o pipeline que move os fatos do OLTP para o OLAP e a infraestrutura do banco colunar.
RiscosOs dados no OLAP têm leve atraso (não tempo real); aceitável para análise e mitigado por sincronização frequente.

Antipadrões

Análise pesada no banco operacional

Consultas de BI que varrem milhões de linhas competindo com as transações do dia a dia. Correção: separar OLTP de OLAP; análise no banco colunar.

Banco colunar para a operação

Usar um banco analítico para transações pequenas e frequentes. Correção: cada banco na sua força — PostgreSQL para OLTP, colunar para OLAP.

Exercício

Separe operação de análise no seu sistema

  1. No seu sistema, as consultas analíticas pesadas (relatórios, dashboards, agregações de muitos dados) rodam no mesmo banco que a operação do dia a dia?
  2. Faça o teste do pico: o que acontece com as transações do dia a dia quando um relatório pesado roda? Elas ficam lentas? Travam?
  3. Identifique suas cargas: quais são OLTP (transações pequenas e rápidas) e quais são OLAP (varreduras grandes para análise)? Elas têm necessidades opostas?
  4. Esboce a separação: a operação ficaria no banco transacional; a análise iria para um colunar. Como os dados fluiriam de um para o outro?
Meta: identificar onde a análise pesada ameaça a operação no seu sistema — e planejar a separação OLTP/OLAP que permite BI pesado sem travar o dia a dia.
✓ Checklist do tópico
  • A operação transacional (OLTP) vive em banco próprio (ex.: PostgreSQL)
  • A análise pesada (OLAP) vive em banco colunar (ex.: ClickHouse/DuckDB)
  • Consultas analíticas pesadas não rodam no banco operacional
  • Os fatos fluem do OLTP para o OLAP sem competir pela mesma máquina
  • BI pesado pode rodar sem travar as transações do dia a dia
Meta Dados · Engenharia de Plataformas · Capítulo IV · §4.6Fim do Capítulo IV · voltar ao início
Meta Dados · Engenharia de Plataformas
Capítulo V

Experiência,
Interface
e Design

A camada que o usuário toca: a variação visual como dado, um componente de cada tipo, acessibilidade como requisito, e a tela gerada a partir da descrição — com todos os seus estados.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo V
Capítulo V · Experiência, Interface e Design
5.1

Design Tokens

A tese do Capítulo I reaparece na superfície: o que varia é dado. Cores, espaçamentos e raios espalhados pelo código tornam impossível mudar a identidade visual. Como tokens, a aparência inteira vira configuração.

Pré-requisitos · Cap I Tese · Cap III MetadadosConecta com · 5.2 Componentes · 5.4 Screen EngineTempo de leitura · ~12 min

O Problema

A identidade visual de um sistema — suas cores, seus espaçamentos, seus cantos arredondados, suas fontes — é variação por natureza. Ela muda entre temas, entre marcas, entre um modo claro e um escuro. Mas quando esses valores estão cravados diretamente em cada componente — um #1A1815 aqui, um 12px ali, repetidos centenas de vezes pelo código —, mudar a identidade visual vira uma operação impossível: é preciso caçar cada ocorrência, em cada arquivo, sem garantia de tê-las encontrado todas.

Essa é exatamente a armadilha que o Capítulo I diagnosticou, agora na camada visual: tratar variação como se fosse código fixo. Uma plataforma que serve múltiplas marcas, ou que precisa de um tema escuro, ou que tem um parceiro white-label, não pode depender de editar centenas de valores espalhados. A aparência precisa ser dado — editável num lugar, refletida em todo o sistema.

O sintoma do valor espalhado

Quando mudar a cor primária exige editar dezenas de componentes, a variação visual está cravada no código. Correção: a aparência vira design tokens — dado referenciado, não valor repetido.

A Decisão

A variação visual é dado: design tokens. Cores, espaçamentos, raios e fontes são definidos uma vez como tokens; os componentes os referenciam. Trocar o token muda todo o sistema.

Os design tokens são a aplicação da tese metadado/código (Cap III) à camada visual. Cada decisão de aparência — a cor primária, o raio de borda padrão, a escala de espaçamento, a família tipográfica — é definida uma vez como um token nomeado. Os componentes nunca usam valores literais; eles referenciam os tokens. Assim, a aparência inteira do sistema passa a viver num conjunto de tokens, e trocá-la é trocar esses valores num só lugar. Um tema escuro é um conjunto de tokens; uma marca white-label é outro. O código dos componentes permanece idêntico — só os tokens que ele referencia mudam.

Diagrama conceitual A variação visual como dado
TOKEN · a variação como dado cor-primaria: #1A1815 raio-borda: 12px COMPONENTE · usa o token background: var(cor-primaria) border-radius: var(raio-borda) referencia TROCAR O TOKEN → todo o sistema muda um tema white-label novo é um conjunto de tokens, não um fork
Figura 5.1-a. O token define a variação uma vez; o componente a referencia. Trocar o token propaga a mudança a todo o sistema — um tema novo é dado, não um fork.

Os tokens como fonte da aparência

tokens.cssCSS
/* A variação visual definida UMA vez, como dado. */
:root {
  /* cores */
  --cor-primaria: #1A1815;
  --cor-acento: #C8452F;
  /* espaçamento e forma */
  --espaco-base: 8px;
  --raio-borda: 12px;
  /* tipografia */
  --fonte-titulo: 'Outfit', sans-serif;
}

/* Um TEMA é só outro conjunto de tokens — sem tocar componentes. */
[data-tema="parceiro-wl"] {
  --cor-primaria: #7A2E8F;   /* marca do parceiro */
  --cor-acento: #C8452F;
}
A aparência inteira são variáveis. Um tema white-label sobrescreve alguns tokens — e nenhum componente precisa mudar.
botao.cssCSS
/* O componente REFERENCIA tokens. Nunca valores literais. */
.botao-primario {
  /* errado seria: background: #1A1815; (valor cravado) */
  background: var(--cor-primaria);
  border-radius: var(--raio-borda);
  padding: var(--espaco-base) calc(var(--espaco-base) * 2);
  font-family: var(--fonte-titulo);
}
/* Trocar --cor-primaria muda ESTE e TODOS os componentes de uma vez. */
O componente só conhece nomes de tokens. A mudança de identidade acontece nos tokens, e ele a herda sem ser tocado.

A Verificação

TokensTest.phpPHP
final class TokensDeComponenteTest extends TestCase
{
    public function test_componentes_nao_usam_valores_literais(): void
    {
        // Varre o CSS dos componentes em busca de cores cravadas.
        $violacoes = $this->buscarCoresLiteraisEm('componentes/');

        // Nenhum componente pode ter um #hex no lugar de um token.
        $this->assertSame([], $violacoes,
            'Componentes com cor literal em vez de token: ' . implode(', ', $violacoes));
    }

    public function test_troca_de_tema_muda_a_cor_primaria(): void
    {
        // O mesmo componente, dois temas, cores diferentes — sem mudar código.
        $this->assertNotEquals(
            $this->corPrimariaNoTema('padrao'),
            $this->corPrimariaNoTema('parceiro-wl'),
        );
    }
}
O teste guarda a disciplina: nenhum componente pode cravar uma cor, e a troca de tema realmente muda a aparência sem tocar código.
Tabela de decisão Valor cravado vs design token
Mudança desejadaValor cravadoDesign token
Trocar a cor primáriaCaçar em N componentesUm token, propaga a tudo
Tema escuroReescrever cada componenteConjunto de tokens alternativo
Marca white-labelFork do sistemaTokens da marca, mesmo código
Ajustar espaçamento globalInconsistente, manualToken de espaço, uniforme

Estudo de Caso

Caso aplicado

Um parceiro white-label sem forkar o sistema

Contexto. Um parceiro white-label precisava rodar a plataforma com sua própria identidade visual — cores, e ajustes de marca —, sob seu próprio domínio. A abordagem ingênua seria duplicar a interface e trocar os valores, criando um fork que divergiria do sistema principal a cada atualização.

Fork visual
Interface duplicada, diverge
Tokens
Mesmo código, tokens da marca
Ganho
White-label sem fork

Decisão. Tratar a identidade do parceiro como um conjunto de design tokens. O tema white-label sobrescreve os tokens de cor e marca; todos os componentes, idênticos aos do sistema principal, herdam a nova aparência apenas por referenciarem os tokens.

Resultado. O parceiro ganhou sua identidade visual sem que uma linha de componente fosse duplicada. Atualizações no sistema principal beneficiam o tema do parceiro automaticamente, porque é o mesmo código com tokens diferentes. A variação visual como dado transformou um fork potencial em uma simples troca de tokens.

Incidente Real

Severidade · Baixa

A cor que mudou em quase todo lugar

  • Antes Numa fase sem tokens consistentes, a cor primária estava cravada diretamente em muitos componentes. Uma mudança de identidade exigiu trocá-la manualmente.
  • Sintoma A troca foi feita componente a componente, mas alguns foram esquecidos — a nova cor apareceu em quase todo lugar, deixando pontos com a cor antiga, de forma inconsistente.
  • Diagnóstico A cor cravada e repetida tornou impossível mudá-la de forma confiável: bastou esquecer alguns dos muitos lugares.
  • Correção Extração da cor (e demais valores visuais) para design tokens. A partir daí, a mudança de identidade passou a acontecer num só lugar.
  • Kaizen Vira regra com teste: componentes não usam valores visuais literais; tudo referencia tokens, verificado no CI.

Decisão Arquitetural

ADR-0025

A variação visual é dado: design tokens, não valores cravados

Aceito
Contexto

Cores, espaçamentos, raios e fontes cravados em cada componente tornam impossível mudar a identidade visual de forma confiável — é preciso caçar cada valor, sem garantia de tê-los achado todos. É a armadilha do Cap I na camada visual.

Decisão

A variação visual é definida uma vez como design tokens nomeados. Os componentes referenciam tokens, nunca valores literais. Temas (escuro, white-label) são conjuntos de tokens; trocá-los muda a aparência sem tocar os componentes. A disciplina é verificada no CI.

Alternativas consideradas

Valores cravados — mudança impossível de fazer com confiança. Tokens parciais — alguns valores ainda espalhados, inconsistência persiste. Pré-processador sem tokens em runtime — dificulta troca de tema dinâmica.

Consequências
PositivasIdentidade muda num só lugar. Temas e white-label sem fork. Consistência visual garantida. Modo escuro trivial.
NegativasExige a disciplina de nunca cravar valores nos componentes, verificada por teste.
RiscosToken mal nomeado ou ausente gera exceção; mitigado por uma escala de tokens bem projetada e verificação.

Antipadrões

Valor visual cravado no componente

Uma cor #hex ou medida fixa repetida em cada componente. Correção: referenciar um design token; o valor vive num só lugar.

Tokens inconsistentes ou parciais

Alguns valores como token, outros cravados. Correção: toda decisão visual é token; verificação no CI impede o literal.

Exercício

Encontre os valores visuais espalhados

  1. Procure no seu código de interface por cores literais (valores hex/rgb) e medidas fixas repetidas. Quantas vezes a sua cor primária aparece cravada?
  2. Imagine que você precisa de um tema escuro amanhã. Hoje, isso seria trocar um conjunto de tokens, ou reescrever componentes?
  3. Defina os tokens fundamentais do seu sistema: quais são as cores, espaçamentos, raios e fontes que toda a interface deveria referenciar?
  4. Esboce uma verificação que detecte um componente usando uma cor literal em vez de um token.
Meta: transformar a aparência cravada do seu sistema em design tokens — para que mudar a identidade visual, ou criar um tema, seja trocar dados num só lugar.
✓ Checklist do tópico
  • A variação visual (cor, espaço, raio, fonte) é definida como design tokens
  • Os componentes referenciam tokens, nunca valores literais
  • Trocar um token propaga a mudança a todo o sistema
  • Temas (escuro, white-label) são conjuntos de tokens, sem fork
  • A ausência de valores literais nos componentes é verificada no CI
Meta Dados · Engenharia de Plataformas · Capítulo V · §5.1Próximo → 5.2 Biblioteca de Componentes
Capítulo V · Experiência, Interface e Design
5.2

Biblioteca de Componentes

Sem uma biblioteca, cada tela inventa seu próprio botão, seu próprio campo, sua própria tabela. O resultado são dez variações ligeiramente diferentes da mesma coisa — e uma interface que parece feita por dez pessoas que não se falaram.

Pré-requisitos · 5.1 TokensConecta com · 5.4 Screen Engine · Cap II ReúsoTempo de leitura · ~11 min

O Problema

Sem uma biblioteca de componentes, cada tela acaba inventando os seus próprios. Uma tela cria um botão de um jeito; outra, sem saber, cria um botão parecido, mas com cor, tamanho e cantos ligeiramente diferentes. Multiplicado por dezenas de telas e dezenas de elementos, o sistema acumula dez variações da mesma coisa — dez botões que deveriam ser um, dez campos de texto sutilmente distintos. A interface passa a parecer feita por dez pessoas que nunca conversaram.

A incoerência visual é só o sintoma mais visível. O custo mais profundo é a manutenção: quando se descobre que o botão precisa de uma melhoria — um ajuste de acessibilidade, uma correção de comportamento no toque —, essa correção precisa ser feita em cada uma das dez variações, e provavelmente algumas serão esquecidas. O que deveria ser uma mudança vira dez, e a divergência só aumenta com o tempo.

O sintoma da reinvenção

Quando duas telas têm botões parecidos mas diferentes, cada uma inventou o seu. Correção: um componente canônico de cada tipo, numa biblioteca, usado por todas as telas.

A Decisão

Existe um componente canônico de cada tipo, numa biblioteca compartilhada. As telas usam os componentes da biblioteca; nunca reinventam os seus. Corrigir um componente beneficia todos os usos.

A plataforma mantém uma biblioteca de componentes com um exemplar canônico de cada tipo: um <Botao>, um <Campo>, uma <Tabela>, um <Modal>. Cada componente encapsula sua aparência (via tokens, §5.1), seu comportamento e sua acessibilidade (§5.3). As telas usam esses componentes; elas nunca recriam um botão do zero. É o princípio do reúso (Cap II) aplicado à interface: um componente, muitos usos. A consequência prática é poderosa: melhorar o componente canônico — torná-lo mais acessível, corrigir um comportamento — melhora todas as telas que o usam, de uma só vez. A correção é única; o benefício, universal.

Diagrama conceitual Um componente de cada tipo
SEM BIBLIOTECA · cinco botões diferentes Botão A Botão B Botão C Botão D divergem em cor, raio, tamanho — incoerência COM BIBLIOTECA · um botão canônico <Botao /> um só, usado em todo lugar — coerente UM componente de cada tipo, na bibliotecacorrigir/melhorar um beneficia todos os usos
Figura 5.2-a. Sem biblioteca, cada tela inventa seu botão e eles divergem. Com a biblioteca, há um botão canônico — e corrigi-lo beneficia todos os usos simultaneamente.

O componente canônico, usado por todas as telas

Botao.jsxReact
// O ÚNICO botão do sistema. Encapsula token, comportamento e acessibilidade.
export function Botao({ variante = 'primario', carregando, children, ...props }) {
  return (
    <button
      className={`botao botao-${variante}`}  // usa tokens (§5.1)
      disabled={carregando}
      aria-busy={carregando}              // acessibilidade embutida (§5.3)
      {...props}
    >
      {carregando ? <Spinner /> : children}
    </button>
  );
}
// Toda tela usa ESTE botão. Nenhuma inventa o seu.
O botão canônico já traz tokens, estado de carregamento e acessibilidade. Cada tela só o usa — e herda tudo isso de graça.
uso_nas_telas.jsxReact
// Telas diferentes, MESMO botão. Coerência por construção.
import { Botao } from '@plataforma/componentes';

// Tela de cadastro:
<Botao variante="primario" onClick={salvar}>Salvar</Botao>

// Tela de pedido — o MESMO componente, não uma cópia:
<Botao variante="primario" carregando={enviando}>Aprovar</Botao>

// Melhorar <Botao> melhora AS DUAS telas — e todas as outras.
Duas telas, um único componente importado. Uma melhoria no botão canônico chega às duas — e a todas — sem retrabalho.

A Verificação

BibliotecaTest.jsxReact
// Teste do componente canônico: vale para TODOS os seus usos.
test('o botão expõe estado de carregamento de forma acessível', () => {
  render(<Botao carregando>Salvar</Botao>);

  // Como há UM botão, este teste cobre toda tela que o usa.
  expect(screen.getByRole('button')).toHaveAttribute('aria-busy', 'true');
  expect(screen.getByRole('button')).toBeDisabled();
});
Testar o componente canônico uma vez cobre todas as telas que o usam — a economia de manutenção que a reinvenção desperdiça.
Tabela de decisão Reinventar vs biblioteca canônica
AspectoCada tela reinventaBiblioteca canônica
Coerência visualDez variaçõesUm padrão, sempre igual
Corrigir o componenteDez vezes, alguns esquecidosUma vez, todos beneficiados
AcessibilidadeRepetida e inconsistenteEmbutida, uma vez (§5.3)
TestarCada variaçãoUm componente cobre todos os usos

Estudo de Caso

Caso aplicado

A melhoria de acessibilidade que chegou a todas as telas

Contexto. Foi identificado que os botões do sistema precisavam expor melhor seu estado de carregamento para tecnologias assistivas (§5.3). Num sistema onde cada tela inventou seu botão, essa correção teria que ser aplicada em dezenas de lugares, com risco de esquecer vários.

Sem biblioteca
Corrigir dezenas de botões
Com biblioteca
Corrigir um, todos herdam
Ganho
Melhoria universal, esforço único

Decisão. Como havia um único componente <Botao> canônico na biblioteca, a melhoria de acessibilidade foi aplicada nele uma só vez — embutindo a sinalização correta de estado.

Resultado. Todas as telas que usam o botão canônico ganharam a melhoria simultaneamente, sem que nenhuma precisasse ser tocada. Uma correção, benefício universal. A biblioteca transformou o que seria um esforço disperso e propenso a falhas numa única mudança confiável.

Incidente Real

Severidade · Baixa

Os campos que se comportavam de formas diferentes

  • Antes Sem um campo de texto canônico, várias telas implementaram seus próprios campos, com tratamentos sutilmente diferentes de validação e foco.
  • Sintoma O usuário encontrava comportamentos inconsistentes: um campo limpava o erro ao digitar, outro não; um mostrava foco claramente, outro mal o indicava.
  • Diagnóstico A ausência de um componente canônico fez cada tela reinventar o campo, com comportamentos divergentes e qualidade desigual.
  • Correção Criação de um campo canônico na biblioteca, com comportamento e acessibilidade consistentes. As telas migraram para usá-lo.
  • Kaizen Vira regra: há um componente de cada tipo na biblioteca; telas usam a biblioteca em vez de reinventar.

Decisão Arquitetural

ADR-0026

Um componente canônico de cada tipo, numa biblioteca compartilhada

Aceito
Contexto

Sem uma biblioteca, cada tela reinventa botões, campos e tabelas, gerando dezenas de variações divergentes — incoerência visual e correções que precisam ser repetidas em cada cópia, com algumas esquecidas.

Decisão

Existe um componente canônico de cada tipo numa biblioteca compartilhada, encapsulando aparência (tokens), comportamento e acessibilidade. As telas usam os componentes da biblioteca; nunca reinventam. Corrigir um componente beneficia todos os usos. É o reúso do Cap II na interface.

Alternativas consideradas

Cada tela reinventa — divergência e manutenção multiplicada. Cópia e cola de componentes — diverge no tempo. Biblioteca sem disciplina de uso — telas voltam a reinventar.

Consequências
PositivasCoerência visual por construção. Correção única, benefício universal. Acessibilidade embutida uma vez. Testes que cobrem todos os usos.
NegativasExige projetar componentes flexíveis o suficiente e a disciplina de usá-los em vez de reinventar.
RiscosComponente rígido demais leva à reinvenção; mitigado por variantes e por ouvir as necessidades reais das telas.

Antipadrões

Cada tela reinventando o componente

Botões e campos recriados do zero em cada tela. Correção: um componente canônico na biblioteca, usado por todas.

Cópia e cola de componente

Duplicar um componente para ajustar numa tela. Correção: estender o canônico com variantes; a cópia diverge com o tempo.

Exercício

Encontre os componentes reinventados

  1. Olhe duas ou três telas do seu sistema. Os botões, campos e tabelas são exatamente os mesmos componentes, ou variações parecidas mas diferentes?
  2. Escolha um elemento comum (o botão, por exemplo). Quantas implementações diferentes dele existem no seu código? Elas divergem em quê?
  3. Imagine uma correção de acessibilidade nesse elemento. Hoje, seria uma mudança ou várias? Quantos lugares você teria que tocar?
  4. Esboce o componente canônico: que variantes ele precisaria para atender todas as telas sem que nenhuma reinvente o seu?
Meta: identificar os componentes que seu sistema reinventa em cada tela — e planejar a biblioteca canônica que os unificaria, tornando cada correção única.
✓ Checklist do tópico
  • Existe um componente canônico de cada tipo numa biblioteca
  • Os componentes encapsulam aparência (tokens), comportamento e acessibilidade
  • As telas usam os componentes da biblioteca, sem reinventar
  • Corrigir um componente beneficia todos os seus usos
  • O componente canônico é testado uma vez, cobrindo todos os usos
Meta Dados · Engenharia de Plataformas · Capítulo V · §5.2Próximo → 5.3 Acessibilidade
Capítulo V · Experiência, Interface e Design
5.3

Acessibilidade

Acessibilidade tratada como enfeite final é acessibilidade que nunca acontece. Sempre há algo mais urgente. Tratada como requisito — verificada como qualquer outro —, ela deixa de ser opcional e passa a ser parte do que "pronto" significa.

Pré-requisitos · 5.2 ComponentesConecta com · Cap VII Conformidade · Cap VIII VerificaçãoTempo de leitura · ~13 min

O Problema

Acessibilidade — tornar a interface utilizável por pessoas com deficiência visual, motora, auditiva ou cognitiva — costuma ser tratada como um polimento final, algo a fazer "se sobrar tempo". E o tempo nunca sobra. Sempre há uma funcionalidade mais urgente, um prazo mais apertado. Tratada como enfeite opcional, a acessibilidade é perpetuamente adiada — e o sistema acaba excluindo, na prática, uma parcela significativa de seus usuários.

O custo dessa exclusão é alto e múltiplo. É ético: pessoas com deficiência ficam impedidas de usar o sistema. É legal: em muitos contextos, a acessibilidade digital é uma exigência da lei, e sua ausência é passível de penalidade. E é de qualidade: uma interface acessível — com bom contraste, navegável por teclado, com foco visível — é melhor para todos, não só para quem tem deficiência. Tratar acessibilidade como opcional é, ao mesmo tempo, excluir usuários, arriscar a conformidade e abrir mão de qualidade.

O enfeite que nunca acontece

Acessibilidade deixada para o final, "se sobrar tempo", nunca é feita — e o sistema exclui usuários, arrisca a lei e perde qualidade. Correção: tratá-la como requisito verificado, parte do que "pronto" significa.

A Decisão

Acessibilidade é requisito, não enfeite. As diretrizes WCAG são embutidas nos componentes canônicos e verificadas automaticamente — uma tela só está "pronta" quando é acessível.

A acessibilidade é tratada como requisito de primeira classe, sujeito a verificação como qualquer outro. As diretrizes do WCAG (o padrão internacional de acessibilidade web) tornam-se parte da definição de "pronto". Crucialmente, a acessibilidade vive nos componentes canônicos (§5.2): o contraste adequado das cores (via tokens), a navegabilidade por teclado, os rótulos e papéis para leitores de tela (atributos ARIA), e o foco visível são embutidos no componente uma vez — e toda tela que o usa herda essa acessibilidade automaticamente. Verificações automatizadas checam contraste, presença de rótulos e navegação por teclado, falhando o build quando algo regride. A acessibilidade deixa de depender de lembrar, e passa a ser garantida pela arquitetura e pela verificação.

Diagrama conceitual Acessibilidade embutida no componente
ACESSIBILIDADE no componente canônico (§5.2) — uma vez, para todos Contrastecor legível para todos Tecladonavegável sem mouse Leitor de telarótulos e papéis (ARIA) Foco visívelonde estou na tela WCAG é requisito verificado, não enfeite opcional
Figura 5.3-a. Contraste, teclado, leitor de tela e foco visível vivem no componente canônico — embutidos uma vez, herdados por todas as telas, e verificados como requisito.

Acessibilidade embutida no componente canônico

CampoAcessivel.jsxReact
// Acessibilidade EMBUTIDA no componente. Toda tela a herda (§5.2).
export function Campo({ rotulo, erro, id, ...props }) {
  const erroId = `${id}-erro`;
  return (
    <div>
      {/* Rótulo associado: leitor de tela anuncia o campo */}
      <label htmlFor={id}>{rotulo}</label>
      <input
        id={id}
        aria-invalid={!!erro}            {/* estado de erro audível */}
        aria-describedby={erro ? erroId : undefined}
        {...props}
      />
      {/* Erro associado e anunciado, não só colorido */}
      {erro && <span id={erroId} role="alert">{erro}</span>}
    </div>
  );
}
O campo canônico já associa rótulo, anuncia erro e expõe estado. Toda tela que o usa é acessível sem esforço extra.
verificacao_a11y.jsJavaScript
// Verificação automatizada: acessibilidade como REQUISITO no CI.
import { axe } from 'jest-axe';

test('o componente não tem violações de acessibilidade', async () => {
  const { container } = render(<Campo rotulo="Nome" id="nome" />);

  // Checa contraste, rótulos, papéis ARIA — falha o build se regride.
  const resultado = await axe(container);
  expect(resultado).toHaveNoViolations();
});
A verificação automatizada torna a acessibilidade um requisito real: o build falha quando uma violação aparece, como em qualquer teste.

A Verificação

NavegacaoTeclado.test.jsxReact
// Acessibilidade de teclado: navegável sem mouse.
test('o formulário é totalmente navegável por teclado', async () => {
  render(<FormularioDeCadastro />);
  const usuario = userEvent.setup();

  // Tab percorre os campos na ordem correta...
  await usuario.tab();
  expect(screen.getByLabelText('Nome')).toHaveFocus();

  // ...e o foco é sempre visível (verificado no componente).
  await usuario.tab();
  expect(screen.getByLabelText('Documento')).toHaveFocus();
});
O teste prova que a interface funciona sem mouse — uma exigência de acessibilidade que só a verificação garante de forma confiável.
Tabela de decisão Enfeite opcional vs requisito verificado
AspectoAcessibilidade como enfeiteAcessibilidade como requisito
Quando é feita"Se sobrar tempo" (nunca)Parte de "pronto"
Onde viveEspalhada, inconsistenteNo componente canônico (§5.2)
Como é garantidaDepende de lembrarVerificação automatizada
Conformidade legalEm riscoAtendida e verificável
BeneficiaNinguém (não é feita)Todos os usuários

Estudo de Caso

Caso aplicado

Acessibilidade que não depende de lembrar

Contexto. Tratada como polimento final, a acessibilidade do sistema era inconsistente — algumas telas a tinham, outras não, conforme quem as construiu lembrou (ou não) de incluí-la. O risco de exclusão de usuários e de não conformidade era real e crescente.

Como enfeite
Inconsistente, depende de lembrar
Como requisito
Embutida e verificada
Ganho
Acessibilidade por construção

Decisão. Mover a acessibilidade para os componentes canônicos (§5.2) e torná-la um requisito verificado: rótulos, contraste, navegação por teclado e foco visível embutidos nos componentes, com verificação automatizada no CI.

Resultado. A acessibilidade deixou de depender da memória de cada desenvolvedor. Toda tela construída com os componentes canônicos nasce acessível, e o build falha se uma violação for introduzida. O que era inconsistente e arriscado tornou-se uma garantia da arquitetura — acessibilidade por construção, não por boa vontade.

Incidente Real

Severidade · Média

A tela que excluía quem usava leitor de tela

  • Antes Uma tela importante foi construída sem rótulos associados aos campos e sem sinalização de erros para tecnologias assistivas.
  • Sintoma Usuários que dependiam de leitor de tela não conseguiam entender o que cada campo pedia, nem por que um envio falhava — a tela era, para eles, inutilizável.
  • Diagnóstico A acessibilidade fora tratada como opcional e esquecida nessa tela, excluindo uma parcela de usuários e expondo a um risco de conformidade.
  • Correção Migração da tela para os componentes canônicos acessíveis e adição de verificação automatizada de acessibilidade no CI.
  • Kaizen Vira regra: acessibilidade é requisito verificado; nenhuma tela está "pronta" sem passar nas checagens de acessibilidade.

Decisão Arquitetural

ADR-0027

Acessibilidade é requisito verificado, embutida nos componentes

Aceito
Contexto

Tratada como enfeite final, a acessibilidade nunca acontece — sempre há algo mais urgente. O sistema acaba excluindo usuários com deficiência, arriscando a conformidade legal e perdendo qualidade para todos.

Decisão

A acessibilidade é um requisito de primeira classe, com as diretrizes WCAG como parte de "pronto". Ela é embutida nos componentes canônicos (§5.2) — contraste, teclado, ARIA, foco visível — e verificada automaticamente no CI, falhando o build em regressões.

Alternativas consideradas

Acessibilidade como polimento final — nunca é feita. Checagem manual ocasional — inconsistente e esquecível. Acessibilidade tela a tela — repetida, divergente e propensa a falhas.

Consequências
PositivasToda tela com componentes canônicos nasce acessível. Conformidade garantida e verificável. Qualidade para todos. Sem depender de lembrar.
NegativasExige embutir acessibilidade nos componentes e manter as verificações automatizadas.
RiscosA verificação automática não cobre tudo; mitigada por complementá-la com avaliação humana em fluxos críticos.

Antipadrões

Acessibilidade como polimento final

Deixar para "se sobrar tempo" — que nunca sobra. Correção: requisito verificado, parte de "pronto", embutido nos componentes.

Cor como único sinal

Indicar erro ou estado apenas por cor exclui quem não a distingue. Correção: cor mais texto/ícone/ARIA — o sinal nunca depende só da cor.

Exercício

Teste a acessibilidade do seu sistema

  1. Tente navegar uma tela do seu sistema usando apenas o teclado, sem mouse. Você consegue alcançar e ativar tudo? O foco é sempre visível?
  2. Verifique se os erros e estados são sinalizados de outra forma além da cor. Alguém que não distingue cores entenderia?
  3. Os campos têm rótulos associados que um leitor de tela anunciaria? Ou dependem de um texto solto ao lado?
  4. Pense na verificação: como você faria a acessibilidade ser um requisito que falha o build, em vez de um enfeite esquecível?
Meta: avaliar a acessibilidade real do seu sistema — por teclado, por contraste, por leitor de tela — e planejar como torná-la um requisito verificado, embutido nos componentes.
✓ Checklist do tópico
  • A acessibilidade é tratada como requisito, parte de "pronto"
  • As diretrizes WCAG são embutidas nos componentes canônicos
  • Contraste, teclado, ARIA e foco visível vêm dos componentes
  • Estados e erros são sinalizados além da cor
  • A acessibilidade é verificada automaticamente no CI
Meta Dados · Engenharia de Plataformas · Capítulo V · §5.3Próximo → 5.4 Do Metamodelo à Tela
Capítulo V · Experiência, Interface e Design
5.4

Do Metamodelo à Tela

O metamodelo descreve o formulário como dado; os componentes encapsulam aparência e acessibilidade. Falta a ponte. E se essa ponte gerar HTML cru, toda a disciplina dos tópicos anteriores se perde no último metro.

Pré-requisitos · Cap III Metamodelo · 5.1–5.3Conecta com · 5.5 Estados · Cap III ResolvedorTempo de leitura · ~12 min

O Problema

Os tópicos anteriores montaram as peças, mas falta conectá-las. O Capítulo III definiu o metamodelo — a descrição de um formulário como dado — e o resolvedor que entrega essa descrição já resolvida. Este Capítulo V definiu os design tokens (§5.1), a biblioteca de componentes (§5.2) e a acessibilidade embutida (§5.3). Mas há uma ponte ausente: como uma definição de formulário em dados vira uma tela renderizada, feita de componentes acessíveis e tematizados?

E essa ponte esconde um risco sutil mas grave. Se o motor que transforma a descrição em tela — o Screen Engine — gerar HTML cru, montando elementos diretamente em vez de usar os componentes do Design System, então toda a disciplina construída se perde no último metro. Os tokens não são aplicados, a acessibilidade não é herdada, a coerência da biblioteca é abandonada — tudo no exato ponto em que o dado finalmente vira interface. Um Screen Engine descuidado anula, no ponto final, o trabalho de todos os tópicos anteriores.

O risco do último metro

Se o Screen Engine gera HTML cru em vez de usar os componentes do Design System, tokens e acessibilidade se perdem justamente onde o dado vira tela. Correção: a ponte renderiza com os componentes canônicos, nunca HTML solto.

A Decisão

O Screen Engine é a ponte explícita do metamodelo à tela — e ele renderiza usando os componentes do Design System, jamais HTML cru. A tela gerada herda tokens e acessibilidade automaticamente.

O Screen Engine recebe a descrição de formulário já resolvida (Cap III) e a transforma em tela usando os componentes canônicos da biblioteca (§5.2). Para cada campo descrito no metamodelo, ele não gera um <input> cru — ele instancia o componente <Campo> apropriado, que já traz tokens (§5.1) e acessibilidade (§5.3). O Screen Engine é, portanto, um tradutor de descrição em componentes, não um gerador de HTML. A consequência é que a tela gerada a partir de dados é tão tematizada e acessível quanto uma tela escrita à mão com a biblioteca — a disciplina dos tópicos anteriores chega intacta ao ponto final. O dado vira tela sem perder nada pelo caminho.

Arquitetura lógica A ponte do metamodelo à tela
METAMODELOdefinição em dados (Cap III) SCREEN ENGINEa ponte COMPONENTES do Design Systemtokens (§5.1) · acessíveis (§5.3) NUNCA gera HTML cru fora do Design System a ponte usa os componentes — a disciplina não se perde no fim
Figura 5.4-a. O Screen Engine traduz a descrição em dados para componentes do Design System — nunca HTML cru. A tela gerada herda tokens e acessibilidade, e a disciplina não se perde no fim.

A ponte traduz descrição em componentes

ScreenEngine.jsxReact
// A ponte: traduz a descrição (Cap III) em COMPONENTES (§5.2), não HTML cru.
export function ScreenEngine({ descricao }) {
  // descricao vem RESOLVIDA do Cap III (versão, override, permissão).
  return (
    <Formulario>
      {descricao.campos.map((campo) => (
        // Para cada campo, instancia o COMPONENTE canônico apropriado.
        // NUNCA um <input> cru — sempre o componente do Design System.
        <CampoPorTipo
          key={campo.chave}
          tipo={campo.tipo}            // 'texto', 'numero', 'data'
          rotulo={campo.rotulo}
          obrigatorio={campo.obrigatorio}
        />
      ))}
    </Formulario>
  );
}
O Screen Engine mapeia cada campo descrito para um componente canônico. A tela nasce com tokens e acessibilidade — sem HTML solto.
CampoPorTipo.jsxReact
// Mapeia o TIPO do metamodelo ao componente acessível correspondente.
function CampoPorTipo({ tipo, ...props }) {
  switch (tipo) {
    case 'texto':  return <Campo tipo="text" {...props} />;   // §5.2/§5.3
    case 'numero': return <CampoNumero {...props} />;
    case 'data':   return <CampoData {...props} />;
    // Tipo desconhecido falha claro — não gera um campo cru de emergência.
    default:       throw new Error(`Tipo de campo sem componente: ${tipo}`);
  }
}
Cada tipo do metamodelo resolve para um componente da biblioteca. Um tipo sem componente falha claro — nunca cai num HTML de emergência.

A Verificação

ScreenEngineTest.jsxReact
// Prova que a tela gerada usa COMPONENTES, e é acessível.
test('a tela gerada do metamodelo é acessível e tematizada', async () => {
  const descricao = { campos: [{ chave: 'nome', tipo: 'texto', rotulo: 'Nome' }] };

  const { container } = render(<ScreenEngine descricao={descricao} />);

  // O campo gerado tem rótulo associado (veio do componente, §5.3).
  expect(screen.getByLabelText('Nome')).toBeInTheDocument();
  // E não tem violações de acessibilidade — a disciplina chegou ao fim.
  expect(await axe(container)).toHaveNoViolations();
});
O teste prova o essencial: uma tela gerada de dados é acessível e tematizada, porque foi montada com os componentes — não com HTML cru.
Tabela de decisão HTML cru vs componentes do Design System
AspectoScreen Engine gera HTML cruScreen Engine usa componentes
Tokens (§5.1)Não aplicadosHerdados do componente
Acessibilidade (§5.3)PerdidaEmbutida no componente
Coerência com telas à mãoDivergeIdêntica
Disciplina dos tópicosAnulada no fimIntacta até a tela

Estudo de Caso

Caso aplicado

A tela gerada que ficou igual à feita à mão

Contexto. A plataforma gera telas a partir de definições no metamodelo (Cap III). O risco era que essas telas geradas saíssem diferentes — menos tematizadas, menos acessíveis — do que as telas escritas manualmente com a biblioteca de componentes.

HTML cru
Tela gerada inferior
Componentes
Igual à feita à mão
Ganho
Disciplina intacta no fim

Decisão. Fazer o Screen Engine renderizar exclusivamente através dos componentes canônicos do Design System, mapeando cada tipo de campo do metamodelo ao componente correspondente, em vez de gerar HTML cru.

Resultado. Uma tela gerada a partir de dados ficou indistinguível, em aparência e acessibilidade, de uma tela escrita à mão com a biblioteca. Os tokens são aplicados, a acessibilidade é herdada, a coerência é total. A disciplina dos tópicos anteriores chega intacta ao ponto onde o dado finalmente vira interface.

Incidente Real

Severidade · Média

As telas geradas que ignoravam o tema

  • Antes Numa primeira versão, o motor de geração de telas montava elementos de formulário diretamente, sem passar pelos componentes do Design System.
  • Sintoma As telas geradas a partir do metamodelo não respeitavam os tokens de tema nem tinham a acessibilidade dos componentes — destoavam visualmente e excluíam usuários de tecnologias assistivas.
  • Diagnóstico O Screen Engine gerava HTML cru, perdendo toda a disciplina de tokens e acessibilidade no último metro.
  • Correção Reescrita do Screen Engine para renderizar via componentes canônicos. As telas geradas passaram a herdar tokens e acessibilidade automaticamente.
  • Kaizen Vira regra: o Screen Engine nunca gera HTML cru; toda tela gerada usa os componentes do Design System, verificado por teste.

Decisão Arquitetural

ADR-0028

O Screen Engine renderiza via componentes do Design System, nunca HTML cru

Aceito
Contexto

O metamodelo (Cap III) descreve formulários como dado; tokens e componentes (§5.1–5.3) definem aparência e acessibilidade. A ponte — o Screen Engine — pode anular tudo se gerar HTML cru, perdendo tokens e acessibilidade no último metro.

Decisão

O Screen Engine traduz a descrição resolvida (Cap III) em componentes canônicos do Design System (§5.2), mapeando cada tipo de campo ao componente correspondente. Nunca gera HTML cru. A tela gerada herda tokens e acessibilidade, igual a uma feita à mão. Verificado por teste.

Alternativas consideradas

Gerar HTML cru — perde tokens e acessibilidade. Componentes só para alguns tipos — inconsistência parcial. Ponte implícita/ad hoc — diverge das telas escritas à mão.

Consequências
PositivasTelas geradas idênticas às feitas à mão. Tokens e acessibilidade herdados. Disciplina intacta até a interface. Geração confiável.
NegativasExige um componente para cada tipo do metamodelo e a disciplina de nunca cair em HTML cru.
RiscosTipo sem componente quebra a geração; tratado por falha clara e por manter a cobertura de tipos completa.

Antipadrões

Screen Engine gerando HTML cru

Montar elementos de formulário diretamente, fora do Design System. Correção: traduzir cada campo para o componente canônico correspondente.

Fallback para HTML em tipo desconhecido

Gerar um campo cru de emergência para um tipo sem componente. Correção: falhar claro; manter a cobertura de tipos completa.

Exercício

Verifique a ponte do seu sistema

  1. Se o seu sistema gera telas ou formulários a partir de configuração/dados, essa geração usa os seus componentes reais, ou monta HTML diretamente?
  2. Compare uma tela gerada com uma escrita à mão: elas têm a mesma aparência e a mesma acessibilidade? Onde divergem?
  3. Para cada tipo de campo que o seu metamodelo suporta, existe um componente correspondente na biblioteca? Há algum tipo que cai num HTML cru?
  4. Esboce a verificação: como você provaria que uma tela gerada de dados é tão acessível quanto uma feita à mão?
Meta: garantir que a ponte do seu sistema — de dados para tela — preserve toda a disciplina de tokens e acessibilidade, renderizando com componentes em vez de HTML cru.
✓ Checklist do tópico
  • O Screen Engine traduz o metamodelo em componentes do Design System
  • Nenhuma tela gerada usa HTML cru fora dos componentes
  • Cada tipo de campo do metamodelo mapeia a um componente canônico
  • A tela gerada herda tokens e acessibilidade automaticamente
  • Uma tela gerada é tão acessível quanto uma feita à mão (verificado)
Meta Dados · Engenharia de Plataformas · Capítulo V · §5.4Próximo → 5.5 Estados de Interface
Capítulo V · Experiência, Interface e Design
5.5

Estados de Interface

Telas reais não vivem só no estado "tudo certo, dados carregados". Há o carregando, o vazio, o erro, o sucesso, o sem-permissão. Quando esses estados são ignorados, o usuário encara telas brancas travadas e spinners eternos.

Pré-requisitos · 5.2 Componentes · 5.4 Screen EngineConecta com · Cap VIII Operações · Cap I OmotenashiTempo de leitura · ~13 min

O Problema

Telas reais não vivem apenas no estado confortável de "tudo certo, dados carregados". Toda tela tem uma vida muito mais variada: o momento em que os dados ainda estão carregando, o estado vazio quando ainda não há nada a mostrar, o estado de erro quando algo falha, o de sucesso após uma ação, e o de sem-permissão quando o usuário não pode ver aquilo. O estado feliz é apenas um entre vários.

Quando esses outros estados não são tratados como parte do design, a experiência quebra exatamente nos momentos em que o usuário mais precisa de clareza. Ele encara uma tela branca travada enquanto algo carrega sem aviso; uma mensagem críptica quando dá erro; um spinner eterno que nunca resolve; um vazio confuso que não diz o que fazer. Tratar só o caminho feliz é entregar um sistema que funciona apenas quando tudo dá certo — e a vida real raramente é assim.

O sintoma do caminho feliz

Telas brancas, spinners eternos e mensagens crípticas surgem quando só o estado "dados carregados" foi pensado. Correção: tratar todos os estados — carregando, vazio, erro, sucesso, sem-permissão — como parte do design.

A Decisão

Toda tela trata seus cinco estados explicitamente: carregando, vazio, erro, sucesso e sem-permissão. Omotenashi — antecipar a necessidade — significa desenhar todos, não só o feliz.

Cada tela é projetada para todos os seus estados, não apenas o de sucesso com dados. O estado de carregamento mostra um esqueleto ou indicador claro, nunca uma tela branca travada. O estado vazio orienta o próximo passo ("nenhum item ainda — crie o primeiro"), em vez de um vazio confuso. O estado de erro é claro e oferece recuperação ("não foi possível carregar — tentar de novo"), nunca uma mensagem críptica. O estado de sucesso confirma o que aconteceu. O estado sem-permissão explica que o acesso é restrito, em vez de simplesmente sumir ou quebrar. Essa é a aplicação do princípio Omotenashi do Capítulo I — a hospitalidade que antecipa a necessidade: o sistema cuida do usuário em todos os momentos, especialmente nos que não são o caminho feliz.

Diagrama conceitual Os cinco estados de uma tela
TODA TELA TEM CINCO ESTADOS, NÃO UM CARREGANDOesqueleto, não tela branca VAZIOorienta o próximo passo ERROclaro e com recuperação SUCESSOconfirma o que ocorreu SEM PERMISSÃOexplica, não some OMOTENASHI · antecipar a necessidade do usuáriotratar todos os estados, não só o feliz
Figura 5.5-a. O estado "dados carregados" é só um dos cinco. Omotenashi exige desenhar carregando, vazio, erro, sucesso e sem-permissão — cuidar do usuário em todos os momentos.

A tela que trata todos os seus estados

TelaComEstados.jsxReact
// Toda tela trata os CINCO estados, não só o feliz (Omotenashi).
export function ListaDeClientes() {
  const { dados, estado } = useClientes();

  switch (estado) {
    // Carregando: esqueleto, NÃO tela branca travada.
    case 'carregando': return <EsqueletoDeLista />;

    // Sem permissão: explica, não some nem quebra.
    case 'sem_permissao': return <AvisoSemAcesso />;

    // Erro: claro e COM recuperação.
    case 'erro': return <EstadoDeErro aoTentar={recarregar} />;

    // Vazio: orienta o próximo passo.
    case 'vazio': return <EstadoVazio acao="Cadastrar o primeiro cliente" />;

    // Sucesso (com dados): o caminho feliz, enfim.
    default: return <Tabela dados={dados} />;
  }
}
Os cinco estados são explícitos no código. O "default" — dados carregados — é o último, não o único: a tela cuida do usuário em todos os momentos.
EstadoVazio.jsxReact
// O estado vazio ORIENTA — não deixa o usuário sem saber o que fazer.
export function EstadoVazio({ acao, aoAgir }) {
  return (
    <div role="status">          {/* anunciado a leitores de tela */}
      <Ilustracao nome="vazio" />
      <p>Nenhum item ainda.</p>
      {/* Diz o próximo passo, em vez de um vazio confuso */}
      <Botao onClick={aoAgir}>{acao}</Botao>
    </div>
  );
}
O vazio não é uma tela em branco: ele explica a situação e oferece o próximo passo. Omotenashi é antecipar essa necessidade.

A Verificação

EstadosDeTela.test.jsxReact
// Cada estado é testado — todos fazem parte do design.
test('mostra esqueleto durante o carregamento', () => {
  render(<ListaDeClientes />, { estado: 'carregando' });
  expect(screen.getByTestId('esqueleto')).toBeInTheDocument();
});

test('no erro, oferece tentar novamente', async () => {
  render(<ListaDeClientes />, { estado: 'erro' });
  // O erro NÃO é críptico: há uma ação de recuperação.
  expect(screen.getByRole('button', { name: /tentar/i })).toBeInTheDocument();
});

test('no vazio, orienta o próximo passo', () => {
  render(<ListaDeClientes />, { estado: 'vazio' });
  expect(screen.getByText(/cadastrar o primeiro/i)).toBeInTheDocument();
});
Cada estado tem seu teste. Tratá-los como parte do design significa também verificá-los — não só o caminho feliz.
Tabela de decisão Estado não tratado vs estado desenhado
EstadoIgnorado (caminho feliz só)Tratado (Omotenashi)
CarregandoTela branca travadaEsqueleto claro
VazioVazio confusoOrienta o próximo passo
ErroMensagem crípticaClaro, com recuperação
SucessoSem confirmaçãoConfirma o que ocorreu
Sem permissãoSome ou quebraExplica a restrição

Estudo de Caso

Caso aplicado

Do spinner eterno à experiência completa

Contexto. Telas do sistema tratavam bem o caminho feliz, mas falhavam nos demais estados: durante o carregamento, o usuário via uma área em branco; em caso de erro, uma mensagem técnica incompreensível; quando não havia dados, um vazio que não orientava nada.

Só caminho feliz
Quebra no carregamento/erro
Cinco estados
Cuida do usuário sempre
Ganho
Experiência completa

Decisão. Aplicar Omotenashi: desenhar explicitamente os cinco estados de cada tela. Carregamento com esqueleto, vazio que orienta, erro com recuperação, sucesso que confirma, sem-permissão que explica — todos tratados como parte do design e verificados.

Resultado. A experiência deixou de quebrar nos momentos difíceis. O usuário sempre sabe o que está acontecendo e o que pode fazer a seguir, mesmo quando algo dá errado. A tela passou a cuidar dele em todos os momentos, não só quando tudo carrega perfeitamente — exatamente o que a hospitalidade do Capítulo I propõe.

Incidente Real

Severidade · Média

O spinner que nunca terminava

  • Antes Uma tela carregava dados de uma fonte que, ocasionalmente, falhava ou demorava demais. O estado de erro não era tratado: só havia o de carregamento.
  • Sintoma Quando a fonte falhava, o usuário ficava preso num spinner que girava para sempre — sem erro, sem recuperação, sem qualquer pista do que fazer.
  • Diagnóstico A ausência do estado de erro transformou uma falha recuperável numa experiência travada e sem saída para o usuário.
  • Correção Tratamento explícito do estado de erro, com mensagem clara e ação de "tentar novamente". O spinner eterno deu lugar a uma saída.
  • Kaizen Vira regra: toda tela trata seus cinco estados; nenhum spinner pode girar sem um caminho para erro e recuperação.

Decisão Arquitetural

ADR-0029

Toda tela trata seus cinco estados (Omotenashi)

Aceito
Contexto

Telas que só tratam o caminho feliz (dados carregados) quebram nos momentos críticos: tela branca no carregamento, mensagem críptica no erro, spinner eterno, vazio confuso. A experiência falha quando o usuário mais precisa de clareza.

Decisão

Toda tela trata explicitamente seus cinco estados: carregando (esqueleto), vazio (orienta), erro (claro, com recuperação), sucesso (confirma) e sem-permissão (explica). É a aplicação do Omotenashi (Cap I) — antecipar a necessidade. Cada estado é verificado por teste.

Alternativas consideradas

Tratar só o caminho feliz — quebra nos demais estados. Estado de erro genérico — críptico, sem recuperação. Estados tratados ad hoc por tela — inconsistente e incompleto.

Consequências
PositivasExperiência completa em todos os momentos. Usuário sempre orientado, mesmo em falhas. Sem spinners eternos nem telas brancas. Hospitalidade real.
NegativasExige desenhar e implementar todos os estados, e a disciplina de não esquecer nenhum.
RiscosMais estados, mais trabalho por tela; mitigado por componentes de estado reutilizáveis (esqueleto, erro, vazio).

Antipadrões

Tratar só o caminho feliz

Desenhar a tela apenas para "dados carregados". Correção: tratar os cinco estados — carregando, vazio, erro, sucesso, sem-permissão.

Erro sem recuperação

Mostrar uma mensagem de erro sem oferecer uma saída. Correção: o estado de erro é claro e sempre oferece uma ação (tentar de novo, voltar).

Exercício

Mapeie os estados das suas telas

  1. Pegue uma tela importante do seu sistema. Liste seus cinco estados: como ela se comporta carregando, vazia, em erro, em sucesso e sem permissão?
  2. Faça o teste da falha: o que o usuário vê quando a fonte de dados demora ou falha? Há um spinner eterno, ou um erro com recuperação?
  3. Faça o teste do vazio: quando não há dados, a tela orienta o próximo passo, ou deixa o usuário sem saber o que fazer?
  4. Para cada estado mal tratado, esboce como ele deveria se comportar para cuidar do usuário (Omotenashi) naquele momento.
Meta: mapear os estados de uma tela real e identificar quais são ignorados — para que a experiência cuide do usuário em todos os momentos, não só no caminho feliz.
✓ Checklist do tópico
  • Toda tela trata explicitamente seus cinco estados
  • O carregamento mostra esqueleto/indicador, não tela branca
  • O estado vazio orienta o próximo passo
  • O estado de erro é claro e oferece recuperação
  • Os estados sem-permissão e sucesso são tratados, e tudo é testado
Meta Dados · Engenharia de Plataformas · Capítulo V · §5.5Fim do Capítulo V · voltar ao início
Meta Dados · Engenharia de Plataformas
Capítulo VI

Integração
e Ecossistema
de Parceiros

A conversa com o mundo externo sem deixar o caos entrar: a camada anticorrupção, a mesma mensagem processada uma vez, o coletor de documentos e a orquestração declarativa.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo VI
Capítulo VI · Integração e Ecossistema de Parceiros
6.1

A Camada Anticorrupção

Todo sistema externo tem seu próprio modelo — muitas vezes estranho, às vezes caótico. Se esse modelo entra direto no seu domínio, ele o contamina: agora o seu código fala a língua do parceiro, e muda quando o parceiro muda.

Pré-requisitos · Cap II Dependência · EnginesConecta com · 6.3 Coletor · 6.5 ParceirosTempo de leitura · ~13 min

O Problema

Todo sistema externo com o qual a plataforma conversa — um parceiro, um provedor, uma autoridade — tem seu próprio modelo de dados. E esse modelo é, com frequência, estranho ao seu: nomes diferentes para as mesmas coisas, estruturas herdadas de decisões antigas, formatos que fazem sentido para eles mas não para você. Às vezes é simplesmente caótico. A tentação é deixar esse modelo entrar direto no domínio — afinal, "os dados já vêm assim".

Mas quando o modelo externo entra sem tradução, ele contamina o domínio. De repente, o seu código fala a língua do parceiro: usa os nomes dele, segue as estruturas dele, carrega as esquisitices dele. Pior, o seu domínio passa a ser refém do externo — quando o parceiro muda o formato, o seu domínio quebra. Cada sistema externo com que você integra dessa forma crava mais um pedaço de modelo alheio no seu coração. O que deveria ser uma conversa vira uma invasão.

O sintoma do contágio

Quando o seu domínio usa os nomes e estruturas de um sistema externo, o modelo dele invadiu o seu. Correção: uma camada anticorrupção que traduz o externo para o seu modelo na fronteira.

A Decisão

Toda integração externa passa por uma camada anticorrupção (ACL) que traduz o modelo do parceiro para o seu modelo interno. O caótico para na fronteira; o domínio permanece limpo e próprio.

A camada anticorrupção (ACL — Anti-Corruption Layer) é a fronteira que protege o domínio do mundo externo. Toda integração passa por ela: a ACL recebe os dados no modelo do sistema externo e os traduz para o modelo interno, limpo e próprio, antes que cheguem ao domínio. Da mesma forma, traduz as requisições do domínio de volta para o formato que o externo espera. O domínio nunca vê o modelo do parceiro — só vê o seu próprio. A consequência é dupla: o domínio permanece limpo, falando sua própria língua, e fica isolado das mudanças do externo. Quando o parceiro muda seu formato, apenas a ACL precisa ser ajustada; o domínio, protegido pela tradução, não é tocado. É a aplicação direta da regra de dependência (Cap II): o detalhe externo é mantido na borda, longe do núcleo.

Arquitetura lógica A camada anticorrupção como fronteira
SISTEMA EXTERNOmodelo estranho, caótico ACLtraduz externo → internoa fronteira que protege SEU DOMÍNIOmodelo limpo, próprio o modelo externo PARA na ACL — nunca entra no domínio mudou lá? só a ACL muda
Figura 6.1-a. A ACL traduz o modelo externo para o interno. O modelo caótico do parceiro para na fronteira; o domínio fica limpo. Quando o externo muda, só a ACL muda.

A tradução na fronteira

AclParceiro.phpPHP
// A ACL traduz o modelo EXTERNO para o modelo INTERNO. O domínio só vê o seu.
final class AclParceiro
{
    public function traduzirPedido(array $payloadExterno): Pedido
    {
        // O modelo externo é estranho: nomes e estruturas do parceiro.
        // A ACL o converte para o NOSSO modelo, limpo.
        return new Pedido(
            // 'cod_ped' externo → id interno; 'vlr_tot' → valor.
            id:      PedidoId::de($payloadExterno['cod_ped']),
            valor:   Dinheiro::deCentavos($payloadExterno['vlr_tot']),
            cliente: $this->traduzirCliente($payloadExterno['cli']),
        );
        // A partir daqui, o domínio NUNCA vê 'cod_ped' ou 'vlr_tot'.
    }
}
A ACL converte os nomes e formatos do parceiro para o modelo interno. O domínio recebe um Pedido limpo, sem vestígio do externo.
uso_isolado.phpPHP
// O domínio trabalha com o SEU modelo. A ACL ficou na fronteira.
final class ReceberPedidoDoParceiro
{
    public function executar(array $payloadExterno): void
    {
        // 1) A ACL traduz na entrada.
        $pedido = $this->acl->traduzirPedido($payloadExterno);

        // 2) O domínio opera no modelo PRÓPRIO — sem saber do parceiro.
        $this->pedidos->registrar($pedido);
        // Se o parceiro mudar 'cod_ped' para 'codigo', só a ACL muda.
    }
}
O caso de uso fala só o modelo interno. A dependência do formato externo ficou contida na ACL, longe do domínio.

A Verificação

AclTest.phpPHP
final class AclParceiroTest extends TestCase
{
    public function test_traduz_modelo_externo_para_interno(): void
    {
        // Entra o modelo externo (estranho)...
        $externo = ['cod_ped' => 'P-1', 'vlr_tot' => 15000, 'cli' => [...]];

        $pedido = $this->acl->traduzirPedido($externo);

        // ...sai o modelo interno (limpo). O domínio só conhece este.
        $this->assertSame('P-1', $pedido->id->valor());
        $this->assertEquals(Dinheiro::deCentavos(15000), $pedido->valor);
    }
}
O teste fixa o contrato da ACL: dado o modelo externo, ela produz o modelo interno esperado. A tradução é verificada.
Tabela de decisão Modelo externo direto vs ACL
AspectoExterno entra diretoCom ACL
Língua do domínioA do parceiroA sua, própria
Parceiro muda o formatoDomínio quebraSó a ACL muda
Esquisitices do externoVazam para o domínioParam na fronteira
Vários parceirosVários modelos no domínioUma ACL cada, domínio único

Estudo de Caso

Caso aplicado

O parceiro que mudou o formato sem quebrar o domínio

Contexto. A plataforma integra com um sistema parceiro cujo modelo de dados é próprio — com nomes e estruturas que fazem sentido para o parceiro, não para o domínio da plataforma. Deixar esse modelo entrar direto significaria cravar a língua do parceiro no coração do sistema.

Sem ACL
Mudança externa quebra o domínio
Com ACL
Mudança contida na fronteira
Ganho
Domínio imune ao externo

Decisão. Toda comunicação com o parceiro passa por uma camada anticorrupção, que traduz o modelo externo para o modelo interno na entrada e vice-versa na saída. O domínio só conhece o seu próprio modelo.

Resultado. Quando o parceiro alterou o formato de alguns campos, o impacto ficou inteiramente contido na ACL — uma única peça precisou ser ajustada, e o domínio sequer percebeu a mudança. A tradução na fronteira transformou uma alteração externa, que poderia ter rompido o sistema, num ajuste localizado e seguro.

Incidente Real

Severidade · Média

O modelo do parceiro que vazou para o domínio

  • Antes Numa integração inicial sem ACL, os dados do parceiro foram usados diretamente no domínio — com os nomes e a estrutura do parceiro.
  • Sintoma O domínio passou a falar a língua do parceiro. Quando o parceiro mudou um campo, a quebra se espalhou por vários pontos do domínio que dependiam daquele formato.
  • Diagnóstico A ausência de uma camada de tradução deixou o modelo externo contaminar o domínio, que ficou refém das decisões do parceiro.
  • Correção Introdução de uma ACL que traduz o modelo do parceiro para o interno. O domínio foi limpo de qualquer vestígio do formato externo.
  • Kaizen Vira regra: toda integração externa passa por uma ACL; o modelo de um parceiro nunca entra no domínio sem tradução.

Decisão Arquitetural

ADR-0030

Toda integração externa passa por uma camada anticorrupção

Aceito
Contexto

Sistemas externos têm modelos próprios, muitas vezes estranhos ou caóticos. Quando esse modelo entra direto no domínio, ele o contamina — o domínio passa a falar a língua do parceiro e quebra quando o parceiro muda.

Decisão

Toda integração externa passa por uma camada anticorrupção (ACL) que traduz o modelo externo para o modelo interno na entrada e vice-versa na saída. O domínio só conhece o seu próprio modelo. Quando o externo muda, apenas a ACL é ajustada. É a regra de dependência (Cap II) aplicada à integração.

Alternativas consideradas

Modelo externo entra direto — contaminação e refém do parceiro. Tradução espalhada pelo domínio — a lógica de conversão vaza. Um modelo "comum" imposto a todos os parceiros — irreal, cada um tem o seu.

Consequências
PositivasDomínio limpo, na própria língua. Isolado das mudanças do externo. Mudança de formato contida na ACL. Vários parceiros sem poluir o domínio.
NegativasExige escrever e manter uma ACL por integração e a disciplina de nunca deixar o externo passar direto.
RiscosACL desatualizada quebra a tradução; mitigada por testes de contrato que fixam o mapeamento externo↔interno.

Antipadrões

Modelo externo direto no domínio

Usar os nomes e estruturas do parceiro dentro do domínio. Correção: a ACL traduz na fronteira; o domínio só vê o modelo próprio.

Tradução espalhada pelo código

Converter o formato externo em vários pontos do domínio. Correção: centralizar a tradução numa ACL, na fronteira da integração.

Exercício

Encontre o modelo externo no seu domínio

  1. Liste as integrações externas do seu sistema. Para cada uma, o domínio usa o seu próprio modelo, ou os nomes e estruturas do sistema externo?
  2. Procure no domínio campos com nomes que vieram de um parceiro (abreviações estranhas, formatos herdados). Cada um é uma contaminação.
  3. Escolha a pior integração — aquela onde o modelo externo mais invadiu. Como uma ACL traduziria o externo para o seu modelo na fronteira?
  4. Faça o teste da mudança: se esse parceiro mudasse um campo amanhã, quantos pontos do seu domínio quebrariam? Com uma ACL, seria só uma.
Meta: identificar onde modelos externos contaminam o seu domínio — e planejar as camadas anticorrupção que o isolariam das mudanças dos parceiros.
✓ Checklist do tópico
  • Toda integração externa passa por uma camada anticorrupção
  • A ACL traduz o modelo externo para o modelo interno na fronteira
  • O domínio nunca usa os nomes ou estruturas do sistema externo
  • Quando o externo muda o formato, apenas a ACL é ajustada
  • O mapeamento externo↔interno é fixado por testes de contrato
Meta Dados · Engenharia de Plataformas · Capítulo VI · §6.1Próximo → 6.2 Idempotência
Capítulo VI · Integração e Ecossistema de Parceiros
6.2

Idempotência

Em sistemas distribuídos, a mesma mensagem chega mais de uma vez — a rede repete, o remetente reenvia por garantia. Se cada chegada gera um efeito, um pagamento vira três cobranças. A mensagem pode chegar muitas vezes; o efeito precisa acontecer uma só.

Pré-requisitos · 6.1 ACL · Cap II EventosConecta com · 6.3 Coletor · 6.5 ParceirosTempo de leitura · ~13 min

O Problema

Em sistemas distribuídos, a entrega de uma mensagem exatamente uma vez é uma garantia notoriamente difícil — na prática, quase sempre inviável. O que se tem é entrega ao menos uma vez: a rede pode duplicar uma mensagem, o remetente reenvia quando não recebe confirmação a tempo, uma falha parcial faz a mesma operação ser repetida por garantia. A consequência é inevitável: a mesma mensagem chega ao seu sistema mais de uma vez.

Se cada chegada de mensagem gera um efeito, a duplicação vira desastre. Uma mensagem que diz "cobre este cliente", entregue três vezes pela rede, gera três cobranças. Um evento de "dar baixa no estoque", repetido, zera o estoque indevidamente. Um documento processado duas vezes aparece duplicado. O problema não é teórico — é o tipo de falha que gera prejuízo financeiro real e perda de confiança. A mensagem vai chegar repetida; a questão é garantir que o efeito aconteça uma só vez.

A mensagem repetida que vira prejuízo

Numa entrega "ao menos uma vez", a mesma mensagem chega múltiplas vezes. Se cada chegada gera um efeito, um pagamento vira três cobranças. Correção: idempotência — o efeito acontece uma vez, por mais que a mensagem se repita.

A Decisão

Cada mensagem carrega uma chave de idempotência única. Antes de processar, verifica-se se aquela chave já foi processada; se sim, ignora-se. A mensagem chega muitas vezes; o efeito acontece uma só.

A idempotência garante que processar a mesma mensagem várias vezes tem o mesmo efeito que processá-la uma vez. O mecanismo é uma chave de idempotência: cada mensagem carrega um identificador único e estável. Antes de processar uma mensagem, o sistema verifica se aquela chave já foi processada. Se já foi, a mensagem é simplesmente ignorada — o efeito não se repete. Se é nova, ela é processada, e a chave é registrada como processada, na mesma transação do efeito (lembrando o padrão do Cap II). Assim, não importa quantas vezes a rede entregue a mesma mensagem: o efeito acontece exatamente uma vez. A entrega "ao menos uma vez" da infraestrutura é transformada, pela idempotência, num efeito "exatamente uma vez" para o negócio.

Diagrama conceitual A mesma mensagem, efeito único
mensagem #A1 mensagem #A1 mensagem #A1 a mesma, entregue 3x (rede repete) CHAVE DE IDEMPOTÊNCIA"já processei #A1?" EFEITO ÚNICOprocessado 1x só
Figura 6.2-a. A mesma mensagem chega três vezes; a chave de idempotência reconhece as repetições e o efeito acontece uma só vez. "Ao menos uma vez" vira "exatamente uma vez".

Verificar a chave antes de processar

idempotencia.sqlPostgreSQL
-- Registro das chaves já processadas. A chave é ÚNICA.
CREATE TABLE mensagem_processada (
    chave_idempotencia TEXT PRIMARY KEY,   -- identificador da mensagem
    processada_em      TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- A PRIMARY KEY garante: a mesma chave não entra duas vezes.
A chave é a chave primária: o banco impede, por construção, que a mesma mensagem seja registrada — e processada — duas vezes.
ProcessadorIdempotente.phpPHP
// O efeito acontece UMA vez, por mais que a mensagem se repita.
final class ProcessadorIdempotente
{
    public function processar(Mensagem $msg): void
    {
        $this->transacao->executar(function () use ($msg) {
            // Tenta registrar a chave. Se já existe, é repetição → ignora.
            if (! $this->registro->tentarMarcar($msg->chaveIdempotencia())) {
                return;  // já processada — efeito NÃO se repete
            }

            // Chave nova: processa o efeito NA MESMA transação da marca.
            // Ou ambos acontecem, ou nenhum (atomicidade, Cap II).
            $this->efeito->aplicar($msg);
        });
    }
}
A marca da chave e o efeito acontecem juntos, atomicamente. Uma repetição encontra a chave já marcada e retorna sem refazer o efeito.

A Verificação

IdempotenciaTest.phpPHP
final class IdempotenciaTest extends TestCase
{
    public function test_mensagem_repetida_tem_efeito_unico(): void
    {
        $efeito = new EfeitoContador();
        $proc = $this->processadorCom($efeito);
        $msg = Mensagem::comChave('A1');

        // A MESMA mensagem, processada três vezes (rede repetiu).
        $proc->processar($msg);
        $proc->processar($msg);
        $proc->processar($msg);

        // O efeito aconteceu UMA vez só.
        $this->assertSame(1, $efeito->vezesAplicado());
    }
}
O teste prova a garantia central: três entregas da mesma mensagem, um único efeito. A repetição da rede não vira repetição de efeito.
Tabela de decisão Sem idempotência vs com idempotência
SituaçãoSem idempotênciaCom idempotência
Mensagem entregue 3x3 efeitos1 efeito
Cobrança duplicada pela redeCliente cobrado 3xCobrado 1x
Reenvio por timeoutOperação repetidaRepetição ignorada
Garantia da infraestrutura"Ao menos uma vez" (repete)Efeito "exatamente uma vez"

Estudo de Caso

Caso aplicado

A integração que sobreviveu às repetições da rede

Contexto. A plataforma recebe mensagens de sistemas externos sobre eventos de negócio. Como toda integração distribuída, a entrega é "ao menos uma vez": a rede e os remetentes ocasionalmente entregam a mesma mensagem mais de uma vez, especialmente sob instabilidade.

Sem chave
Repetição da rede = efeito duplicado
Com chave
Repetição ignorada
Ganho
Efeito exatamente uma vez

Decisão. Cada mensagem passou a carregar uma chave de idempotência única. O processamento registra a chave e aplica o efeito na mesma transação; uma mensagem cuja chave já foi registrada é ignorada.

Resultado. As repetições inevitáveis da rede deixaram de causar efeitos duplicados. Uma mensagem de negócio entregue várias vezes — por reenvio ou duplicação — produz exatamente um efeito. A integração tornou-se robusta à realidade dos sistemas distribuídos, onde a mensagem chega repetida mas o efeito não pode se repetir.

Incidente Real

Severidade · Alta

O efeito que aconteceu duas vezes

  • Antes Numa integração sem idempotência, uma instabilidade de rede fez uma mensagem de negócio ser entregue duas vezes ao sistema.
  • Sintoma O efeito da mensagem foi aplicado duas vezes — gerando uma duplicação indevida que precisou ser detectada e corrigida manualmente, com impacto real.
  • Diagnóstico A ausência de uma chave de idempotência fez cada entrega da mesma mensagem gerar um efeito, transformando a repetição da rede em repetição de efeito.
  • Correção Introdução da chave de idempotência: cada mensagem é processada uma vez, e repetições são reconhecidas e ignoradas.
  • Kaizen Vira regra: toda mensagem de integração carrega chave de idempotência; o efeito é sempre "exatamente uma vez", por mais que a mensagem se repita.

Decisão Arquitetural

ADR-0031

Idempotência por chave: a mensagem chega muitas vezes, o efeito acontece uma

Aceito
Contexto

Sistemas distribuídos entregam mensagens "ao menos uma vez": a rede duplica, remetentes reenviam. Se cada chegada gera um efeito, a duplicação vira prejuízo — um pagamento vira três cobranças, um efeito de negócio se repete indevidamente.

Decisão

Cada mensagem carrega uma chave de idempotência única. Antes de processar, verifica-se se a chave já foi processada; se sim, ignora-se. A chave é registrada na mesma transação do efeito (atomicidade). O efeito acontece exatamente uma vez, por mais que a mensagem se repita.

Alternativas consideradas

Processar toda chegada — efeitos duplicados e prejuízo. Tentar entrega "exatamente uma vez" na infra — quase sempre inviável. Deduplicar por conteúdo — frágil; a chave explícita é confiável.

Consequências
PositivasEfeito "exatamente uma vez" sobre entrega "ao menos uma vez". Robustez à repetição da rede. Sem cobranças nem efeitos duplicados.
NegativasExige uma chave estável por mensagem e o registro transacional das chaves processadas.
RiscosChaves crescem com o tempo; mitigado por retenção adequada e por chaves bem definidas.

Antipadrões

Processar toda chegada de mensagem

Aplicar o efeito a cada entrega, mesmo repetida. Um pagamento vira três cobranças. Correção: chave de idempotência; repetição é ignorada.

Marcar a chave fora da transação do efeito

Registrar a chave separada do efeito abre janela para inconsistência. Correção: marca e efeito na mesma transação (Cap II).

Exercício

Proteja uma operação contra repetição

  1. Liste as mensagens/eventos que o seu sistema recebe de fora. A entrega é garantidamente única, ou pode haver repetição (reenvio, duplicação de rede)?
  2. Para uma operação com efeito sensível (cobrança, baixa de estoque, envio), pergunte: o que acontece se a mesma mensagem chegar duas vezes? O efeito duplica?
  3. Identifique a chave de idempotência possível: que identificador único e estável cada mensagem carrega (ou poderia carregar)?
  4. Esboce o fluxo idempotente: verificar a chave, processar se nova, registrar a chave na mesma transação do efeito.
Meta: tornar pelo menos uma operação sensível do seu sistema idempotente — para que a repetição inevitável das mensagens não vire repetição de efeito.
✓ Checklist do tópico
  • Cada mensagem de integração carrega uma chave de idempotência única
  • Antes de processar, verifica-se se a chave já foi processada
  • Mensagens repetidas (mesma chave) são ignoradas
  • A chave é registrada na mesma transação do efeito
  • O efeito acontece exatamente uma vez, por mais que a mensagem se repita
Meta Dados · Engenharia de Plataformas · Capítulo VI · §6.2Próximo → 6.3 O Coletor de Documentos
Capítulo VI · Integração e Ecossistema de Parceiros
6.3

O Coletor de Documentos

A plataforma captura documentos fiscais eletrônicos de um provedor externo. O erro fácil é acoplar o coletor ao provedor — e, no dia em que o provedor mudar ou cair, reescrever tudo. O coletor depende de um contrato abstrato; o provedor é um detalhe trocável.

Pré-requisitos · 6.1 ACL · 6.2 IdempotênciaConecta com · 6.5 Parceiros · Cap IV Event StoreTempo de leitura · ~13 min

O Problema

A plataforma precisa capturar documentos fiscais eletrônicos — notas, conhecimentos de transporte, manifestos — que são disponibilizados por um provedor externo. A forma mais rápida de fazer isso funcionar é também a mais perigosa: escrever o coletor falando diretamente com a API daquele provedor específico, usando seus endpoints, seus formatos, suas peculiaridades, espalhados pelo código do coletor.

O problema aparece — e sempre aparece — quando o provedor muda. Ele altera sua API, e o coletor quebra. Ele aumenta o preço, e você quer trocar de provedor, mas não pode sem reescrever o coletor inteiro. Ele sai do ar, e você fica sem alternativa porque todo o seu código assume aquele provedor. Acoplar o coletor a um provedor concreto é apostar que esse provedor nunca mudará, nunca encarecerá e nunca falhará — uma aposta que sempre se perde. O documento que você precisa coletar é estável; o provedor por onde o coleta, não.

O sintoma do acoplamento ao provedor

Quando o código do coletor está cheio dos endpoints e formatos de um provedor específico, trocá-lo exige reescrever tudo. Correção: o coletor depende de uma interface abstrata; o provedor é uma implementação trocável.

A Decisão

O coletor depende de uma interface abstrata — ProvedorDeDocumentos — não de um provedor concreto. Trocar de provedor, ou usar vários, é trocar a implementação; o coletor não muda.

O coletor é construído sobre uma abstração de provedor: uma interface ProvedorDeDocumentos que declara o que o coletor precisa — "buscar documentos disponíveis", "baixar um documento" — em termos do domínio, não de um provedor específico. Cada provedor real é uma implementação concreta dessa interface, e é nela que vivem os endpoints, formatos e peculiaridades daquele provedor (traduzidos para o modelo interno pela ACL, §6.1). O coletor depende apenas da interface; ele não sabe qual provedor está por trás. Trocar de provedor é escrever outra implementação da mesma interface — o coletor não é tocado. Usar vários provedores ao mesmo tempo, ou ter um de reserva para quando o principal falhar, torna-se natural. É a regra de dependência (Cap II) aplicada à captura: o provedor concreto é mantido na borda, e o coletor, no núcleo, depende só da abstração.

Arquitetura lógica O coletor sobre uma abstração de provedor
COLETOR (seu domínio)depende da INTERFACE, não do provedor interface ProvedorDeDocumentoso contrato abstrato Provedor Aimplementação concreta Provedor Btroca sem tocar o coletor Provedor Cfuturo, sem reescrever
Figura 6.3-a. O coletor depende da interface ProvedorDeDocumentos, não de um provedor concreto. Cada provedor é uma implementação trocável — mudar de provedor não toca o coletor.

A interface abstrata e o coletor

ProvedorDeDocumentos.phpPHP
// O contrato ABSTRATO. Fala a língua do domínio, não de um provedor.
interface ProvedorDeDocumentos
{
    /** @return ReferenciaDeDocumento[] */
    public function documentosDisponiveis(Periodo $periodo): array;

    public function baixar(ReferenciaDeDocumento $ref): DocumentoFiscal;
}
A interface declara o que o coletor precisa, em termos do domínio. Nenhum endpoint, nenhum formato de provedor — só o contrato.
ColetorDeDocumentos.phpPHP
// O coletor depende da INTERFACE. Não sabe qual provedor está por trás.
final class ColetorDeDocumentos
{
    public function __construct(
        private readonly ProvedorDeDocumentos $provedor,  // abstração
        private readonly ProcessadorIdempotente $idempotente,  // §6.2
    ) {}

    public function coletar(Periodo $periodo): void
    {
        foreach ($this->provedor->documentosDisponiveis($periodo) as $ref) {
            $doc = $this->provedor->baixar($ref);
            // Idempotência (§6.2): o mesmo documento não é coletado 2x.
            $this->idempotente->processar(Mensagem::deDocumento($doc));
        }
    }
}
O coletor combina os tópicos: depende da abstração de provedor e usa a idempotência para não duplicar documentos repetidos.
ProvedorConcretoA.phpPHP
// A implementação concreta: AQUI vivem os detalhes do provedor real.
final class ProvedorConcretoA implements ProvedorDeDocumentos
{
    public function baixar(ReferenciaDeDocumento $ref): DocumentoFiscal
    {
        // Endpoints, formato e peculiaridades do provedor A — contidos aqui.
        $bruto = $this->http->get("/api/a/docs/{$ref->codigo()}");
        // A ACL (§6.1) traduz o formato do provedor para o modelo interno.
        return $this->acl->traduzirDocumento($bruto);
    }
    // Trocar para o Provedor B = outra classe; o coletor não muda.
}
Os detalhes do provedor real ficam confinados na implementação concreta. Trocar de provedor é escrever outra classe como esta.

A Verificação

ColetorTest.phpPHP
final class ColetorDeDocumentosTest extends TestCase
{
    public function test_coletor_funciona_com_qualquer_provedor(): void
    {
        // Um provedor falso (dublê) prova que o coletor depende só da interface.
        $provedorFalso = new ProvedorDeDocumentosFalso([$this->documento('D1')]);
        $coletor = $this->coletorCom($provedorFalso);

        $coletor->coletar(Periodo::hoje());

        // O documento foi coletado — sem nenhum provedor real envolvido.
        $this->assertTrue($this->foiColetado('D1'));
    }
}
O teste usa um provedor falso, provando que o coletor depende só da interface. Trocar o provedor real não exige tocar o coletor nem o teste.
Tabela de decisão Acoplado ao provedor vs abstração
SituaçãoAcoplado a um provedorSobre uma abstração
Provedor muda a APIColetor quebra, reescreveSó a implementação muda
Trocar de provedorReescrever o coletorOutra implementação
Provedor sai do arSem alternativaUsar um de reserva
Vários provedoresInviávelNatural

Estudo de Caso

Caso aplicado

O coletor que não depende do provedor

Contexto. A plataforma captura documentos fiscais eletrônicos de um provedor externo. Acoplar o coletor diretamente à API daquele provedor significaria que qualquer mudança nele — de formato, de preço, de disponibilidade — exigiria reescrever o coletor.

Acoplado
Mudar provedor = reescrever
Abstração
Provedor é implementação trocável
Ganho
Coletor imune ao provedor

Decisão. Construir o coletor sobre uma interface abstrata, ProvedorDeDocumentos, que fala a língua do domínio. O provedor concreto vira uma implementação dessa interface, com a ACL (§6.1) traduzindo o formato e a idempotência (§6.2) evitando duplicações.

Resultado. O coletor passou a depender apenas do contrato abstrato, não do provedor concreto. Trocar de provedor, ter um de reserva, ou suportar vários tornou-se uma questão de escrever implementações da mesma interface — sem nunca tocar o coletor. O documento a coletar é estável; o provedor, agora, é um detalhe trocável.

Incidente Real

Severidade · Média

O provedor que mudou e parou a coleta

  • Antes Numa versão acoplada, o coletor falava diretamente com a API de um provedor de documentos, com seus endpoints e formatos espalhados pelo código.
  • Sintoma O provedor alterou seu formato, e a coleta parou — a quebra atingiu vários pontos do coletor que dependiam do formato anterior daquele provedor.
  • Diagnóstico O acoplamento direto ao provedor concreto fez uma mudança externa derrubar o coletor inteiro, sem possibilidade de trocar rapidamente de provedor.
  • Correção Refatoração para uma interface abstrata de provedor, com o provedor concreto isolado numa implementação e a tradução na ACL. O coletor passou a depender só do contrato.
  • Kaizen Vira regra: o coletor depende de uma abstração de provedor; nenhum provedor concreto é referenciado diretamente no domínio do coletor.

Decisão Arquitetural

ADR-0032

O coletor depende de uma abstração de provedor, não do provedor concreto

Aceito
Contexto

A captura de documentos de um provedor externo, se acoplada à API daquele provedor, quebra quando ele muda, impede a troca quando ele encarece, e deixa sem alternativa quando ele cai. O documento é estável; o provedor não.

Decisão

O coletor depende de uma interface abstrata (ProvedorDeDocumentos) que fala a língua do domínio. Cada provedor real é uma implementação concreta, com seus detalhes isolados e traduzidos pela ACL (§6.1). Trocar, reservar ou somar provedores é trocar a implementação; o coletor não muda. Usa idempotência (§6.2) contra duplicação.

Alternativas consideradas

Acoplar ao provedor concreto — refém de suas mudanças e disponibilidade. Abstração que vaza detalhes do provedor — acoplamento disfarçado. Um provedor único sem reserva — ponto único de falha.

Consequências
PositivasColetor imune às mudanças do provedor. Troca e reserva de provedores naturais. Vários provedores suportados. Detalhes confinados na borda.
NegativasExige projetar a interface em termos do domínio e uma implementação por provedor.
RiscosAbstração mal projetada limita os provedores; mitigada por modelá-la a partir da necessidade do domínio, não de um provedor.

Antipadrões

Coletor acoplado a um provedor concreto

Endpoints e formatos de um provedor específico espalhados no coletor. Correção: depender de uma interface abstrata; o provedor é uma implementação.

Abstração que vaza o provedor

A interface expor conceitos de um provedor específico. Correção: a interface fala a língua do domínio; o provedor concreto traduz na ACL.

Exercício

Abstraia um provedor externo

  1. Identifique um provedor externo do qual o seu sistema depende (de dados, de documentos, de algum serviço). O seu código fala diretamente com a API dele, ou com uma abstração?
  2. Faça o teste do divórcio: se você precisasse trocar esse provedor amanhã, quanto código mudaria? Está confinado a um lugar, ou espalhado?
  3. Desenhe a interface abstrata: o que o seu domínio realmente precisa desse provedor, em termos próprios (não nos termos do provedor)?
  4. Pense na resiliência: com a abstração, seria viável ter um provedor de reserva para quando o principal falhar?
Meta: abstrair um provedor externo atrás de uma interface própria — para que trocá-lo, reservá-lo ou somá-lo não exija reescrever o seu sistema.
✓ Checklist do tópico
  • O coletor depende de uma interface abstrata de provedor
  • Nenhum provedor concreto é referenciado diretamente no domínio do coletor
  • Cada provedor real é uma implementação isolada da interface
  • Os detalhes do provedor são traduzidos pela ACL (§6.1)
  • Trocar, reservar ou somar provedores não toca o coletor
Meta Dados · Engenharia de Plataformas · Capítulo VI · §6.3Próximo → 6.4 O Workflow Engine
Capítulo VI · Integração e Ecossistema de Parceiros
6.4

O Workflow Engine

Um fluxo de negócio tem ordem, condições, ramificações e o que fazer quando algo falha no meio. Se essa orquestração for escrita no código de cada caso de uso, ela fica espalhada e rígida — e o analista, que entende o fluxo, não consegue mudá-lo.

Pré-requisitos · Cap II Eventos · Cap III BREConecta com · 6.2 Idempotência · Cap IV Event StoreTempo de leitura · ~14 min

O Problema

Um processo de negócio raramente é um único disparo. É uma sequência: um documento é emitido, então é validado, então uma notificação é enviada, então um sistema parceiro é atualizado, então a auditoria é registrada. E é mais que uma sequência linear — tem ordem que importa, condições que decidem caminhos, ramificações que divergem, e a questão espinhosa do que fazer quando algo falha no meio. O Capítulo II estabeleceu os eventos de domínio, mas um fluxo de negócio é mais que reagir a um evento.

Quando essa orquestração é escrita no código de cada caso de uso, dois problemas surgem. Primeiro, ela fica espalhada e rígida: a lógica do fluxo se dilui entre os casos de uso, difícil de ver como um todo e arriscada de mudar. Segundo, e mais limitante, o analista de negócio fica de fora: é ele quem entende o processo, quem sabe que um passo deveria vir antes de outro, ou que uma condição mudou — mas, com o fluxo cravado em código, ele não pode alterá-lo. Cada ajuste no processo de negócio vira um pedido à engenharia, um ciclo, um deploy. O fluxo, que é conhecimento de negócio, fica preso no código.

O sintoma do fluxo cravado

Quando a ordem dos passos de um processo está espalhada pelo código dos casos de uso, mudá-lo é arriscado e exige um desenvolvedor. Correção: o fluxo vira dado que um motor executa e o analista edita.

A Decisão

O fluxo é descrito como dado — passos, ordem, condições, ramificações e compensação. Um Workflow Engine o executa. O analista de negócio edita o fluxo sem depender de deploy.

O Workflow Engine trata a orquestração como dado, no espírito da plataforma orientada por metadados (Cap III). Um fluxo é descrito declarativamente: a sequência de passos (cada um uma ação nomeada, Cap III §3.2), a ordem entre eles, as condições que decidem caminhos, as ramificações. O motor lê essa descrição e a executa. Crucialmente, o motor também cuida da compensação: se um passo falha no meio do fluxo, ele desfaz os passos já realizados, na ordem inversa, em vez de deixar o sistema num estado inconsistente pela metade. Como o fluxo é dado, o analista de negócio — que detém o conhecimento do processo — pode alterá-lo diretamente: mudar a ordem, ajustar uma condição, adicionar um passo, sem depender de um ciclo de desenvolvimento. A lógica de orquestração, antes espalhada e rígida, fica centralizada no motor e flexível no dado.

Fluxograma operacional O fluxo declarativo e sua compensação
O FLUXO é DADO: passos, ordem, condições — o analista edita Validar Notificar Atualizar Auditar Falhou no meio? COMPENSA os passos já feitosdesfaz na ordem inversa — não deixa estado pela metade o MOTOR executa; a lógica não está espalhada no código
Figura 6.4-a. O fluxo — passos, ordem, condições — é dado que o motor executa. Se algo falha no meio, o motor compensa os passos já feitos, sem deixar estado pela metade.

O fluxo descrito como dado

workflow.sqlPostgreSQL
-- O fluxo é DADO: passos ordenados, cada um uma ação nomeada (§3.2).
CREATE TABLE workflow_passo (
    id          BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    workflow    TEXT NOT NULL,        -- 'emissao_documento'
    ordem       INT  NOT NULL,        -- 1, 2, 3...
    acao        TEXT NOT NULL,        -- 'validar' (ação nomeada)
    acao_compensacao TEXT,             -- 'desfazer_validacao' (rollback)
    condicao    TEXT                     -- opcional: só executa se...
);
-- O analista edita ESTA tabela. Mudar o fluxo não exige deploy.
Os passos do fluxo são linhas: ordem, ação a executar, ação de compensação. O analista altera o processo editando dado, não código.
WorkflowEngine.phpPHP
// O motor executa o fluxo e COMPENSA se algo falhar no meio.
final class WorkflowEngine
{
    public function executar(string $workflow, Contexto $ctx): void
    {
        $feitos = [];
        try {
            foreach ($this->passosOrdenados($workflow) as $passo) {
                if ($passo->condicaoSatisfeita($ctx)) {
                    $this->acoes->resolver($passo->acao)->executar($ctx);
                    $feitos[] = $passo;  // lembra o que já fez
                }
            }
        } catch (PassoFalhou $e) {
            // Falhou no meio: COMPENSA os feitos, na ordem INVERSA.
            foreach (array_reverse($feitos) as $passo) {
                $this->compensar($passo, $ctx);  // desfaz
            }
            throw $e;
        }
    }
}
O motor executa os passos na ordem e guarda o que fez. Se um falha, ele desfaz os anteriores em ordem inversa — nada fica pela metade.

A Verificação

WorkflowEngineTest.phpPHP
final class WorkflowEngineTest extends TestCase
{
    public function test_falha_no_meio_compensa_os_passos_feitos(): void
    {
        // Fluxo de 3 passos; o 3º falha.
        $engine = $this->engineComFluxo(['p1', 'p2', 'p3_que_falha']);

        try { $engine->executar('fluxo', $this->ctx()); } catch (PassoFalhou) {}

        // p1 e p2 foram COMPENSADOS, na ordem inversa (p2, depois p1).
        $this->assertSame(['compensar_p2', 'compensar_p1'], $this->compensacoesAplicadas());
    }
}
O teste prova a propriedade crítica: ao falhar no meio, os passos já feitos são compensados na ordem inversa — o sistema não fica inconsistente.
Tabela de decisão Fluxo no código vs no Workflow Engine
AspectoFluxo cravado no códigoWorkflow Engine (dado)
Mudar a ordem dos passosDeployEditar o dado
Quem altera o fluxoDesenvolvedorAnalista de negócio
Ver o fluxo como um todoEspalhadoDescrição declarativa única
Falha no meioEstado pela metadeCompensação automática

Estudo de Caso

Caso aplicado

O fluxo de emissão que o analista ajusta sozinho

Contexto. Um processo de negócio encadeava vários passos após a emissão de um documento — validação, notificações, atualização de um sistema parceiro, registro de auditoria. A ordem e as condições desses passos mudavam conforme a necessidade do negócio.

Fluxo no código
Cada ajuste = deploy
Workflow Engine
Analista edita o dado
Ganho
Orquestração flexível e consistente

Decisão. Descrever o fluxo como dado num Workflow Engine: cada passo uma ação nomeada, com ordem, condições e ações de compensação. O motor executa o fluxo e, em caso de falha no meio, compensa os passos já realizados.

Resultado. Quando o negócio precisa ajustar o processo — mudar a ordem de dois passos, adicionar uma notificação, condicionar uma etapa —, o analista altera o fluxo diretamente, sem um ciclo de desenvolvimento. E uma falha no meio do fluxo não deixa mais o sistema num estado inconsistente: a compensação desfaz o que foi feito. A orquestração saiu do código rígido para o dado flexível.

Incidente Real

Severidade · Alta

O fluxo que falhou no meio e deixou tudo pela metade

  • Antes Num processo orquestrado em código, sem compensação, uma sequência de passos foi interrompida por uma falha no meio do caminho.
  • Sintoma Os primeiros passos já tinham alterado o estado do sistema, mas os seguintes não rodaram — deixando o sistema num estado inconsistente, pela metade, difícil de detectar e corrigir.
  • Diagnóstico A orquestração no código não previa compensação: uma falha no meio não desfazia os passos já realizados.
  • Correção Adoção do Workflow Engine com compensação: cada passo tem sua ação de desfazer, e uma falha no meio dispara a compensação na ordem inversa.
  • Kaizen Vira regra: fluxos de negócio rodam no Workflow Engine com compensação; uma falha no meio nunca deixa estado pela metade.

Decisão Arquitetural

ADR-0033

Orquestração declarativa no Workflow Engine, com compensação

Aceito
Contexto

Um fluxo de negócio tem ordem, condições, ramificações e falhas no meio. Cravado no código dos casos de uso, fica espalhado e rígido, e o analista que entende o processo não pode alterá-lo — cada ajuste vira deploy.

Decisão

O fluxo é descrito como dado (passos, ordem, condições, ramificações, compensação); um Workflow Engine o executa. Cada passo é uma ação nomeada (§3.2). Se um passo falha no meio, o motor compensa os já feitos na ordem inversa. O analista edita o fluxo sem deploy.

Alternativas consideradas

Fluxo no código — espalhado, rígido, sem o analista. Fluxo como dado sem compensação — falha no meio deixa estado pela metade. Linguagem de fluxo livre demais — vira a linguagem acidental (Cap III).

Consequências
PositivasAnalista ajusta o processo sem deploy. Fluxo visível como um todo. Falha no meio não deixa inconsistência. Orquestração centralizada.
NegativasExige o motor de execução, a compensação por passo e uma forma de fluxo contida.
RiscosCompensação é mais difícil que execução; mitigada por definir a ação de desfazer junto com cada passo.

Antipadrões

Fluxo de negócio cravado no código

A orquestração espalhada pelos casos de uso, mutável só por deploy. Correção: o fluxo vira dado que um motor executa e o analista edita.

Fluxo sem compensação

Uma falha no meio deixa o sistema pela metade. Correção: cada passo tem sua compensação; o motor desfaz os feitos na ordem inversa.

Exercício

Modele um fluxo de negócio como dado

  1. Escolha um processo do seu sistema com vários passos encadeados (ex.: aprovação, emissão, onboarding). Hoje, a ordem desses passos vive no código ou em algum lugar editável?
  2. Faça o teste da mudança: se o negócio quisesse inverter dois passos ou adicionar uma etapa, isso exigiria um desenvolvedor e um deploy?
  3. Faça o teste da falha: se o processo falhasse no terceiro de cinco passos, o que aconteceria com os dois primeiros já executados? O sistema ficaria consistente?
  4. Esboce o fluxo como dado: os passos, sua ordem, suas condições e — para cada um — qual seria a ação de compensação.
Meta: modelar um processo de negócio real como um fluxo declarativo com compensação — para que o analista o ajuste sem deploy e uma falha no meio nunca deixe o sistema pela metade.
✓ Checklist do tópico
  • Os fluxos de negócio são descritos como dado (passos, ordem, condições)
  • Um Workflow Engine executa os fluxos; a lógica não está espalhada no código
  • Cada passo é uma ação nomeada (§3.2)
  • Uma falha no meio compensa os passos já feitos, em ordem inversa
  • O analista de negócio pode alterar o fluxo sem depender de deploy
Meta Dados · Engenharia de Plataformas · Capítulo VI · §6.4Próximo → 6.5 Protocolos e Parceiros
Capítulo VI · Integração e Ecossistema de Parceiros
6.5

Protocolos e Parceiros

A integração fala muitos protocolos, e a plataforma quer um ecossistema onde parceiros conectam seus sistemas. Se cada protocolo é tratado de um jeito e cada parceiro integra à sua maneira, a camada de integração vira uma colcha de retalhos impossível de manter, monitorar e proteger.

Pré-requisitos · 6.1 ACL · 6.3 ColetorConecta com · Cap VII Segurança · Cap VIII ObservabilidadeTempo de leitura · ~12 min

O Problema

Uma plataforma que integra com o mundo precisa falar muitos protocolos — REST, SOAP, SFTP, webhooks, filas de mensagens, streams de eventos. E quando a ambição é um ecossistema, onde parceiros conectam seus próprios sistemas livremente, a diversidade explode: cada parceiro traz seu protocolo, seu jeito de autenticar, suas peculiaridades. A forma natural — e desastrosa — de lidar com isso é tratar cada protocolo de modo ad hoc e deixar cada parceiro integrar à sua maneira.

O resultado é uma colcha de retalhos. A camada de integração vira um emaranhado de código específico por protocolo e por parceiro, sem padrão comum. E essa colcha é impossível de manter — cada mudança é um caso especial; impossível de monitorar — não há um ponto uniforme onde observar todas as integrações; e impossível de proteger — cada integração ad hoc é uma porta com sua própria fechadura, ou sem nenhuma. Quanto mais a plataforma cresce em protocolos e parceiros, mais a colcha de retalhos se torna ingovernável. Sem um padrão, o ecossistema colapsa sob seu próprio peso.

O sintoma da colcha de retalhos

Quando cada protocolo e cada parceiro têm seu próprio código sem padrão comum, a integração não pode ser mantida, monitorada nem protegida de forma uniforme. Correção: uma abstração de protocolo única, com cada protocolo e parceiro como implementação.

A Decisão

Todos os protocolos vivem atrás de uma interface uniforme. Cada protocolo, e cada parceiro, é uma implementação do mesmo contrato — monitorada, protegida e mantida de forma consistente. Um parceiro novo não é um retalho.

A plataforma define uma abstração de protocolo uniforme: uma interface que declara o essencial de qualquer integração — enviar e receber mensagens — independentemente do protocolo concreto. REST, SOAP, SFTP, webhook, fila, stream: cada um é uma implementação dessa mesma interface, com seus detalhes confinados ali (e o modelo externo traduzido pela ACL, §6.1). O domínio e os fluxos (§6.4) falam com a abstração, nunca com um protocolo específico. Da mesma forma, cada parceiro do ecossistema integra através desse contrato uniforme, em vez de inventar seu próprio jeito. A consequência é decisiva: como todas as integrações compartilham a mesma forma, elas podem ser monitoradas num ponto uniforme (Cap VIII), protegidas pelas mesmas políticas (Cap VII) e mantidas com um padrão comum. Um parceiro novo passa a ser uma nova implementação do contrato — não mais um retalho costurado à colcha. O ecossistema cresce sem virar caos.

Arquitetura lógica Protocolos e parceiros sob um contrato uniforme
REST SOAP SFTP Webhook Fila Stream interface Protocolo · contrato uniformeenviar / receber — igual para todos parceiro novo = nova implementação, não nova colchamonitorar, proteger e manter de forma uniforme
Figura 6.5-a. Todos os protocolos atrás de uma interface uniforme. Cada protocolo e cada parceiro é uma implementação do mesmo contrato — monitorável, protegível e mantível de forma consistente.

A abstração de protocolo

Protocolo.phpPHP
// O contrato UNIFORME. O domínio não conhece o protocolo concreto.
interface Protocolo
{
    public function enviar(Mensagem $mensagem, Destino $destino): Resultado;

    /** @return Mensagem[] */
    public function receber(Origem $origem): array;
}
A interface reduz toda integração ao essencial: enviar e receber. O protocolo concreto — REST, fila, stream — é detalhe da implementação.
protocolos_concretos.phpPHP
// Cada protocolo é uma IMPLEMENTAÇÃO do mesmo contrato.
final class ProtocoloRest implements Protocolo
{
    public function enviar(Mensagem $m, Destino $d): Resultado
    {
        // Detalhes de HTTP/REST — confinados AQUI.
        return $this->http->post($d->url(), $m->corpo());
    }
    // ... receber() ...
}

final class ProtocoloFila implements Protocolo
{
    public function enviar(Mensagem $m, Destino $d): Resultado
    {
        // Detalhes da fila de mensagens — confinados AQUI.
        return $this->fila->publicar($d->topico(), $m);
    }
    // Mesmo contrato do REST → tratados de forma uniforme.
}
REST e fila implementam a mesma interface. Seus detalhes ficam confinados; quem os usa os trata de forma idêntica.
integracao_uniforme.phpPHP
// O domínio fala com a ABSTRAÇÃO. Monitoramento e segurança, uniformes.
final class IntegracaoComParceiro
{
    public function __construct(private readonly Protocolo $protocolo) {}

    public function notificar(Mensagem $m, Destino $d): void
    {
        // Não importa o protocolo: o ponto de envio é UM só,
        // onde se observa (Cap VIII) e se aplica política (Cap VII).
        $this->protocolo->enviar($m, $d);
    }
}
O domínio envia pela abstração. Como o ponto é uniforme, monitoramento e segurança aplicam-se a todas as integrações de uma vez.

A Verificação

ProtocoloTest.phpPHP
final class ProtocoloUniformeTest extends TestCase
{
    /** @dataProvider todosOsProtocolos */
    public function test_todo_protocolo_cumpre_o_mesmo_contrato(Protocolo $protocolo): void
    {
        // O MESMO teste vale para REST, fila, stream... — contrato uniforme.
        $resultado = $protocolo->enviar($this->mensagem(), $this->destinoFalso());

        $this->assertTrue($resultado->sucesso());
    }
}
Um único teste, parametrizado por protocolo, vale para todos. O contrato uniforme permite verificar todas as integrações com a mesma régua.
Tabela de decisão Ad hoc vs abstração de protocolo
AspectoCada protocolo/parceiro ad hocAbstração uniforme
ManterCaso especial cada umPadrão comum
MonitorarSem ponto uniformeUm ponto, todas (Cap VIII)
ProtegerCada porta, sua fechaduraPolítica uniforme (Cap VII)
Parceiro novoMais um retalhoImplementação do contrato

Estudo de Caso

Caso aplicado

O ecossistema que cresceu sem virar caos

Contexto. A plataforma precisa falar diversos protocolos e abrir um ecossistema onde parceiros conectam seus sistemas. Tratar cada protocolo de forma ad hoc e cada parceiro à sua maneira transformaria a camada de integração numa colcha de retalhos ingovernável à medida que crescesse.

Colcha de retalhos
Cada parceiro um caso especial
Abstração uniforme
Parceiro é implementação do contrato
Ganho
Ecossistema sustentável

Decisão. Definir uma abstração de protocolo uniforme. Cada protocolo (REST, SOAP, fila, stream...) e cada parceiro vira uma implementação do mesmo contrato, com a ACL (§6.1) traduzindo modelos externos. O domínio fala apenas com a abstração.

Resultado. O ecossistema cresce sem virar caos. Adicionar um parceiro é escrever uma implementação do contrato, não costurar mais um retalho. E porque todas as integrações compartilham a mesma forma, elas são monitoradas num ponto uniforme, protegidas pelas mesmas políticas e mantidas com um padrão comum. A diversidade de protocolos deixou de ser ingovernável.

Incidente Real

Severidade · Média

A integração ad hoc que ninguém conseguia monitorar

  • Antes Numa fase sem abstração, cada integração com parceiro foi feita de um jeito, com seu próprio código de protocolo e autenticação, sem padrão comum.
  • Sintoma Quando foi preciso monitorar a saúde e aplicar políticas de segurança às integrações, não havia um ponto uniforme: cada integração ad hoc precisava ser tratada como caso especial.
  • Diagnóstico A falta de uma abstração de protocolo transformou as integrações numa colcha de retalhos impossível de monitorar e proteger de forma consistente.
  • Correção Introdução da abstração de protocolo uniforme. As integrações passaram a compartilhar a mesma forma, com um ponto único de observação e política.
  • Kaizen Vira regra: todo protocolo e todo parceiro implementam o contrato uniforme; nenhuma integração ad hoc fora do padrão.

Decisão Arquitetural

ADR-0034

Abstração de protocolo uniforme para protocolos e parceiros

Aceito
Contexto

A plataforma fala muitos protocolos e quer um ecossistema de parceiros. Tratar cada protocolo ad hoc e cada parceiro à sua maneira cria uma colcha de retalhos impossível de manter, monitorar e proteger, que piora à medida que cresce.

Decisão

Todos os protocolos vivem atrás de uma interface uniforme (enviar/receber). Cada protocolo e cada parceiro é uma implementação do mesmo contrato, com detalhes confinados e modelos traduzidos pela ACL (§6.1). O domínio fala com a abstração. Monitoramento (Cap VIII) e segurança (Cap VII) aplicam-se de forma uniforme. Um parceiro novo é uma implementação, não um retalho.

Alternativas consideradas

Cada protocolo/parceiro ad hoc — colcha de retalhos ingovernável. Abstração que vaza o protocolo — acoplamento disfarçado. Um único protocolo imposto a todos — irreal num ecossistema.

Consequências
PositivasIntegração mantível, monitorável e protegível de forma uniforme. Parceiro novo sem retalho. Ecossistema que cresce sem virar caos.
NegativasExige projetar a abstração no nível certo e uma implementação por protocolo/parceiro.
RiscosAbstração genérica demais perde especificidades de um protocolo; mitigada por permitir extensões controladas quando necessárias.

Antipadrões

Cada protocolo e parceiro tratado ad hoc

Código específico sem padrão comum, formando uma colcha de retalhos. Correção: uma abstração de protocolo uniforme; cada protocolo/parceiro é uma implementação.

Integração que pula a abstração

Um parceiro "especial" integrado fora do contrato uniforme. Correção: todo parceiro implementa o mesmo contrato; sem exceções fora do padrão.

Exercício

Unifique suas integrações

  1. Liste as integrações externas do seu sistema e os protocolos que elas usam. Cada uma tem seu próprio código, ou compartilham uma abstração comum?
  2. Faça o teste do monitoramento: existe um ponto uniforme onde você observa a saúde de todas as integrações? Ou cada uma é um caso à parte?
  3. Faça o teste da segurança: as mesmas políticas se aplicam a todas as integrações, ou cada uma tem (ou não tem) sua própria proteção?
  4. Esboce a abstração de protocolo: que contrato mínimo (enviar/receber) toda integração poderia compartilhar, com os detalhes de cada protocolo confinados numa implementação?
Meta: unificar as integrações do seu sistema sob uma abstração de protocolo comum — para que monitorar, proteger e adicionar parceiros deixe de produzir uma colcha de retalhos.
✓ Checklist do tópico
  • Todos os protocolos vivem atrás de uma interface uniforme
  • Cada protocolo e cada parceiro é uma implementação do mesmo contrato
  • O domínio fala com a abstração, não com um protocolo específico
  • As integrações são monitoradas e protegidas de forma uniforme
  • Um parceiro novo é uma implementação do contrato, não um retalho ad hoc
Meta Dados · Engenharia de Plataformas · Capítulo VI · §6.5Fim do Capítulo VI · voltar ao início
Meta Dados · Engenharia de Plataformas
Capítulo VII

Segurança,
Privacidade
e Conformidade

Proteger por construção, não por remendo: negar por padrão, guardar segredos fora do código, cifrar o que é sensível, respeitar os direitos do titular e estar pronto para o incidente.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo VII
Capítulo VII · Segurança, Privacidade e Conformidade
7.1

Menor Privilégio

A diferença entre permitir e negar por padrão é a diferença entre um esquecimento que abre uma porta e um esquecimento que tranca uma. A segurança não pode depender de lembrar de proibir; o padrão precisa ser o seguro.

Pré-requisitos · Cap I Capacidades · Cap IV TenantConecta com · 7.2 Segredos · Cap IV RLSTempo de leitura · ~13 min

O Problema

Toda regra de segurança será, em algum momento, esquecida. Alguém vai criar um novo endpoint e esquecer de protegê-lo, adicionar um recurso e esquecer de restringir quem o acessa, configurar um serviço e esquecer de limitar suas permissões. Isso não é pessimismo — é a realidade de sistemas construídos por pessoas ao longo do tempo. A pergunta que define a segurança de um sistema não é "como evitar o esquecimento?", mas "o que acontece quando o esquecimento ocorrer?".

E a resposta depende inteiramente de uma escolha de padrão. Num sistema que permite por padrão, esquecer de proibir deixa uma porta aberta: o endpoint desprotegido fica acessível a todos, o recurso sem restrição vaza, o erro humano vira uma vulnerabilidade explorável. Num sistema que nega por padrão, o mesmo esquecimento tranca a porta: o endpoint sem permissão explícita simplesmente não é acessível, e o erro humano falha de forma segura. A diferença entre os dois é a diferença entre um esquecimento que expõe e um esquecimento que protege.

O padrão que transforma erro em brecha

Quando o padrão é permitir, esquecer de proteger algo o deixa exposto — o erro humano vira vulnerabilidade. Correção: negar por padrão, para que o esquecimento tranque a porta em vez de abri-la.

A Decisão

O padrão é negar. Todo acesso é proibido a menos que explicitamente permitido, e cada um recebe apenas o privilégio mínimo de que precisa. O esquecimento falha seguro, não exposto.

A plataforma adota negar por padrão como princípio inviolável. Nenhum acesso é concedido implicitamente: um endpoint, um recurso, uma operação só são acessíveis se houver uma permissão explícita que os libere. Na ausência dessa permissão — inclusive por esquecimento —, o acesso é negado. Sobre essa base assenta o menor privilégio: cada usuário, cada serviço, cada processo recebe exatamente o conjunto mínimo de permissões necessárias para sua função, e nada além. Um serviço que só precisa ler não recebe permissão de escrever; um usuário que opera numa área não alcança as outras. A superfície de ataque — tudo o que um invasor poderia explorar — encolhe ao mínimo indispensável. E o mais importante: quando alguém esquece de configurar uma permissão, o resultado é um acesso bloqueado, que se percebe e corrige, não um acesso aberto, que se descobre tarde demais.

Diagrama conceitual Negar por padrão e menor privilégio
PERMITIR por padrão esquecer de proibir = porta aberta o erro vira vulnerabilidade NEGAR por padrão esquecer de permitir = acesso bloqueado o erro falha seguro MENOR PRIVILÉGIO cada um recebe só o acesso que precisa, nada além
Figura 7.1-a. Permitir por padrão transforma o esquecimento em porta aberta; negar por padrão o transforma em porta trancada. Sobre essa base, cada um recebe só o privilégio mínimo.

A negação como base da autorização

Autorizador.phpPHP
// O padrão é NEGAR. Só uma permissão explícita libera o acesso.
final class Autorizador
{
    public function pode(Usuario $usuario, string $acao, Recurso $recurso): bool
    {
        // Procura uma permissão EXPLÍCITA que conceda a ação.
        foreach ($usuario->permissoes() as $permissao) {
            if ($permissao->concede($acao, $recurso)) {
                return true;
            }
        }

        // Nenhuma permissão explícita → NEGA. (esquecer = bloquear, não vazar)
        return false;
    }
}
A última linha é a decisão de design: na ausência de permissão explícita, nega. O esquecimento de liberar bloqueia o acesso — falha seguro.
endpoint_protegido.phpPHP
// Todo endpoint exige permissão explícita. Sem ela, é negado.
final class AcaoProtegida
{
    public function executar(Requisicao $req): Resposta
    {
        // A guarda vem ANTES de qualquer lógica. Negar por padrão.
        if (! $this->autorizador->pode($req->usuario(), 'aprovar', $req->recurso())) {
            throw new AcessoNegado();  // 403
        }

        // Só chega aqui quem tem permissão EXPLÍCITA.
        return $this->aprovar($req);
    }
}
A guarda de autorização precede a lógica. Um endpoint novo sem permissão configurada nega o acesso — em vez de expô-lo por omissão.

A Verificação

MenorPrivilegioTest.phpPHP
final class MenorPrivilegioTest extends TestCase
{
    public function test_sem_permissao_explicita_o_acesso_e_negado(): void
    {
        // Um usuário SEM nenhuma permissão para a ação.
        $usuario = $this->usuarioSemPermissoes();

        // O padrão é negar: o acesso é bloqueado.
        $this->assertFalse($this->autorizador->pode($usuario, 'aprovar', $this->recurso()));
    }

    public function test_concede_apenas_o_explicitamente_permitido(): void
    {
        // Permissão SÓ para 'ler' — não para 'escrever'.
        $usuario = $this->usuarioComPermissao('ler');

        $this->assertTrue($this->autorizador->pode($usuario, 'ler', $this->recurso()));
        // Menor privilégio: o que não foi concedido é negado.
        $this->assertFalse($this->autorizador->pode($usuario, 'escrever', $this->recurso()));
    }
}
Os testes fixam os dois lados: sem permissão, nega; e concede apenas o que foi explicitamente liberado — quem pode ler não pode escrever.
Tabela de decisão Permitir por padrão vs negar por padrão
SituaçãoPermitir por padrãoNegar por padrão
Endpoint novo sem configExposto a todosBloqueado
Esquecer de restringirVazaFalha seguro
Serviço que só lêPode escrever tambémSó lê (menor privilégio)
Erro humanoVira vulnerabilidadeVira acesso bloqueado

Estudo de Caso

Caso aplicado

O esquecimento que trancou a porta em vez de abri-la

Contexto. Numa plataforma em crescimento, novos endpoints e recursos são adicionados continuamente, por pessoas diferentes. É inevitável que, em algum momento, alguém adicione algo e esqueça de configurar quem pode acessá-lo.

Permitir por padrão
Esquecimento expõe
Negar por padrão
Esquecimento bloqueia
Ganho
Erro humano falha seguro

Decisão. Adotar negar por padrão: nenhum acesso é concedido sem permissão explícita, e cada papel recebe apenas o privilégio mínimo. A guarda de autorização precede toda lógica.

Resultado. Quando um novo recurso foi adicionado sem a permissão configurada — o esquecimento inevitável —, o resultado foi um acesso bloqueado, rapidamente notado e corrigido, em vez de um recurso exposto silenciosamente. O mesmo erro humano que, num sistema de permitir por padrão, teria sido uma brecha, tornou-se apenas um ajuste de configuração. Negar por padrão transformou a falha em segurança.

Incidente Real

Severidade · Alta

O acesso amplo demais que ninguém tinha questionado

  • Antes Um serviço havia recebido, por conveniência inicial, permissões mais amplas do que precisava — acesso de escrita onde só lia, alcance a recursos que não usava.
  • Risco Esse privilégio excessivo ampliava a superfície de ataque: se o serviço fosse comprometido, o invasor herdaria muito mais acesso do que a função exigia.
  • Diagnóstico A ausência do princípio de menor privilégio deixou permissões concedidas "por garantia", muito além do necessário.
  • Correção Redução das permissões ao mínimo estrito da função do serviço. O acesso de escrita não usado foi removido; o alcance, restringido ao indispensável.
  • Kaizen Vira regra: cada serviço e papel recebe só o privilégio mínimo; permissões amplas "por garantia" são proibidas e auditadas.

Decisão Arquitetural

ADR-0035

Negar por padrão e menor privilégio

Aceito
Contexto

Toda regra de segurança será, em algum momento, esquecida. Num sistema que permite por padrão, esse esquecimento expõe recursos — o erro humano vira vulnerabilidade. A segurança não pode depender de nunca esquecer.

Decisão

O padrão é negar: nenhum acesso é concedido sem permissão explícita; na ausência dela, nega-se. Sobre essa base, aplica-se menor privilégio: cada usuário, serviço e processo recebe só as permissões mínimas de que precisa. O esquecimento falha seguro (bloqueia), e a superfície de ataque encolhe ao mínimo.

Alternativas consideradas

Permitir por padrão — esquecimento expõe; erro vira brecha. Permissões amplas "por garantia" — superfície de ataque inflada. Autorização opcional por endpoint — o que se esquece de proteger vaza.

Consequências
PositivasErro humano falha seguro. Superfície de ataque mínima. Comprometimento limitado ao privilégio concedido. Segurança não depende de lembrar.
NegativasExige conceder permissões deliberadamente e a disciplina de negar por padrão em toda a stack.
RiscosNegar por padrão pode bloquear acessos legítimos não previstos; aceitável, pois falha seguro e se corrige concedendo explicitamente.

Antipadrões

Permitir por padrão

Conceder acesso na ausência de regra. Esquecer de proteger expõe. Correção: negar por padrão; só permissão explícita libera.

Permissões amplas "por garantia"

Conceder mais acesso do que a função precisa, por conveniência. Correção: menor privilégio — só o mínimo necessário, nada além.

Exercício

Teste o padrão de acesso do seu sistema

  1. No seu sistema, o que acontece quando alguém cria um endpoint ou recurso e esquece de configurar a autorização? Ele fica exposto, ou bloqueado?
  2. Faça o teste do esquecimento: o padrão é permitir (esquecer expõe) ou negar (esquecer bloqueia)? Essa é a pergunta que define sua postura de segurança.
  3. Escolha um serviço do seu sistema. Liste as permissões que ele tem e as que ele realmente usa. Há privilégios concedidos "por garantia" que poderiam ser removidos?
  4. Esboce a guarda de autorização que nega por padrão: como garantir que nenhuma operação seja acessível sem uma permissão explícita?
Meta: avaliar se o seu sistema nega ou permite por padrão — e reduzir os privilégios ao mínimo necessário, para que o esquecimento inevitável falhe seguro.
✓ Checklist do tópico
  • O padrão é negar: sem permissão explícita, o acesso é bloqueado
  • A guarda de autorização precede a lógica de cada operação
  • Cada usuário, serviço e processo recebe só o privilégio mínimo
  • Não há permissões amplas concedidas "por garantia"
  • Um endpoint novo sem permissão configurada nega o acesso
Meta Dados · Engenharia de Plataformas · Capítulo VII · §7.1Próximo → 7.2 Cofre de Segredos
Capítulo VII · Segurança, Privacidade e Conformidade
7.2

Cofre de Segredos

Uma senha de banco no código, uma chave de API num arquivo de configuração versionado — e o segredo vaza para sempre no histórico do repositório. Segredos não pertencem ao código; pertencem a um cofre, buscados em runtime.

Pré-requisitos · 7.1 Menor privilégioConecta com · 7.3 Criptografia · Cap VIII OperaçõesTempo de leitura · ~12 min

O Problema

Todo sistema tem segredos: a senha do banco de dados, a chave de uma API externa, o token que autentica uma integração, a chave que cifra dados. O caminho mais fácil — e mais perigoso — é colocá-los onde o código os encontra facilmente: cravados numa constante, escritos num arquivo de configuração que é versionado junto com o código. Funciona de imediato. E é uma bomba-relógio.

O problema é que um segredo colocado no código ou num arquivo versionado vaza para sempre. No momento em que é commitado, ele entra no histórico do repositório — e o histórico é permanente. Removê-lo depois não adianta: ele continua lá, em algum commit anterior, acessível a qualquer pessoa que tenha clonado o repositório ou que venha a ter acesso a ele. Um repositório que vai parar em mãos erradas entrega, junto, todos os segredos que já passaram por ele. E mesmo internamente, segredos no código são vistos por todo desenvolvedor, copiados para máquinas locais, espalhados sem controle. O segredo deixou de ser secreto no instante em que virou código.

O segredo que vaza para sempre

Um segredo commitado entra no histórico permanente do repositório — removê-lo depois não o apaga do passado. Correção: segredos nunca no código; vivem num cofre, buscados em runtime.

A Decisão

Segredos nunca ficam no código ou em arquivos versionados. Eles vivem num cofre de segredos, buscados em runtime, com acesso controlado, auditado e rotação sem mudança de código.

A plataforma adota um cofre de segredos como o único lugar onde segredos vivem. O código nunca contém senhas, chaves ou tokens; em vez disso, ele os busca no cofre em tempo de execução, quando precisa deles. O cofre oferece o que o código jamais poderia: acesso controlado (só quem tem permissão — menor privilégio, §7.1 — obtém cada segredo), auditoria (cada acesso a um segredo é registrado), e rotação (um segredo pode ser trocado no cofre sem alterar nem reimplantar o código). Os arquivos de configuração versionados contêm apenas referências a segredos ("a senha do banco está no cofre, sob este nome"), nunca os segredos em si. Assim, o repositório pode ser clonado, vazado ou auditado sem expor nenhum segredo — porque nenhum segredo jamais esteve nele.

Arquitetura lógica O cofre como única morada dos segredos
SEGREDO no código/repositóriovaza para sempre no histórico SEGREDO no cofrebuscado em runtime, rotacionável COFRE DE SEGREDOSacesso controlado · auditado · rotação ✕ nunca aqui
Figura 7.2-a. O segredo nunca toca o código ou o repositório; vive no cofre, buscado em runtime, com acesso controlado, auditado e rotacionável. O repositório pode vazar sem entregar segredos.

Buscar o segredo no cofre, nunca no código

CofreDeSegredos.phpPHP
// O segredo é BUSCADO no cofre em runtime. Nunca está no código.
interface CofreDeSegredos
{
    // Acesso controlado (§7.1) e auditado por trás desta chamada.
    public function obter(string $nome): Segredo;
}

// Uso: o código pede o segredo pelo NOME, não o contém.
final class ConexaoComBanco
{
    public function __construct(private readonly CofreDeSegredos $cofre) {}

    public function conectar(): Conexao
    {
        // A senha vem do cofre, em tempo de execução. Jamais hardcoded.
        $senha = $this->cofre->obter('banco/senha');
        return Conexao::abrir($this->host, $senha);
    }
}
O código referencia o segredo pelo nome e o busca no cofre. A senha nunca aparece no fonte — nem hoje, nem no histórico.
config_versionada.yamlYAML
# O arquivo versionado contém REFERÊNCIAS, nunca segredos.
banco:
  host: db.interno
  # errado seria: senha: "p@ssw0rd"  (vazaria no histórico)
  senha_ref: cofre://banco/senha   # só o NOME no cofre

api_externa:
  token_ref: cofre://api/token      # o token vive no cofre
O que vai para o repositório são referências — o nome do segredo no cofre. O segredo em si nunca é versionado.

A Verificação

SegredosTest.phpPHP
final class SegredosNoCodigoTest extends TestCase
{
    public function test_nenhum_segredo_no_codigo_ou_config(): void
    {
        // Varre o código e os arquivos versionados em busca de segredos.
        $achados = $this->procurarSegredosEm(['src/', 'config/']);

        // Nenhum segredo pode estar versionado — só referências ao cofre.
        $this->assertSame([], $achados,
            'Possíveis segredos versionados: ' . implode(', ', $achados));
    }

    public function test_segredo_vem_do_cofre(): void
    {
        $cofre = new CofreFalso(['banco/senha' => 'secreta']);
        $this->assertSame('secreta', $cofre->obter('banco/senha')->valor());
    }
}
Uma varredura automatizada falha o build se um segredo for versionado — a primeira linha de defesa contra o vazamento permanente.
Tabela de decisão Segredo no código vs no cofre
AspectoSegredo no código/configSegredo no cofre
Vaza no históricoPara sempreNunca esteve lá
RotacionarMudar código + deployNo cofre, sem deploy
Quem acessaTodo dev, sem controleControlado (§7.1)
Auditoria de acessoNenhumaCada acesso registrado
Repositório vazadoEntrega os segredosNão expõe nada

Estudo de Caso

Caso aplicado

O repositório que pode vazar sem entregar segredos

Contexto. A plataforma depende de diversos segredos — senhas de banco, chaves de APIs, tokens de integração. Mantê-los em arquivos de configuração versionados significaria que qualquer acesso indevido ao repositório, agora ou no futuro, entregaria todos eles.

Segredo versionado
Repositório entrega tudo
Segredo no cofre
Repositório não expõe nada
Ganho
Segredo realmente secreto

Decisão. Mover todos os segredos para um cofre, buscados em runtime. Os arquivos versionados passaram a conter apenas referências (o nome do segredo no cofre), e uma verificação automatizada impede que qualquer segredo seja commitado.

Resultado. O repositório tornou-se seguro para clonar, auditar e — no pior caso — vazar, porque nenhum segredo jamais esteve nele. Além disso, segredos passaram a ser rotacionáveis sem mexer no código, e cada acesso a um segredo ficou controlado e auditado. O segredo voltou a ser secreto.

Incidente Real

Severidade · Alta

O segredo que ficou no histórico

  • Antes Num momento inicial, um segredo foi colocado num arquivo de configuração versionado, por conveniência, e commitado ao repositório.
  • Sintoma Mesmo após ser removido do arquivo, o segredo permaneceu no histórico do repositório — acessível a qualquer um que consultasse commits anteriores.
  • Diagnóstico Segredos versionados vazam para sempre: o histórico é permanente, e remover do estado atual não apaga o passado.
  • Correção O segredo comprometido foi rotacionado (invalidado e substituído) e movido para o cofre. Uma verificação passou a impedir segredos versionados.
  • Kaizen Vira regra: nenhum segredo no código ou em arquivos versionados; tudo no cofre, com verificação automatizada no CI.

Decisão Arquitetural

ADR-0036

Segredos vivem num cofre, nunca no código ou repositório

Aceito
Contexto

Segredos no código ou em arquivos versionados vazam para sempre: ao serem commitados, entram no histórico permanente do repositório, e removê-los depois não os apaga do passado. Um repositório vazado entrega todos os segredos que já passaram por ele.

Decisão

Todos os segredos vivem num cofre, buscados em runtime. O código e os arquivos versionados contêm apenas referências (o nome do segredo no cofre), nunca os segredos. O acesso é controlado (§7.1), auditado, e segredos são rotacionáveis sem mudar código. Uma verificação no CI impede segredos versionados.

Alternativas consideradas

Segredo no código/config — vaza para sempre no histórico. Segredo em variável de ambiente não gerenciada — melhor que o código, mas sem rotação nem auditoria centrais. Cifrar o segredo no repositório — a chave de decifração vira o novo segredo a proteger.

Consequências
PositivasRepositório seguro para vazar. Rotação sem deploy. Acesso controlado e auditado. Segredo nunca exposto no histórico.
NegativasExige integrar um cofre e buscar segredos em runtime em toda a stack.
RiscosO cofre vira dependência crítica; mitigado por alta disponibilidade e cache controlado dos segredos.

Antipadrões

Segredo no código ou config versionada

Senha ou chave cravada no fonte ou em arquivo commitado. Vaza para sempre. Correção: segredo no cofre; só referências no repositório.

Remover o segredo só do estado atual

Apagar o segredo do arquivo mas deixá-lo no histórico. Correção: rotacionar (invalidar) o segredo exposto e movê-lo para o cofre.

Exercício

Procure os segredos no seu código

  1. Vasculhe o seu código e arquivos de configuração versionados: há senhas, chaves de API ou tokens escritos diretamente? E no histórico do repositório?
  2. Faça o teste do vazamento: se o seu repositório fosse clonado por alguém indevido hoje, que segredos ele entregaria?
  3. Para cada segredo encontrado, pergunte: ele poderia ser buscado de um cofre em runtime, deixando no repositório apenas uma referência?
  4. Pense na rotação: hoje, trocar uma senha de banco exige mudar código e reimplantar? Com um cofre, seria uma troca sem deploy.
Meta: identificar os segredos expostos no seu código e histórico — e planejar a migração para um cofre, para que o repositório nunca mais entregue um segredo.
✓ Checklist do tópico
  • Nenhum segredo (senha, chave, token) está no código ou em arquivos versionados
  • Segredos vivem num cofre e são buscados em runtime
  • Os arquivos versionados contêm apenas referências a segredos
  • O acesso a cada segredo é controlado e auditado
  • Segredos podem ser rotacionados sem mudar código, e o CI barra segredos versionados
Meta Dados · Engenharia de Plataformas · Capítulo VII · §7.2Próximo → 7.3 Criptografia e Crypto-shredding
Capítulo VII · Segurança, Privacidade e Conformidade
7.3

Criptografia e Crypto-shredding

Dados sensíveis em texto puro são uma exposição esperando acontecer. E quando é preciso apagá-los de verdade — em todos os backups, réplicas e cópias —, a tarefa é quase impossível. A criptografia resolve os dois: cifrar o sensível, e apagar destruindo a chave.

Pré-requisitos · 7.2 CofreConecta com · 7.4 LGPD · Cap IV DadosTempo de leitura · ~13 min

O Problema

Dados sensíveis — informações pessoais, documentos, dados que a lei protege — armazenados em texto puro são uma exposição à espera de acontecer. Quem quer que obtenha acesso ao banco de dados, por qualquer via, lê tudo diretamente. Um backup que vaza, uma réplica mal protegida, um acesso indevido: em todos esses casos, o dado em texto puro é entregue de bandeja. A proteção que existe é apenas a do perímetro — e perímetros falham.

Há ainda um segundo problema, mais sutil e igualmente sério: apagar dados de verdade é quase impossível. Quando a lei (ou o titular) exige que uma informação seja efetivamente apagada, não basta um DELETE na tabela principal. O dado provavelmente existe também em backups, em réplicas, em logs, em cópias feitas para análise. Rastrear e apagar cada uma dessas cópias, em todos os lugares onde o dado se espalhou, é uma tarefa hercúlea e propensa a falhas — sempre resta uma cópia esquecida em algum backup antigo. A obrigação de apagar esbarra na realidade de que o dado, uma vez espalhado, é dificílimo de erradicar.

Dois problemas, uma solução

Dados sensíveis em texto puro vazam quando o perímetro falha; e apagá-los de verdade, de todas as cópias, é quase impossível. A criptografia resolve ambos: cifrar protege o sensível, e destruir a chave (crypto-shredding) apaga o dado em todo lugar de uma vez.

A Decisão

Dados sensíveis são cifrados, com as chaves no cofre. Para apagar de verdade, destrói-se a chave (crypto-shredding): o dado cifrado torna-se ilegível para sempre, em todas as cópias simultaneamente.

A plataforma cifra os dados sensíveis antes de armazená-los. No banco, eles ficam ilegíveis — apenas texto cifrado, inútil sem a chave de decifração, que vive no cofre de segredos (§7.2) com acesso controlado e auditado. Quem obtém o banco sem a chave não obtém nada de aproveitável: um backup vazado é um amontoado de dados cifrados. Isso resolve o primeiro problema, o da exposição.

O segundo problema — apagar de verdade — é resolvido por uma técnica elegante: o crypto-shredding (destruição por criptografia). A ideia é que, se um dado só pode ser lido com sua chave, então destruir a chave torna o dado permanentemente ilegível, não importa quantas cópias dele existam. Em vez de caçar e apagar o dado em cada backup, réplica e cópia — tarefa quase impossível —, destrói-se a chave, uma única coisa, num único lugar. No instante seguinte, todas as cópias daquele dado cifrado, em todos os lugares, tornam-se lixo indecifrável de uma só vez. Apagar a chave é fácil e definitivo; apagar o dado espalhado seria difícil e incerto. O crypto-shredding transforma a obrigação de apagar, antes hercúlea, numa operação trivial e garantida.

Diagrama conceitual Cifrar o sensível e apagar pela chave
DADO SENSÍVEL cifradoilegível sem a chave CHAVE no cofre (§7.2)decifra quando autorizado CRYPTO-SHREDDINGdestruir a chave = destruir o dado, instantaneamente apagar o dado em todo lugar é difícil; apagar a chave é fácil
Figura 7.3-a. O dado sensível é cifrado, ilegível sem a chave do cofre. Para apagá-lo de verdade, destrói-se a chave — e todas as cópias do dado cifrado, em todo lugar, viram lixo indecifrável de uma vez.

Cifrar com uma chave por titular

crypto_shredding.sqlPostgreSQL
-- Dados sensíveis CIFRADOS; a chave (referência) vive no cofre.
CREATE TABLE titular_dados (
    id            BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    tenant_id     BIGINT NOT NULL,
    chave_ref     TEXT   NOT NULL,   -- nome da chave deste titular no cofre
    nome_cifrado  BYTEA  NOT NULL,   -- ilegível sem a chave
    doc_cifrado   BYTEA  NOT NULL    -- ilegível sem a chave
);
-- Uma CHAVE POR TITULAR permite apagar UM titular destruindo SUA chave.
Os campos sensíveis são bytes cifrados. A chave de cada titular vive no cofre — e uma chave por titular é o que permite apagar um titular específico.
CryptoShredding.phpPHP
// Cifrar ao gravar; decifrar (autorizado) ao ler; APAGAR destruindo a chave.
final class DadosDoTitular
{
    public function gravar(Titular $t, DadosSensiveis $d): void
    {
        $chave = $this->cofre->obter($t->chaveRef());   // §7.2
        $this->repo->salvarCifrado($t, $this->cifrar($d, $chave));
    }

    public function apagarDefinitivamente(Titular $t): void
    {
        // CRYPTO-SHREDDING: destrói a CHAVE, não caça as cópias do dado.
        $this->cofre->destruir($t->chaveRef());
        // Agora TODA cópia cifrada (banco, backups, réplicas) é ilegível.
    }
}
Apagar definitivamente não percorre cópia por cópia: destrói a chave do titular. No instante seguinte, todo dado cifrado dele vira lixo, em todo lugar.

A Verificação

CryptoShreddingTest.phpPHP
final class CryptoShreddingTest extends TestCase
{
    public function test_destruir_a_chave_torna_o_dado_ilegivel(): void
    {
        $titular = $this->titularComDados('João');

        // Antes: com a chave, o dado é legível.
        $this->assertSame('João', $this->dados->ler($titular)->nome());

        // Crypto-shredding: destrói a chave.
        $this->dados->apagarDefinitivamente($titular);

        // Depois: o dado cifrado é IRRECUPERÁVEL — em qualquer cópia.
        $this->expectException(DadoIrrecuperavel::class);
        $this->dados->ler($titular);
    }
}
O teste prova a garantia do crypto-shredding: destruída a chave, o dado é irrecuperável — e isso vale para toda cópia cifrada que exista.
Tabela de decisão Apagar o dado vs destruir a chave
AspectoApagar o dado em siCrypto-shredding (destruir a chave)
Alcança backups/réplicasQuase impossívelSim, todas de uma vez
Cópia esquecidaPermanece legívelIlegível (sem a chave)
EsforçoHercúleo, incertoTrivial, definitivo
Dado em texto puroExposto se vazarCifrado, inútil sem chave

Estudo de Caso

Caso aplicado

Apagar de verdade, sem caçar cada cópia

Contexto. A plataforma armazena dados pessoais sensíveis que a lei protege e que, a pedido do titular ou por obrigação legal, precisam por vezes ser efetivamente apagados. Caçar e apagar cada cópia desses dados — no banco, em backups, em réplicas — seria quase impossível e nunca garantiria a erradicação completa.

Apagar cada cópia
Hercúleo, incompleto
Destruir a chave
Trivial, total
Ganho
Apagamento garantido e dado protegido

Decisão. Cifrar os dados sensíveis com uma chave por titular, guardada no cofre (§7.2). Para apagar definitivamente, aplicar crypto-shredding: destruir a chave do titular, em vez de perseguir o dado por todas as cópias.

Resultado. O apagamento tornou-se trivial e garantido. Destruída a chave de um titular, todos os seus dados cifrados — onde quer que estivessem, inclusive em backups antigos — tornaram-se permanentemente ilegíveis no mesmo instante. E, como bônus, os dados em repouso ficaram protegidos: um vazamento do banco entrega apenas texto cifrado. Um problema antes hercúleo virou uma operação de uma linha.

Incidente Real

Severidade · Média

O dado que "apagado" continuava num backup

  • Antes Num modelo sem crypto-shredding, um dado sensível foi "apagado" com um DELETE na tabela principal, conforme solicitado.
  • Sintoma O mesmo dado, porém, continuava presente em backups e réplicas — tecnicamente, ele não havia sido apagado de verdade, apenas removido de um lugar.
  • Diagnóstico Apagar o dado em si não alcança todas as cópias espalhadas; sempre resta uma, e a obrigação de apagar não foi de fato cumprida.
  • Correção Adoção de criptografia com chave por titular e crypto-shredding: apagar passou a significar destruir a chave, tornando o dado ilegível em todas as cópias.
  • Kaizen Vira regra: dados sensíveis são cifrados por titular; o apagamento definitivo é por crypto-shredding, garantindo a ilegibilidade em todo lugar.

Decisão Arquitetural

ADR-0037

Cifrar dados sensíveis e apagar por crypto-shredding

Aceito
Contexto

Dados sensíveis em texto puro vazam quando o perímetro falha. E apagá-los de verdade — em todos os backups, réplicas e cópias — é quase impossível; sempre resta uma cópia, e a obrigação de apagar não é cumprida.

Decisão

Dados sensíveis são cifrados antes de armazenados, com uma chave por titular guardada no cofre (§7.2). O apagamento definitivo é feito por crypto-shredding: destrói-se a chave, tornando todas as cópias cifradas do dado permanentemente ilegíveis de uma vez. Verificado por teste.

Alternativas consideradas

Texto puro — vaza se o perímetro falha; apagar é caçar cópias. Cifrar com uma chave única global — não permite apagar um titular específico. Apagar o dado em cada cópia — hercúleo e incompleto.

Consequências
PositivasDado em repouso protegido (vazamento entrega só cifra). Apagamento definitivo trivial e garantido, em todas as cópias. Conformidade com o direito ao apagamento (§7.4).
NegativasExige cifrar/decifrar nos pontos certos e gerir uma chave por titular no cofre.
RiscosPerder uma chave indevidamente apaga o dado; mitigado por gestão cuidadosa do ciclo de vida das chaves.

Antipadrões

Dados sensíveis em texto puro

Armazenar informação protegida legível diretamente no banco. Vaza se o perímetro falha. Correção: cifrar, com a chave no cofre.

Apagar caçando cada cópia

Tentar erradicar um dado de todos os backups e réplicas manualmente. Correção: crypto-shredding — destruir a chave torna todas as cópias ilegíveis de uma vez.

Exercício

Avalie a proteção dos seus dados sensíveis

  1. Quais dados sensíveis o seu sistema armazena? Eles ficam em texto puro no banco, ou cifrados? O que um vazamento de backup entregaria?
  2. Faça o teste do apagamento: se um titular pedisse hoje para apagar definitivamente seus dados, você conseguiria garantir que sumiram de todos os backups e réplicas?
  3. Considere o crypto-shredding: se cada titular tivesse sua própria chave de cifração, apagar um titular seria destruir sua chave. Como isso simplificaria o apagamento?
  4. Pense na gestão de chaves: onde as chaves viveriam (cofre, §7.2), e como garantir que destruí-las seja deliberado e auditado?
Meta: avaliar como o seu sistema protege e apaga dados sensíveis — e planejar criptografia com crypto-shredding, para que apagar de verdade deixe de ser impossível.
✓ Checklist do tópico
  • Dados sensíveis são armazenados cifrados, ilegíveis sem a chave
  • As chaves de cifração vivem no cofre (§7.2), com acesso controlado
  • Há uma chave por titular, permitindo apagamento individual
  • O apagamento definitivo é feito por crypto-shredding (destruir a chave)
  • Destruída a chave, o dado é ilegível em todas as cópias (banco, backups, réplicas)
Meta Dados · Engenharia de Plataformas · Capítulo VII · §7.3Próximo → 7.4 LGPD e Direitos do Titular
Capítulo VII · Segurança, Privacidade e Conformidade
7.4

LGPD e Direitos do Titular

Conformidade que existe só na política de privacidade é teatro. A lei exige base legal para cada dado pessoal, finalidade declarada, consentimento quando aplicável e o atendimento dos direitos do titular — e nada disso se prova com um texto numa página.

Pré-requisitos · 7.3 Crypto-shreddingConecta com · Cap IV Soft delete · Cap VIII AuditoriaTempo de leitura · ~13 min

O Problema

A lei de proteção de dados pessoais exige muito mais do que cifrar a informação (§7.3). Ela exige uma base legal para tratar cada dado pessoal — uma justificativa válida para tê-lo e usá-lo. Exige uma finalidade declarada — para qual propósito específico o dado é tratado. Exige consentimento, quando essa é a base aplicável, registrado e revogável. E exige o atendimento dos direitos do titular: o direito de acessar seus dados, corrigi-los, levá-los para outro lugar (portabilidade) e eliminá-los.

Quando tudo isso é tratado apenas como texto numa política de privacidade — aquela página que ninguém lê e que afirma "respeitamos a sua privacidade" —, o sistema não consegue nem provar a conformidade nem atender um pedido do titular em tempo hábil. Se um titular pede para ver todos os seus dados, ou para eliminá-los, e a "conformidade" mora só num documento de texto, não há como responder de forma confiável e tempestiva. Conformidade que não está no sistema, como dado e como operação, é teatro: uma encenação de proteção que desmorona no primeiro pedido real ou na primeira fiscalização.

O teatro da conformidade

Conformidade que existe só na política de privacidade não prova nada nem atende um pedido do titular em tempo. Correção: base legal, finalidade, consentimento e direitos vivem como dado e operações no sistema.

A Decisão

A conformidade é executável: base legal, finalidade e consentimento são registrados como dado por dado pessoal, e os direitos do titular são operações reais do sistema, atendíveis em tempo.

A plataforma trata a conformidade como algo que vive no sistema, não no papel. Para cada categoria de dado pessoal, registra-se como dado a base legal que autoriza seu tratamento e a finalidade para a qual é usado. Quando a base é o consentimento, ele é capturado, registrado com data e versão, e pode ser revogado pelo titular — e a revogação tem efeito real no sistema. Os direitos do titular são implementados como operações: o direito de acesso é uma função que reúne e entrega todos os dados de um titular; o de correção, uma que os atualiza; o de portabilidade, uma que os exporta em formato utilizável; o de eliminação, o crypto-shredding do tópico anterior (§7.3). Assim, quando um titular exerce um direito, o sistema o atende em tempo, de forma verificável; e quando é preciso provar conformidade, o sistema mostra — base legal, finalidade, consentimentos, atendimentos — em vez de apontar para um texto. A conformidade deixa de ser teatro e passa a ser uma capacidade executável.

Diagrama conceitual Conformidade no sistema, não no papel
só no PAPEL (política)não prova nem atende — teatro no SISTEMA (dado)prova e atende em tempo Base legalpor que trata o dado Finalidadepara qual uso Consentimentoregistrado, revogável Direitosacesso, eliminação CONFORMIDADE EXECUTÁVELo sistema prova e atende — não é texto numa página
Figura 7.4-a. Base legal, finalidade, consentimento e direitos do titular vivem como dado e operações no sistema. A conformidade é provável e atendível — não um texto que ninguém pode executar.

A conformidade registrada como dado

lgpd.sqlPostgreSQL
-- A conformidade vive como DADO: base legal e finalidade por dado pessoal.
CREATE TABLE tratamento_dado_pessoal (
    id          BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    titular_id  BIGINT NOT NULL,
    categoria   TEXT   NOT NULL,   -- 'contato', 'documento'
    base_legal  TEXT   NOT NULL,   -- 'consentimento', 'contrato'...
    finalidade  TEXT   NOT NULL    -- para qual uso declarado
);

-- Consentimento: registrado, datado, versionado e REVOGÁVEL.
CREATE TABLE consentimento (
    id          BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    titular_id  BIGINT      NOT NULL,
    finalidade  TEXT        NOT NULL,
    concedido_em TIMESTAMPTZ NOT NULL,
    revogado_em  TIMESTAMPTZ             -- nulo = vigente
);
Base legal, finalidade e consentimento são linhas no banco — consultáveis, prováveis. A conformidade vira dado, não texto.
DireitosDoTitular.phpPHP
// Os direitos do titular são OPERAÇÕES reais, atendíveis em tempo.
final class DireitosDoTitular
{
    // Direito de ACESSO: reúne e entrega todos os dados do titular.
    public function acessar(Titular $t): RelatorioDeDados
    {
        return $this->coletarTodosOsDados($t);
    }

    // Direito de PORTABILIDADE: exporta em formato utilizável.
    public function exportar(Titular $t): ArquivoExportavel
    {
        return $this->coletarTodosOsDados($t)->paraFormatoAberto();
    }

    // Direito de ELIMINAÇÃO: crypto-shredding (§7.3).
    public function eliminar(Titular $t): void
    {
        $this->cryptoShredding->apagarDefinitivamente($t);
    }
}
Cada direito do titular é uma função executável — acessar, exportar, eliminar. O pedido do titular é atendido por código, não por um e-mail manual.

A Verificação

LgpdTest.phpPHP
final class DireitosDoTitularTest extends TestCase
{
    public function test_titular_acessa_todos_os_seus_dados(): void
    {
        $titular = $this->titularComDadosEmVariasTabelas();

        $relatorio = $this->direitos->acessar($titular);

        // O direito de acesso reúne TUDO — de forma verificável.
        $this->assertTrue($relatorio->incluiTodasAsCategorias());
    }

    public function test_revogar_consentimento_tem_efeito_real(): void
    {
        $titular = $this->titularQueConsentiu('marketing');

        $this->consentimentos->revogar($titular, 'marketing');

        // A revogação BLOQUEIA o tratamento para aquela finalidade.
        $this->assertFalse($this->podeTratarPara($titular, 'marketing'));
    }
}
Os testes provam que os direitos funcionam: o acesso reúne tudo, e a revogação de consentimento bloqueia o tratamento de fato — conformidade executável, verificada.
Tabela de decisão Conformidade no papel vs no sistema
AspectoSó na política (teatro)No sistema (executável)
Provar a base legalAponta para um textoMostra o dado registrado
Pedido de acessoColeta manual, incertaOperação que reúne tudo
Revogar consentimentoSem efeito realBloqueia o tratamento
EliminaçãoPromessaCrypto-shredding (§7.3)
FiscalizaçãoDifícil de comprovarSistema demonstra

Estudo de Caso

Caso aplicado

Conformidade que o sistema prova e atende

Contexto. A plataforma trata dados pessoais sujeitos à lei de proteção de dados. Tratar a conformidade apenas como uma política de privacidade textual deixaria o sistema incapaz de provar que cumpre a lei ou de atender, em tempo, a um pedido de um titular para acessar ou eliminar seus dados.

Conformidade no papel
Não prova nem atende
Conformidade no sistema
Prova e atende em tempo
Ganho
Conformidade executável

Decisão. Levar a conformidade para dentro do sistema: registrar base legal, finalidade e consentimento como dado, e implementar os direitos do titular — acesso, correção, portabilidade, eliminação — como operações reais, com a eliminação apoiada no crypto-shredding (§7.3).

Resultado. Quando um titular exerce um direito, o sistema o atende em tempo e de forma verificável. Quando é preciso demonstrar conformidade, o sistema mostra os registros de base legal, finalidade e consentimento, em vez de apontar para um texto. A conformidade deixou de ser teatro e tornou-se uma capacidade real, provável e operável.

Incidente Real

Severidade · Alta

O pedido do titular que o sistema não sabia atender

  • Antes Num estágio em que a conformidade vivia só na política de privacidade, chegou um pedido de um titular para acessar todos os seus dados pessoais.
  • Sintoma Não havia uma operação que reunisse os dados do titular espalhados pelo sistema; atender ao pedido exigiria uma busca manual, demorada e sem garantia de completude.
  • Diagnóstico A conformidade não estava no sistema, apenas no papel — então um direito do titular não podia ser atendido de forma confiável e tempestiva.
  • Correção Implementação dos direitos do titular como operações reais e do registro de base legal, finalidade e consentimento como dado.
  • Kaizen Vira regra: a conformidade vive no sistema; cada direito do titular é uma operação atendível em tempo, e base legal/finalidade/consentimento são dados prováveis.

Decisão Arquitetural

ADR-0038

Conformidade executável: base legal, consentimento e direitos no sistema

Aceito
Contexto

A lei de proteção de dados exige base legal, finalidade, consentimento e atendimento dos direitos do titular. Tratada só como texto numa política, a conformidade não prova nada nem atende um pedido em tempo — é teatro.

Decisão

A conformidade vive no sistema: base legal e finalidade são registradas como dado por categoria de dado pessoal; o consentimento é capturado, datado, versionado e revogável com efeito real; e os direitos do titular (acesso, correção, portabilidade, eliminação) são operações reais, com a eliminação via crypto-shredding (§7.3). Verificado por teste.

Alternativas consideradas

Conformidade só na política — não prova nem atende; teatro. Direitos atendidos manualmente — lentos, incompletos, não confiáveis. Consentimento sem efeito no sistema — revogação que não bloqueia nada.

Consequências
PositivasConformidade provável e atendível em tempo. Direitos do titular como operações reais. Revogação com efeito. Pronto para fiscalização.
NegativasExige modelar base legal/finalidade/consentimento como dado e implementar os direitos como operações.
RiscosA lei evolui e varia por jurisdição; mitigado por manter a conformidade como dado e operações ajustáveis, não cravada.

Antipadrões

Conformidade só na política de privacidade

Tratar base legal, consentimento e direitos como texto numa página. Não prova nem atende. Correção: conformidade como dado e operações no sistema.

Consentimento sem efeito real

Registrar consentimento que, ao ser revogado, não muda nada no tratamento. Correção: a revogação bloqueia, de fato, o tratamento para aquela finalidade.

Exercício

Teste a conformidade do seu sistema

  1. Para os dados pessoais que o seu sistema trata, você consegue dizer — consultando o sistema, não um documento — qual a base legal e a finalidade de cada um?
  2. Faça o teste do pedido de acesso: se um titular pedisse hoje todos os seus dados, o seu sistema teria uma operação que os reúne, ou seria uma busca manual?
  3. Faça o teste da revogação: quando alguém revoga um consentimento, isso bloqueia de fato o tratamento para aquela finalidade, ou só registra a revogação sem efeito?
  4. Esboce os direitos do titular como operações: acessar, corrigir, exportar (portabilidade) e eliminar. Quais já existem como código, e quais são só promessa?
Meta: avaliar se a conformidade do seu sistema é executável ou teatro — e planejar como levar base legal, consentimento e direitos do titular para dentro do sistema.
✓ Checklist do tópico
  • Base legal e finalidade são registradas como dado por dado pessoal
  • O consentimento é capturado, datado, versionado e revogável
  • A revogação de consentimento bloqueia o tratamento de fato
  • Os direitos do titular (acesso, correção, portabilidade, eliminação) são operações reais
  • O sistema prova a conformidade e atende pedidos do titular em tempo
Meta Dados · Engenharia de Plataformas · Capítulo VII · §7.4Próximo → 7.5 Hardening e Resposta a Incidentes
Capítulo VII · Segurança, Privacidade e Conformidade
7.5

Hardening e Resposta a Incidentes

Sistemas são atacados — a questão não é "se", mas "quando". Sem reduzir a superfície de ataque antes e sem um processo para detectar, conter, recuperar e aprender depois, um incidente deixa de ser um problema gerenciável e vira uma catástrofe.

Pré-requisitos · 7.1–7.4Conecta com · Cap VIII Observabilidade · RunbooksTempo de leitura · ~13 min

O Problema

Sistemas conectados ao mundo são atacados — não é uma possibilidade remota, é uma certeza estatística. Sondas automatizadas varrem a internet sem cessar à procura de portas abertas e vulnerabilidades conhecidas; atacantes oportunistas exploram qualquer brecha que encontrem. A pergunta que importa, para quem opera um sistema, não é "se" ele será atacado, mas "quando". E um lembrete vivo dessa realidade é o comprometimento de um servidor de aplicação por um minerador de criptomoedas, que exigiu a reconstrução completa do servidor — um ataque real, com custo real.

Sem duas coisas, um incidente deixa de ser um problema gerenciável e vira uma catástrofe. A primeira é o hardening preventivo: se a superfície de ataque não foi reduzida de antemão — se há portas abertas sem necessidade, software desnecessário instalado, privilégios excessivos —, há muito mais a ser explorado, e o ataque tem mais chances de sucesso e mais alcance. A segunda é um processo de resposta: se, quando o ataque acontece, não há um plano para detectar, conter, recuperar e aprender, a organização reage no improviso, e o estrago se multiplica — dias de operação parada, dados expostos, confiança perdida. Um incidente sem preparo prévio e sem processo de resposta é a diferença entre um susto e um desastre.

O incidente que vira catástrofe

Sem hardening (reduzir a superfície antes) e sem processo de resposta (detectar, conter, recuperar, aprender), um ataque inevitável vira dias parados, dados expostos e confiança perdida. Correção: preparar-se antes e ter um processo para depois.

A Decisão

A segurança operacional tem duas frentes: hardening, que reduz a superfície de ataque antes do incidente, e um processo de resposta — detectar, conter, recuperar, aprender — para quando ele acontecer.

A plataforma trata a inevitabilidade do ataque com duas frentes complementares. A primeira é o hardening: reduzir preventivamente a superfície de ataque. Isso significa fechar portas que não precisam estar abertas, remover software e serviços desnecessários, manter tudo atualizado contra vulnerabilidades conhecidas, e aplicar o menor privilégio (§7.1) em toda parte. Quanto menor a superfície, menos há para um atacante explorar. A segunda frente é um processo de resposta a incidentes, com quatro etapas claras: detectar (perceber que algo está errado, apoiado na observabilidade do Cap VIII), conter (isolar o problema para que não se espalhe), recuperar (restaurar a operação a um estado seguro — que, no caso do servidor comprometido, foi a reconstrução completa) e aprender (entender o que aconteceu e endurecer o sistema para que não se repita — o kaizen que aparece em cada incidente deste livro). Com hardening, o ataque encontra menos brechas; com o processo de resposta, quando uma brecha é explorada, o incidente é contido e vira aprendizado, não catástrofe.

Fluxograma operacional Hardening antes, resposta depois
A pergunta não é SE será atacado, mas QUANDO HARDENING (antes)reduzir a superfície de ataque RESPOSTA (durante/depois)detectar · conter · recuperar · aprender Detectar Conter Recuperar Aprender sem hardening + resposta, um incidente vira catástrofe
Figura 7.5-a. Duas frentes contra o ataque inevitável: o hardening reduz a superfície antes; o processo de resposta — detectar, conter, recuperar, aprender — contém o incidente depois e o transforma em aprendizado.

Reduzir a superfície e responder com método

hardening.shBash
# HARDENING: reduzir a superfície de ataque ANTES do incidente.

# 1) Fechar tudo por padrão; abrir só o necessário (§7.1 na rede).
ufw default deny incoming
ufw allow 443/tcp          # só a porta que precisa estar aberta

# 2) Remover software desnecessário — menos a explorar.
apt-get purge -y servicos-nao-usados

# 3) Manter atualizado contra vulnerabilidades conhecidas.
apt-get update && apt-get upgrade -y

# 4) Menor privilégio: a aplicação NÃO roda como administrador.
useradd -r app && chown -R app:app /opt/app
O hardening é redução: fechar portas, remover o desnecessário, atualizar, e nunca rodar como administrador. Menos superfície, menos a explorar.
RespostaAIncidente.phpPHP
// O processo de RESPOSTA: detectar, conter, recuperar, aprender.
final class RespostaAIncidente
{
    public function conduzir(Incidente $inc): void
    {
        // 1) DETECTAR — a observabilidade (Cap VIII) acende o alerta.
        $this->registrar($inc->detectadoEm());

        // 2) CONTER — isolar para não se espalhar.
        $this->isolar($inc->ativosAfetados());

        // 3) RECUPERAR — restaurar a um estado SEGURO (ex.: rebuild).
        $this->restaurarEstadoSeguro($inc);

        // 4) APRENDER — post-mortem e hardening contra a repetição.
        $this->postMortem($inc)->gerarAcoesDeHardening();
    }
}
A resposta segue quatro passos claros, não o improviso: detectar, conter, recuperar e — crucial — aprender, endurecendo o sistema contra a repetição.

A Verificação

hardening_check.shBash
# Verificação: o hardening é CONFERIDO, não presumido.

# Nenhuma porta inesperada está aberta.
test "$(portas_abertas)" = "443" || falhar "Porta inesperada aberta"

# A aplicação NÃO roda como administrador.
test "$(usuario_da_app)" != "root" || falhar "App roda como root"

# Não há atualizações de segurança pendentes.
test "$(atualizacoes_seguranca_pendentes)" = "0" || falhar "Patches pendentes"
O hardening é verificado automaticamente: portas, privilégio e atualizações são conferidos, para que a superfície não volte a crescer despercebida.
Tabela de decisão Despreparado vs preparado para o incidente
AspectoSem hardening/respostaCom hardening e processo
Superfície de ataqueAmpla, muito a explorarMínima
Ao detectar o ataqueImproviso, pânicoProcesso claro
EspalhamentoSem contençãoIsolado rápido
RecuperaçãoLenta, incertaA estado seguro (rebuild)
RepetiçãoProvávelAprendizado endurece o sistema

Estudo de Caso

Caso aplicado

O servidor comprometido que virou aprendizado

Contexto. Um servidor de aplicação da plataforma foi comprometido por um minerador de criptomoedas — um atacante explorou uma brecha e passou a usar os recursos do servidor para minerar. É o lembrete concreto de que o ataque não é hipótese: acontece.

Sem processo
Ataque vira catástrofe
Com processo
Conter, reconstruir, aprender
Ganho
Incidente gerenciado e lição aprendida

Decisão. Conduzir a resposta pelo processo: detectar o comprometimento, conter isolando o servidor, recuperar com a reconstrução completa do servidor a partir de uma base limpa e segura, e aprender — investigar a brecha e aplicar hardening para fechá-la.

Resultado. O incidente foi contido e resolvido com a reconstrução do servidor, e — mais importante — virou aprendizado: a brecha explorada foi fechada e a superfície de ataque, reduzida, para que o mesmo vetor não pudesse ser usado de novo. Um ataque real, em vez de catástrofe prolongada, tornou-se um incidente gerenciado e uma lição que endureceu o sistema. É o padrão de todos os incidentes deste livro: conter, recuperar e transformar em kaizen.

Incidente Real

Severidade · Crítica

O minerador que comprometeu o servidor

  • Detecção Um servidor de aplicação passou a apresentar comportamento anômalo — consumo de recursos incompatível com sua carga normal, sinal de um minerador de criptomoedas em execução.
  • Contenção O servidor comprometido foi isolado para impedir que o atacante avançasse para outros ativos ou continuasse a operar a partir dele.
  • Recuperação Como um servidor comprometido não é confiável, a recuperação foi a reconstrução completa a partir de uma base limpa e segura — não uma simples limpeza.
  • Aprendizado A brecha que permitiu o comprometimento foi investigada e fechada; aplicou-se hardening para reduzir a superfície e impedir o mesmo vetor.
  • Kaizen Vira regra: hardening preventivo contínuo e processo de resposta ensaiado; cada incidente fecha sua brecha e endurece o sistema.

Decisão Arquitetural

ADR-0039

Hardening preventivo e processo de resposta a incidentes

Aceito
Contexto

Sistemas serão atacados — não "se", mas "quando" (como o comprometimento de um servidor por um minerador demonstrou). Sem reduzir a superfície antes e sem um processo de resposta depois, um incidente vira catástrofe: dias parados, dados expostos, confiança perdida.

Decisão

Duas frentes: hardening, que reduz a superfície de ataque preventivamente (fechar portas, remover software desnecessário, atualizar, menor privilégio §7.1), verificado automaticamente; e um processo de resposta a incidentes em quatro etapas — detectar (Cap VIII), conter, recuperar (a estado seguro, ex.: rebuild) e aprender (post-mortem + hardening).

Alternativas consideradas

Sem hardening — superfície ampla, ataque tem mais a explorar. Sem processo de resposta — improviso multiplica o estrago. Confiar só no perímetro — perímetros falham; é preciso preparo e resposta.

Consequências
PositivasSuperfície de ataque mínima. Incidente contido e recuperado com método. Cada ataque vira aprendizado que endurece o sistema, não catástrofe.
NegativasExige manter o hardening contínuo e ensaiar o processo de resposta.
RiscosNenhum hardening elimina todo risco; por isso a resposta a incidentes é igualmente essencial — preparar-se para o ataque que passar.

Antipadrões

Confiar que não será atacado

Operar sem hardening nem plano de resposta, na esperança de não ser alvo. Correção: assumir o ataque como "quando"; reduzir a superfície e ter processo de resposta.

Limpar um servidor comprometido em vez de reconstruí-lo

Tentar "limpar" um sistema invadido, que não é mais confiável. Correção: recuperar a um estado seguro conhecido — reconstrução a partir de base limpa.

Exercício

Avalie seu preparo para o inevitável

  1. Faça o inventário da superfície de ataque de um servidor seu: que portas estão abertas? Que software desnecessário está instalado? A aplicação roda como administrador? Há atualizações de segurança pendentes?
  2. Faça o teste do "quando": se um servidor seu fosse comprometido agora, você perceberia (detecção)? Saberia isolá-lo (contenção)?
  3. Pense na recuperação: você conseguiria reconstruir um servidor comprometido a partir de uma base limpa e segura, ou dependeria de "limpá-lo"?
  4. Esboce o processo de resposta para o seu time: quem faz o quê em cada etapa — detectar, conter, recuperar, aprender?
Meta: avaliar honestamente a superfície de ataque e o preparo de resposta do seu sistema — e planejar o hardening e o processo que transformam um ataque inevitável em incidente gerenciado.
✓ Checklist do tópico
  • A superfície de ataque é reduzida por hardening (portas, software, atualizações, privilégio)
  • O hardening é verificado automaticamente, não presumido
  • Existe um processo de resposta: detectar, conter, recuperar, aprender
  • A recuperação restaura a um estado seguro (ex.: rebuild de servidor comprometido)
  • Cada incidente fecha sua brecha e endurece o sistema (aprendizado)
Meta Dados · Engenharia de Plataformas · Capítulo VII · §7.5Fim do Capítulo VII · voltar ao início
Meta Dados · Engenharia de Plataformas
Capítulo VIII

Confiabilidade,
Observabilidade
e Operações

Tornar o invisível visível e o visível acionável — dos três pilares ao Command Center, com cada incidente virando aprendizado de sistema.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo VIII
Capítulo VIII · Confiabilidade, Observabilidade e Operações
8.1

Os Três Pilares

Quando algo quebra em produção, a primeira pergunta é “o que está acontecendo?”. Sem instrumentação, a resposta vem de adivinhação — e cada minuto de adivinhação é operação degradada.

Pré-requisitos · Cap I Genba · Cap II Engines Conecta com · 8.2 Alertas · 8.4 Command Center Tempo de leitura · ~13 min

O Problema

Sem instrumentação adequada, diagnosticar produção vira um exercício de adivinhação: tentar reproduzir localmente e não conseguir, pedir ao cliente que descreva o que ele não sabe descrever, ou encarar um log que só diz “erro” — sem dizer onde, para quem, ou por quê.

Cada minuto gasto adivinhando é um minuto de operação degradada. E a adivinhação não escala: o que funciona com um punhado de servidores colapsa diante de uma frota como os ~40 servidores virtuais em operação. A pergunta deste tópico é como tornar o sistema legível — capaz de contar o que está fazendo, com contexto suficiente para agir.

O custo da cegueira

Um log sem contexto — log("erro") — é pior que inútil: dá a falsa sensação de que há observabilidade, quando não há nada acionável na madrugada de um plantão.

A Decisão

Toda operação emite os três pilares: logs, métricas e traces — e os três compartilham um identificador de correlação.

Os três se complementam e nenhum substitui os outros. Logs registram o que aconteceu, com contexto (tenant, operação, usuário). Métricas dizem quanto e com que frequência, de forma agregável (latência, taxa de erro, contadores). Traces mostram o caminho de uma requisição através das engines. O correlação_id é o fio que costura os três, permitindo navegar de um sintoma agregado até a linha exata que o causou.

Diagrama conceitual Como os três pilares se ligam
Requisição correlação_id gerado LOGS o que aconteceu · contexto MÉTRICAS quanto · com que frequência TRACES o caminho na requisição mesmo correlação_id → métrica anômala → trace lento → log da causa
Figura 8.1-a. O identificador de correlação é o que transforma três fluxos separados numa única narrativa de diagnóstico.

A Implementação

O contexto que viaja com a requisição

O correlação_id nasce na entrada e é propagado a todas as engines. Tudo o que a requisição tocar — log, métrica, trace — carrega o mesmo identificador.

ContextoDeObservabilidade.phpPHP
final class ContextoDeObservabilidade
{
 public function __construct(
 public readonly string $correlacaoId, // único por requisição
 public readonly int $tenantId,
 public readonly string $operacao,
 public readonly ?int $usuarioId = null,
 ) {}

 public static function iniciar(int $tenantId, string $operacao): self
 {
 return new self(
 correlacaoId: bin2hex(random_bytes(16)), // id de correlação
 tenantId: $tenantId,
 operacao: $operacao,
 );
 }
}
Gerado uma vez, propagado a tudo: é a âncora que liga os três pilares de uma mesma operação.
Nota de projeto

A instrumentação é transversal: um decorador envolve o caso de uso e emite os três pilares automaticamente. A lógica de negócio não muda — observabilidade não polui o domínio.

CasoDeUsoObservavel.phpPHP
public function executar(ContextoDeObservabilidade $ctx, callable $casoDeUso): mixed
{
 $span = $this->tracer->iniciarSpan($ctx->operacao, $ctx->correlacaoId);
 $inicio = microtime(true);
 $rotulos = ['operacao' => $ctx->operacao, 'tenant' => (string) $ctx->tenantId];

 try {
 $resultado = $casoDeUso();
 $this->metricas->contar('operacao.sucesso', $rotulos);
 $this->metricas->duracao('operacao.duracao', microtime(true) - $inicio, $rotulos);
 $span->encerrarComSucesso();
 return $resultado;
 } catch (Throwable $e) {
 // Distingue erro de domínio (esperado) de falha técnica (Cap I).
 $categoria = $e instanceof ErroDeDominio ? 'dominio' : 'tecnico';
 $this->metricas->contar('operacao.erro', $rotulos + ['categoria' => $categoria]);
 $this->log->falhaTecnica($ctx->operacao, $e);
 $span->encerrarComErro($e);
 throw $e;
 }
}
Sucesso e falha emitem métricas distintas; o erro de domínio é separado da falha técnica, evitando ruído.
Tabela de decisão Qual pilar responde qual pergunta
Pergunta operacionalPilarFormaExemplo
“A latência subiu?”MétricaSérie agregável (p95/p99)p95 de cadastro passou de 200 p/ 800ms
“Subiu em qual engine?”TraceCaminho da requisiçãoO span do banco consumiu 600ms
“Por que, nesta requisição?”LogEvento com contextoTimeout no parceiro X, tenant 7
“Com que frequência ocorre?”MétricaContador rotulado42 timeouts/min na operação Y

Estudo de Caso

Caso aplicado

Da adivinhação ao diagnóstico em minutos

Contexto. Uma operação intermitentemente lenta gerava reclamações esparsas de clientes, impossíveis de reproduzir localmente. Sem correlação entre os pilares, cada investigação começava do zero.

Intervenção. Instrumentação dos três pilares com correlação_id compartilhado. A partir daí, o caminho de diagnóstico virou mecânico: um pico na métrica de p95 leva ao trace daquela janela, que aponta a engine lenta, cujo log — filtrado pelo mesmo id — revela a causa.

Antes
Reproduzir localmente
Depois
Seguir o correlação_id
Diagnóstico
De horas a minutos

Resultado. A investigação deixou de depender de sorte e reprodução. O sintoma agregado e a causa pontual passaram a estar a três cliques de distância — exatamente a promessa dos três pilares.

Incidente Real

Severidade · Baixa

Métrica de erro poluída por erro de domínio

  • T+0 Um formulário de cadastro com validação rígida começa a rejeitar entradas inválidas — comportamento correto do domínio.
  • T+5min A métrica genérica operacao.erro dispara: cadastros “falhando” em massa. O painel fica vermelho.
  • T+12min A equipe investiga uma “falha de infra” que não existe — eram apenas rejeições de validação legítimas.
  • Correção Separar a categoria do erro: domínio (esperado) vs técnico (falha real). A métrica de infra para de contar rejeições válidas.
  • Kaizen Vira regra: todo caso de uso observável distingue as duas categorias por código, não por convenção.

Decisão Arquitetural

ADR-0040

Correlação obrigatória e separação domínio/técnico

Aceito
Contexto

Diagnóstico em produção dependia de reprodução local, inviável em escala. Métricas genéricas misturavam erro de domínio (esperado) com falha técnica, gerando falsos alarmes.

Decisão

Toda operação emite logs, métricas e traces com um correlação_id compartilhado. Erros são categorizados em domínio vs técnico no ponto de emissão.

Alternativas consideradas

Só logs — sem agregação nem caminho, insuficiente. Métricas sem correlação — mostram o sintoma mas não levam à causa. Amostragem agressiva de traces — perde a requisição que importa.

Consequências
PositivasDiagnóstico mecânico do sintoma à causa. Painéis confiáveis sem falsos alarmes.
NegativasExige propagar o contexto por todas as engines e disciplina de categorização.
RiscosCapítulo de telemetria cresce; mitigado por retenção e amostragem inteligente fora do caminho crítico.

Antipadrões

Log sem contexto

log("erro") — inútil na madrugada. Correção: correlação, tenant, operação em todo log.

Misturar erro de domínio com falha técnica

Cadastro inválido alertando como falha de infra. Correção: categoria distinta no ponto de emissão.

Exercício

Siga o fio da correlação

  1. Escolha uma operação crítica do seu sistema. Verifique: ela emite os três pilares? Se falta algum, qual pergunta operacional fica sem resposta?
  2. Confirme se logs, métricas e traces dessa operação compartilham um identificador comum. Se não, eles contam três histórias desconexas.
  3. Audite as métricas de erro: elas distinguem rejeição de domínio de falha técnica? Quantos “erros” do último mês eram, na verdade, validações funcionando?
Meta: conseguir partir de um pico numa métrica e chegar, seguindo o correlação_id, até a linha de log que explica a causa.
✓ Checklist do tópico
  • Toda operação emite logs, métricas e traces
  • Os três pilares compartilham um correlação_id
  • Logs carregam contexto (tenant, operação, usuário)
  • Métricas são agregáveis por operação e tenant
  • Erro de domínio é distinguido de falha técnica nas métricas
Meta Dados · Engenharia de Plataformas · Capítulo VIII · §8.1Próximo → 8.2 Alertas Acionáveis
Capítulo VIII · Confiabilidade, Observabilidade e Operações
8.2

Alertas Acionáveis

Um alerta que dispara o tempo todo não protege ninguém — ensina a equipe a ignorá-lo. O sinal que importa se perde no ruído.

Pré-requisitos · 8.1 Três Pilares · Cap I Genba Conecta com · 8.4 Command Center · Cap VII Incidentes Tempo de leitura · ~14 min

O Problema

A fadiga de alerta é tão perigosa quanto não ter alerta nenhum — porque o efeito prático é idêntico: ninguém reage. Quando todo alerta dispara o tempo todo, a equipe aprende, por sobrevivência, a tratá-los como fundo de tela.

O caso do cliente de logística-servidor de banco de dados é o retrato exato do antipadrão. Um problema de flapping no MySQL — o serviço oscilando entre disponível e indisponível em segundos — fez o ferramenta de monitoramento disparar dezenas de alertas em sequência. Em pouco tempo, aqueles alertas viraram ruído, e o sinal que de fato importava ficou enterrado no meio de uma enxurrada de notificações idênticas.

Da operação realO cliente de logística-servidor de banco de dados e o flapping do MySQL na ferramenta de monitoramento são o pano de fundo deste tópico — um caso vivido, não hipotético.

O problema não é técnico, é humano. Um sistema pode estar perfeitamente instrumentado e ainda assim falhar em sua função operacional se os alertas que emite não merecem confiança. A pergunta central deste tópico não é "como detectar mais" — é "como detectar com parcimônia, de modo que cada alerta signifique algo".

A armadilha

Quando a resposta correta a um alerta é "ignorar" ou "esperar passar", ele nunca deveria ter disparado. Cada alerta desses corrói a confiança em todos os outros.

A Decisão

A regra que adotamos é deliberadamente restritiva, porque a restrição é o ponto:

Um alerta só existe se for acionável: representa um problema real que exige ação humana agora, e aponta para um runbook.

Tudo o que não atende a esse critério não é alerta — é métrica de painel. A diferença não é semântica: alerta notifica uma pessoa e interrompe o que ela está fazendo; painel informa quando consultado. Confundir os dois é a origem da fadiga.

Disso decorrem três exigências, cada uma materializada como restrição de sistema, não como recomendação: todo alerta tem um dono (quem responde), um runbook (o que fazer), e um limiar calibrado para problema real — não flutuação normal.

Diagrama conceitual O teste de um alerta
Condição detectada Exige ação humana já? ALERTA aponta p/ runbook sim Painel / métrica não notifica não
Figura 8.2-a. O critério único: se a resposta certa não é “agir agora”, a condição vira painel, nunca notificação.
Tabela de decisão Alerta ou painel?
Condição observadaExige ação agora?DestinoPor quê
API p95 > 500ms sustentado 5 minSimAlerta críticoDegradação real afetando clientes
Uso de CPU em 70%NãoPainelNormal sob carga; não exige reação
Fila de eventos crescendo 10 minSimAlerta avisoTende a virar atraso; runbook de drenagem
Pico isolado de erro (1 amostra)NãoPainelFlapping; não sustentado (lição cliente de logística-servidor de banco de dados)
Certificado TLS expira em 7 diasSimAlerta avisoAção programável com runbook claro
Deploy concluídoNãoPainel / logInformativo; nada a fazer

A Implementação

Anti-flapping: a condição precisa se sustentar

A defesa central contra o caso cliente de logística-servidor de banco de dados é exigir que a condição se mantenha por toda uma janela de tempo. Um pico isolado — violou, normalizou, violou — não sustenta a condição e, portanto, não dispara.

AvaliadorDeAlerta.phpPHP
final class AvaliadorDeAlerta
{
 public function deveDisparar(RegraDeAlerta $regra): bool
 {
 // Lê as amostras dentro da janela (ex.: últimos 5 minutos).
 $amostras = $this->historico->amostrasNaJanela(
 $regra->metrica(),
 $regra->janelaMinutos(),
 );

 if ($amostras === []) return false;

 // Só dispara se TODAS as amostras violam o limiar.
 // Flapping (viola, normaliza, viola) NÃO sustenta → não alerta.
 foreach ($amostras as $amostra) {
 if (! $regra->condicaoViolada($amostra)) {
 return false; // a condição não se sustentou
 }
 }
 return true;
 }
}
O laço é a vacina contra o flapping: basta uma amostra normal na janela para o alerta não disparar.
Nota de projeto

A regra de alerta é dado governado, não uma configuração solta na ferramenta de monitoramento. Dono e runbook são colunas NOT NULL — é fisicamente impossível criar um alerta órfão.

ops_alert_rule.sqlPostgreSQL
CREATE TABLE ops_alert_rule (
 id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
 chave TEXT NOT NULL, -- 'latencia_api_alta'
 metrica TEXT NOT NULL,
 condicao TEXT NOT NULL, -- 'p95 > 500ms por 5min'
 severidade TEXT NOT NULL, -- 'aviso' | 'critico'
 dono TEXT NOT NULL, -- quem responde (jamais nulo)
 runbook_ref TEXT NOT NULL, -- runbook obrigatório
 -- Anti-flapping: condição sustentada por uma janela, não um pico.
 janela_minutos INTEGER NOT NULL DEFAULT 5
);
A própria estrutura da tabela impõe a disciplina: sem dono e sem runbook, o registro não existe.

Incidente Real

Antes de o anti-flapping existir, o cliente de logística-servidor de banco de dados produziu um incidente exemplar — não pela gravidade do problema técnico, mas pelo que o ruído de alerta causou na resposta.

Severidade · Média

Tempestade de alertas no cliente de logística-servidor de banco de dados (MySQL flapping)

  • T+0min O MySQL do cliente de logística-servidor de banco de dados começa a oscilar: disponível, indisponível, disponível, em ciclos de segundos.
  • T+2min O ferramenta de monitoramento dispara o primeiro alerta. Em seguida, o segundo, o terceiro — uma cada ciclo de flapping.
  • T+15min Dezenas de notificações idênticas acumuladas. A equipe, sem distinguir sinal de ruído, começa a tratar o canal como fundo de tela.
  • T+40min A causa real (saturação intermitente) só é diagnosticada quando alguém ignora os alertas e olha as métricas diretamente — o oposto do que um alerta deveria provocar.
  • Correção Introdução da janela anti-flapping: a condição passa a exigir persistência. Um ciclo de oscilação deixa de gerar uma enxurrada.
  • Kaizen A lição vira sistema: regra de alerta com janela obrigatória e a métrica “razão de alertas acionáveis” passa a ser monitorada.
Lição que dói

Quando o operador resolve um incidente ignorando o sistema de alertas e olhando as métricas na mão, o sistema de alertas falhou — mesmo que “tenha alertado”. Alertar sem ser crível é não alertar.

Estudo de Caso

O contraponto positivo: como a mesma família de problemas é tratada depois que o critério de acionabilidade e o anti-flapping entram em vigor.

Caso aplicado

Da enxurrada à parcimônia: redesenho dos alertas de banco

Contexto. Após o incidente do cliente de logística-servidor de banco de dados, a frota de bancos monitorados (entre os ~40 servidores virtuais em operação) tinha dezenas de regras de alerta herdadas, muitas disparando por flutuação normal. A confiança no canal estava no chão.

Intervenção. Cada regra passou pelo teste de acionabilidade da Figura 8.2-a. As que não exigiam ação humana imediata viraram painel no Command Center. As que sobraram ganharam janela anti-flapping, dono nomeado e runbook obrigatório.

Antes
~dezenas de alertas/dia
Depois
Poucos, todos acionáveis
Métrica nova
Razão acionáveis/total
Efeito
Canal volta a ser crível

Resultado. O número de notificações despencou, mas — e este é o ponto — cada notificação restante passou a merecer reação imediata. A queda no volume não foi perda de cobertura; foi remoção de ruído. O que era fundo de tela voltou a ser sinal.

Decisão Arquitetural

A regra de acionabilidade foi formalizada num ADR, para que o porquê sobreviva à rotatividade da equipe:

ADR-0042

Alertas exigem acionabilidade, dono e runbook

Aceito
Contexto

A frota de monitoramento acumulou regras que disparam por flutuação normal e por flapping (caso cliente de logística-servidor de banco de dados). O excesso de alertas não acionáveis gerou fadiga: a equipe passou a ignorar o canal, e o sinal real se perdeu no ruído. Precisamos restaurar a credibilidade das notificações.

Decisão

Um alerta só existe se exigir ação humana imediata e apontar para um runbook. Dono e runbook_ref são obrigatórios por constraint. Toda regra usa janela anti-flapping (condição sustentada). Condições informativas vão para painel, não para notificação.

Alternativas consideradas

Agrupar alertas duplicados — alivia o sintoma do flapping, mas não resolve alertas não acionáveis. Aumentar limiares — arbitrário e frágil. Não fazer nada — a fadiga se agrava até o canal ser abandonado.

Consequências
PositivasCada alerta volta a merecer reação. Canal crível. Métrica de acionabilidade torna a fadiga visível.
NegativasExige curadoria inicial das regras herdadas e disciplina para criar regra só com dono e runbook.
RiscosJanela mal calibrada pode atrasar a detecção de um problema genuíno; mitigado por revisão dos limiares.

Antipadrões

Alerta para tudo

O caso ferramenta de monitoramento no cliente de logística-servidor de banco de dados: ruído que vira fundo de tela. Correção: só alerta acionável; o resto é painel.

Alerta sem dono ou runbook

Dispara e ninguém é responsável, ou ninguém sabe o que fazer. Correção: dono e runbook obrigatórios por constraint.

Disparar em pico isolado

Uma amostra fora do limiar gera enxurrada no flapping. Correção: janela anti-flapping; só dispara condição sustentada.

Exercício

Audite seus alertas

  1. Liste todos os alertas que sua equipe recebeu na última semana. Para cada um, responda com honestidade: exigiu ação humana imediata?
  2. Some quantos foram simplesmente ignorados ou “esperados passar”. Calcule a razão de alertas acionáveis (acionáveis ÷ total).
  3. Para cada alerta não acionável, decida: vira painel, ganha janela anti-flapping, ou é eliminado?
  4. Para cada alerta que sobreviver, verifique: ele tem dono nomeado e runbook executável? Se não, crie antes de mantê-lo ativo.
Meta: ao final, todo alerta restante deve passar no teste da Figura 8.2-a — e você deve conseguir explicar, para cada um, qual ação humana ele dispara.
✓ Checklist do tópico
  • Todo alerta exige ação humana agora (senão, é painel)
  • Todo alerta tem dono e runbook obrigatórios
  • Alertas usam janela anti-flapping (condição sustentada)
  • A razão de alertas acionáveis é monitorada (combate à fadiga)
  • Condições informativas vão para painel, não para notificação
Meta Dados · Engenharia de Plataformas · Capítulo VIII · §8.2 Próximo → 8.3 SLOs e Error Budget
Capítulo VIII · Confiabilidade, Observabilidade e Operações
8.3

SLOs e Error Budget

Ausência de alerta não é saúde — pode ser ausência de visão. Sem um objetivo explícito de confiabilidade, “está funcionando” é uma afirmação sem régua.

Pré-requisitos · 8.1 Pilares · Cap I Economia Conecta com · 8.4 Command Center · 8.2 Alertas Tempo de leitura · ~15 min

O Problema

Ausência de alerta não é saúde. O sistema de monitoramento interno criou exatamente esse engano: a queda de cobertura de monitoramento fez “sem alertas” ser lido como “tudo bem”, quando o real era “ninguém está olhando”. Silêncio pode ser paz — ou pode ser cegueira.

Sem um objetivo numérico explícito de confiabilidade, não há como saber se o sistema está bom o suficiente, nem como equilibrar a tensão permanente entre estabilidade (não mexer) e evolução (entregar novidades). “Está funcionando” é uma afirmação sem régua — e o que não tem régua não se gerencia.

O engano do silêncio

“Sem alerta = saudável” foi precisamente o que mascarou a perda de cobertura do sistema de monitoramento interno. A correção não é mais alertas — é uma medida positiva de saúde contra um alvo.

A Decisão

A diferença entre o SLO e 100% é o error budget: o quanto de falha é tolerável. Enquanto há budget, evolua rápido; quando acaba, estabilize.

A saúde de cada serviço é medida contra SLOs (Service Level Objectives) explícitos — alvos numéricos como 99,5% de sucesso ou p95 abaixo de 500ms. Os SLOs usam exatamente os indicadores mínimos do Capítulo I. O error budget é o complemento do alvo: se a meta é 99,5%, então 0,5% de falha é o orçamento disponível. Esse orçamento transforma uma discussão eterna — “podemos arriscar um deploy?” — numa leitura objetiva.

Fluxograma operacional Como o budget decide
SLO definido 99,5% sucesso/mês Error budget 0,5% pode falhar Budget consumido? Há budget → evoluir rápido Esgotado → estabilizar
Figura 8.3-a. A mesma pergunta — “podemos arriscar?” — passa a ter resposta objetiva: depende de quanto budget resta.

Importante: não estamos buscando 100%. Um SLO de 100% é impossível e caríssimo — e mata a evolução, porque qualquer mudança vira risco inaceitável. O budget existe justamente para ser gasto: ele é a licença para inovar dentro de um limite seguro.

A Implementação

O SLO como dado, por serviço

ops_slo.sqlPostgreSQL
-- SLOs explícitos por serviço. A régua de saúde, não a ausência de alerta.
CREATE TABLE ops_slo (
 id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
 servico TEXT NOT NULL, -- 'api_cadastro'
 indicador TEXT NOT NULL, -- 'taxa_sucesso' | 'latencia_p95'
 alvo NUMERIC NOT NULL, -- 99.5 (%) ou 500 (ms)
 janela_dias INTEGER NOT NULL DEFAULT 30,
 descricao TEXT NOT NULL
);
O alvo é explícito e versionado — a saúde deixa de ser opinião e vira número acordado.
PoliticaDeReleasePorBudget.phpPHP
public function podeEvoluirRapido(Slo $slo): DecisaoDeRelease
{
 $budget = $this->calc->consumo($slo);

 return match (true) {
 $budget->semDados() => DecisaoDeRelease::cautela('sem dados'),
 $budget->percentualConsumido() < 75 => DecisaoDeRelease::evoluir(), // folga
 $budget->percentualConsumido() < 100 => DecisaoDeRelease::cautela('quase'),
 default => DecisaoDeRelease::estabilizar(), // estourou
 };
}
A decisão de evoluir ou estabilizar é derivada do budget consumido — não de palpite.
Tabela de decisão Budget consumido → postura de release
Budget consumidoPosturaO que é liberadoO que é freado
< 75%EvoluirDeploys e features no ritmo normalNada — há folga
75–99%CautelaMudanças de baixo riscoMudanças arriscadas aguardam
≥ 100%EstabilizarSó correções de confiabilidadeCongela features; foco em recuperar budget
Sem dadosCautelaAguarda; investiga coberturaTudo — pode ser ponto cego (lição sistema de monitoramento interno)

Estudo de Caso

Caso aplicado

O budget como árbitro de uma sexta-feira de deploy

Contexto. Discussões recorrentes sobre “pode subir na sexta?” terminavam em impasse: metade do time citava estabilidade, a outra metade, a urgência da entrega. Sem critério objetivo, vencia quem falava mais alto.

Intervenção. O serviço ganhou um SLO de 99,5%/mês. A pergunta deixou de ser de opinião e virou consulta: qual o budget consumido neste momento da janela?

Budget consumido
38% no dia
Leitura
Folga confortável
Decisão
Deploy liberado

Resultado. A decisão tornou-se rápida e impessoal. Em outra ocasião, com 92% do budget já consumido, a mesma régua segurou um deploy arriscado — e ninguém precisou “ganhar a discussão”. O dado decidiu.

Incidente Real

Severidade · Alta

A cobertura caiu e o silêncio enganou (sistema de monitoramento interno)

  • Semanas A cobertura do sistema de monitoramento interno cai gradualmente; serviços deixam de emitir métricas sem que ninguém perceba.
  • Sintoma O canal de alertas fica em silêncio. A leitura coletiva: “está tudo estável”.
  • Realidade O silêncio não era saúde — era ausência de sinal. Problemas reais passavam sem deteção.
  • Correção Medir saúde por SLO, não por ausência de alerta. Serviço sem dados vira estado explícito de cautela, nunca “verde”.
  • Kaizen “Sem dados” passa a ser tratado como sinal de alerta de cobertura — desenvolvido em detalhe em §8.4.

Decisão Arquitetural

ADR-0043

Saúde medida por SLO e error budget

Aceito
Contexto

A perda de cobertura do sistema de monitoramento interno mostrou que ausência de alerta é régua falha. Decisões de release dependiam de intuição e da tensão estabilidade-vs-evolução.

Decisão

Cada serviço crítico tem SLOs explícitos (indicadores do Cap I). O error budget é derivado do alvo e das métricas reais, e governa a decisão de evoluir vs estabilizar. Nenhum SLO é de 100%.

Alternativas consideradas

Medir por uptime binário — grosseiro demais. Decidir release por comitê — lento e político. Alvo de 100% — impossível e anti-evolução.

Consequências
PositivasDecisão objetiva e impessoal. Saúde mensurável. Evolução segura dentro do budget.
NegativasExige definir e manter SLOs realistas e medir o budget continuamente.
RiscosSLO mal calibrado engana; revisão periódica e dados históricos mitigam.

Antipadrões

“Sem alerta = saudável”

O falso senso do sistema de monitoramento interno. Correção: medir contra SLO explícito; “sem dados” nunca é “verde”.

SLO de 100% ou sem budget

Alvo impossível mata a evolução; alvo sem consequência prática é decorativo. Correção: alvo realista com budget que governa o release.

Exercício

Defina um SLO e calcule seu budget

  1. Escolha um serviço crítico. Defina um SLO realista de taxa de sucesso (ex.: 99,5% em 30 dias). Justifique o alvo com dados históricos, não com um número redondo.
  2. Calcule o error budget em valor absoluto: sobre o volume mensal de requisições, quantas falhas o orçamento permite?
  3. Olhe o mês corrente: qual percentual do budget já foi consumido? A postura indicada seria evoluir, cautela ou estabilizar?
  4. Liste os serviços que hoje você considera “saudáveis” só porque não alertam. Algum deles pode estar sem dados — um ponto cego à espera?
Meta: substituir “acho que está estável” por “consumimos X% do budget, então a postura é Y”.
✓ Checklist do tópico
  • Cada serviço crítico tem SLOs explícitos (indicadores do Cap I)
  • O error budget é derivado do SLO e das métricas reais
  • A decisão evoluir-vs-estabilizar segue o budget
  • Nenhum SLO é de 100% (há budget para falhar)
  • Saúde é medida contra o SLO, não contra a ausência de alerta
Meta Dados · Engenharia de Plataformas · Capítulo VIII · §8.3Próximo → 8.4 O Command Center
Capítulo VIII · Confiabilidade, Observabilidade e Operações
8.4

O Command Center

Sem uma visão única, o operador pula entre dez ferramentas para entender um incidente — e ninguém vê o quadro completo. Foi essa fragmentação que deixou a queda de cobertura passar despercebida.

Pré-requisitos · 8.1 Pilares · 8.3 SLOs · Cap IV Eventos Conecta com · 8.2 Alertas · Cap VI Integrações Tempo de leitura · ~14 min

O Problema

A fragmentação é o inimigo silencioso da operação. Sem uma visão única, entender um incidente exige pular entre dez ferramentas — uma para métricas, outra para logs, outra para o estado das integrações, outra para as filas — e remontar o quadro de cabeça, sob pressão. Ninguém vê o todo.

Foi essa fragmentação que permitiu à queda de cobertura do sistema de monitoramento interno (§8.3) passar despercebida: não havia um lugar único onde a ausência de sinal de um serviço saltasse aos olhos. O Command Center que descrevemos aqui — um “painel de observabilidade interno” — resolve isso, e combina diretamente com a operação da plataforma, que já faz NOC e monitoramento sobre uma frota de ~40 servidores virtuais.

O princípio que evita o retrabalho

O Command Center não é uma nova fonte de dados. Ele é a janela, não o sensor: consolida o que os três pilares (§8.1), os SLOs (§8.3) e os eventos (Cap IV) já produzem.

A Decisão

A visão operacional única materializa o Genba: o lugar onde se olha o trabalho real, numa tela só — e que vigia a própria cobertura.

O Command Center consolida saúde de serviços (por SLO), estado das integrações (Cap VI), filas, banco e os alertas ativos (§8.2). Cada serviço aparece com um status derivado do seu budget — verde, amarelo ou vermelho — não com um número solto que ninguém sabe interpretar. E, criticamente, ele vigia a si mesmo: um serviço que parou de emitir sinal não é “saudável”, é um ponto cego que precisa gritar.

Arquitetura lógica Consolidação de fontes
FONTES (JÁ EXISTEM) Métricas (8.1) SLOs (8.3) Alertas (8.2) Integrações(Cap VI) Eventos(Cap IV) COMMAND CENTER visão operacional única Saúde por SLOverde/amarelo/vermelho Filas · banco · perf Integrações/parceiro Incidentes + runbooks
Figura 8.4-a. Fluxo unidirecional: as fontes alimentam a visão; a visão não duplica coleta. Reúso, não reimplementação.

A Implementação

Status derivado do SLO, não de valores soltos

VisaoDeSaude.phpPHP
public function montar(int $tenantId): PainelDeSaude
{
 $servicos = [];
 foreach ($this->slos->doTenant($tenantId) as $slo) {
 $consumo = $this->budget->consumo($slo); // reusa 8.3
 $servicos[] = new StatusDeServico(
 nome: $slo->servico(),
 status: $this->statusPorBudget($consumo), // verde/amarelo/vermelho
 budgetConsumido: $consumo->percentualConsumido(),
 );
 }
 return new PainelDeSaude(
 servicos: $servicos,
 alertasAtivos: $this->alertas->ativos($tenantId),
 integracoes: $this->integracoes->statusPorParceiro($tenantId), // Cap VI
 );
}
O Command Center compõe — não recalcula. Cada peça vem de quem já é responsável por ela.

O vigia de cobertura: o anti-sistema de monitoramento interno

Esta é a peça que faltava no sistema de monitoramento interno. Um serviço que deveria emitir métricas e parou não é “verde” — é um ponto cego. O vigia detecta a ausência de sinal e a transforma em alerta.

VigiaDeCobertura.phpPHP
// "Sem dados" NUNCA é "tudo bem". Lição direta do sistema de monitoramento interno.
public function servicosSemSinal(int $tenantId, int $minutosSemDados = 10): array
{
 $esperados = $this->metricas->servicosEsperados($tenantId);
 $cegos = [];
 foreach ($esperados as $servico) {
 $ultima = $this->metricas->ultimaAmostra($servico);
 if ($ultima === null || $ultima->minutosAtras() > $minutosSemDados) {
 $cegos[] = $servico; // ponto cego, não saúde → vira alerta
 }
 }
 return $cegos;
}
Vigiar a própria observabilidade é o que impede um segundo incidente do tipo sistema de monitoramento interno.
Tabela de decisão Como o Command Center colore um serviço
Sinal observadoStatus exibidoOrigemAção sugerida
Budget < 75%, emitindoVerdeSLO (8.3)Nenhuma; operação normal
Budget 75–99%, emitindoAmareloSLO (8.3)Atenção; evitar mudança arriscada
Budget ≥ 100%VermelhoSLO (8.3)Estabilizar; abrir investigação
Sem amostra há > 10minPonto cegoVigia de coberturaAlerta: serviço parou de emitir (anti-sistema de monitoramento interno)

Estudo de Caso

Caso aplicado

Uma tela para a frota de ~40 servidores virtuais

Contexto. A operação acompanhava a frota saltando entre o ferramenta de monitoramento, os logs de cada host e os painéis de integração. Um incidente exigia abrir várias telas e correlacionar de cabeça — lento e sujeito a erro sob pressão.

Intervenção. O Command Center passou a consolidar, numa visão só, a saúde por SLO de cada serviço, o estado das integrações por parceiro (Cap VI), as filas e o vigia de cobertura. Nada de coleta nova — apenas a janela única sobre o que já era emitido.

Antes
~10 telas por incidente
Depois
1 visão consolidada
Novo guardião
Ponto cego vira alerta

Resultado. O tempo até entender “o que está acontecendo” caiu drasticamente, e — o ganho decisivo — um serviço que silenciasse deixaria de se esconder atrás do silêncio. O Genba, antes espalhado, passou a caber numa tela.

Incidente Real

Severidade · Média

O ponto cego que só apareceu quando alguém procurou

  • Antes Um coletor de métricas de um subconjunto de hosts falha silenciosamente. As telas fragmentadas não destacam a ausência.
  • Sintoma Os painéis daqueles hosts ficam “sem dados” — interpretado, por hábito, como “nada de errado”.
  • Descoberta O problema só vem à tona quando um operador procura ativamente pelo host e estranha o vazio.
  • Correção O vigia de cobertura passa a listar serviços esperados sem amostra recente e a emitir alerta de ponto cego.
  • Kaizen O Command Center ganha um indicador permanente de cobertura: quantos serviços esperados estão emitindo agora.

Decisão Arquitetural

ADR-0044

Command Center consolida fontes e vigia a própria cobertura

Aceito
Contexto

A operação sobre ~40 servidores virtuais dependia de múltiplas telas. A fragmentação atrasava o diagnóstico e mascarou a perda de cobertura do sistema de monitoramento interno.

Decisão

Construir uma visão operacional única que consolida o que pilares, SLOs, alertas e eventos já emitem — sem nova coleta — e que vigia a própria cobertura, tratando “sem sinal” como alerta de ponto cego.

Alternativas consideradas

Adotar só uma ferramenta externa — não consolida o contexto de negócio (SLOs, integrações). Reimplementar coleta — duplica esforço e diverge. Manter telas separadas — perpetua a fragmentação.

Consequências
PositivasDiagnóstico mais rápido. Ponto cego deixa de se esconder. Genba numa tela.
NegativasRequer manter o mapa de serviços esperados atualizado para o vigia funcionar.
RiscosMapa desatualizado gera falso ponto cego ou esconde um real; revisão periódica mitiga.

Antipadrões

“Sem dados = saudável”

O ponto cego do sistema de monitoramento interno. Correção: vigia de cobertura; serviço sem sinal vira alerta, nunca verde.

Command Center como nova fonte

Reimplementar coleta diverge da realidade. Correção: consolida o que pilares, SLOs e eventos já emitem.

Exercício

Desenhe sua visão única

  1. Liste todas as telas que sua equipe abre durante um incidente típico. Quantas são? Quanto tempo se perde correlacionando entre elas?
  2. Para cada fonte, identifique: ela já existe e só precisa ser consolidada, ou exigiria coleta nova? (Evite a coleta nova — prefira reúso.)
  3. Defina o mapa de “serviços esperados”: o que deveria estar emitindo agora? É contra essa lista que o vigia detecta pontos cegos.
  4. Especifique a regra do ponto cego: após quantos minutos sem amostra um serviço esperado vira alerta?
Meta: uma única tela onde o status de cada serviço deriva do SLO e onde um serviço silencioso não consegue se esconder.
✓ Checklist do tópico
  • O Command Center é a visão operacional única (Genba)
  • Consolida o que pilares, SLOs e eventos já emitem (não é nova fonte)
  • Status de serviço é derivado do SLO/budget (verde/amarelo/vermelho)
  • Vigia a própria cobertura: serviço sem sinal vira alerta de ponto cego
  • Mostra saúde, filas, banco, integrações e incidentes ativos
Meta Dados · Engenharia de Plataformas · Capítulo VIII · §8.4Próximo → 8.5 Runbooks e Post-mortem
Capítulo VIII · Confiabilidade, Observabilidade e Operações
8.5

Runbooks e Post-mortem

Sem isto, o conhecimento operacional vive na cabeça de uma pessoa — e some quando ela está de férias. O incidente vira improviso; a lição, esquecimento.

Pré-requisitos · 8.2 Alertas · Cap I Hansei/Kaizen Conecta com · 8.4 Command Center · Cap VII Incidentes Tempo de leitura · ~14 min

O Problema

O conhecimento operacional, quando vive só na cabeça de uma pessoa, é um ponto único de falha humano. Some nas férias, evapora na saída, e deixa o plantonista de madrugada sozinho diante de um problema que alguém já sabia resolver — mas não escreveu.

Os tópicos anteriores tornaram o runbook obrigatório em cada alerta. Falta fechar o ciclo: o que é um bom runbook — aquele que funciona às 3h da manhã, na mão de quem não escreveu o sistema — e como o post-mortem transforma cada incidente em aprendizado de sistema, em vez de culpa de pessoa.

O teste das 3h da manhã

Um runbook vago — “verificar o sistema e reiniciar se necessário” — falha exatamente quando mais importa. O bom runbook é executável por quem não conhece o sistema inteiro.

A Decisão

Todo post-mortem foca a causa sistêmica, sem caça às bruxas, e sempre produz ao menos uma melhoria de sistema — um teste, um alerta, um guard-rail. Nunca “ter mais cuidado”.

Cada alerta tem um runbook executável: passos concretos, verificáveis, que um operador de plantão segue sem entender o sistema todo. Cada incidente gera um post-mortem guiado pelos princípios Kanso do Capítulo I — Hansei (reflexão honesta, sem buscar culpados) e Kaizen (melhoria contínua). A lição não é um conselho moral; é uma mudança no sistema que impede a recorrência.

Anatomia de um runbook executável

O runbook abaixo trata o problema real do cliente de logística-firebird: conexões órfãs acumulando, originadas de processos do sistema de rastreamento que não as fechavam. Note que cada passo tem uma verificação — não “reinicie”, mas “execute e confirme que a contagem caiu”.

runbook-firebird-conexoes-orfas.mdMarkdown
# Runbook: Firebird com conexões órfãs acumulando
Alerta: firebird_conexoes_orfas · Dono: Infra · Severidade: crítico

## Sintoma
Conexões ativas crescendo sem liberação; performance degradando.
(Histórico: processos do sistema de rastreamento não fechando conexões.)

## Diagnóstico (verificável)
1. Confirmar contagem: [comando de verificação]
 - Normal: < 50. Se > 150, prosseguir.
2. Identificar origem: conexões do IP do sistema de rastreamento?
 - Se sim, é o padrão conhecido → contenção.

## Contenção
3. Executar o connection-killer: [script]
 - Verificar: a contagem caiu para < 50?
4. Se reincidir em < 1h, ativar o circuit breaker da integração (Cap II).

## Recuperação
5. Confirmar performance normalizada (Command Center, 8.4).

## Escalonamento
Se não resolver em 30min, acionar [contato] e abrir incidente.
Cada passo é verificável e há escalonamento explícito: o plantonista nunca fica sem próximo passo.
Tabela de decisão O que distingue um runbook bom de um ruim
AspectoRunbook ruimRunbook executável
Passos“Verificar e reiniciar”Comando + resultado esperado
DiagnósticoAusenteLimiares claros (normal < 50)
Quem executaSó quem escreveu o sistemaQualquer plantonista
Quando paraIndefinidoEscalonamento com prazo
Onde viveNa cabeça de alguémVersionado, ao lado do alerta

A Implementação

O post-mortem que força o aprendizado

A disciplina do Kaizen não é confiada à boa vontade — é imposta pelo código. Um post-mortem sem ao menos uma lição acionável é incompleto, e uma lição vaga como “ter mais cuidado” é rejeitada.

PostMortem.phpPHP
public static function registrar(
 string $incidenteId, string $causaSistemica, // não "fulano errou"
 Timeline $timeline, array $licoesKaizen,
): self {
 // Regra: sem ao menos uma lição acionável, o post-mortem é incompleto.
 if ($licoesKaizen === []) {
 throw new PostMortemSemLicao(
 'Todo post-mortem deve gerar ao menos uma melhoria de sistema.'
 );
 }
 // Cada lição precisa virar teste/alerta/guard-rail — não conselho vago.
 foreach ($licoesKaizen as $licao) {
 if (! $licao->ehAcionavel()) {
 throw new LicaoNaoAcionavel($licao->texto());
 }
 }
 return new self($incidenteId, $causaSistemica, $timeline, $licoesKaizen);
}
“Ter mais cuidado” lança LicaoNaoAcionavel. A disciplina é estrutural, não opcional.
Por que isto funciona

Forçar a lição a ser acionável muda a cultura sem precisar pregá-la. O sistema não aceita o post-mortem até que o aprendizado vire uma mudança concreta — um teste, um alerta, um guard-rail.

Estudo de Caso

Caso aplicado

O conhecimento que saiu da cabeça e virou runbook

Contexto. A resolução do problema recorrente de conexões órfãs no cliente de logística-firebird vivia na experiência de uma pessoa. Quando ela não estava disponível, o mesmo problema levava horas a mais para ser contido.

Intervenção. O conhecimento foi destilado num runbook executável, versionado e ligado ao alerta correspondente. Diagnóstico com limiares, contenção com verificação, e escalonamento com prazo.

Antes
Depende de uma pessoa
Depois
Qualquer plantonista resolve
Tempo de contenção
Previsível e curto

Resultado. O incidente deixou de depender de quem estava de plantão. O conhecimento, antes tácito e frágil, virou um ativo versionado — e a próxima ocorrência foi contida seguindo passos, não improvisando.

Incidente Real

Severidade · Crítica

Conexões órfãs no cliente de logística-firebird (sistema de rastreamento)

  • T+0 Processos do sistema de rastreamento abrem conexões ao Firebird e não as fecham. As conexões órfãs começam a acumular.
  • T+20min A contagem ultrapassa o normal; a performance do banco degrada de forma perceptível.
  • T+30min Sem runbook, a contenção é improvisada — identificar a origem e matar as conexões leva tempo precioso.
  • Contenção Connection-killer das conexões órfãs; contagem volta ao normal. Para reincidência, circuit breaker da integração (Cap II).
  • Kaizen Runbook versionado criado; alerta firebird_conexoes_orfas ligado a ele. A próxima ocorrência vira procedimento, não pânico.

Decisão Arquitetural

ADR-0045

Runbook executável por alerta; post-mortem com Kaizen obrigatório

Aceito
Contexto

Conhecimento operacional tácito (caso cliente de logística-firebird) tornava a resposta dependente de pessoas específicas. Post-mortems sem ação concreta não evitavam recorrência.

Decisão

Cada alerta tem runbook executável com passos verificáveis e escalonamento. Cada incidente gera post-mortem com causa sistêmica (sem culpados) e ao menos uma lição acionável — imposta por código.

Alternativas consideradas

Documentação livre — vira prosa vaga, não executável. Post-mortem opcional — não acontece sob pressão de entrega. Lição como texto livre — degenera em “ter mais cuidado”.

Consequências
PositivasResposta independente de pessoas. Recorrência evitada por melhoria concreta. Cultura sem culpa.
NegativasExige escrever e manter runbooks; disciplina de fechar post-mortem com ação.
RiscosRunbook desatualizado engana; revisão periódica e uso real em incidentes mitigam.

Antipadrões

Conhecimento na cabeça de uma pessoa

Some nas férias. Correção: runbook versionado, executável por qualquer plantonista.

Post-mortem como tribunal

Procurar culpado em vez de causa (anti-Hansei). Correção: foco na causa sistêmica; a lição vira Kaizen acionável.

Exercício

Escreva um runbook que passe no teste das 3h

  1. Escolha um problema recorrente que hoje só uma pessoa sabe resolver. Peça a ela que descreva os passos — e transforme cada um em comando + resultado esperado.
  2. Adicione limiares de diagnóstico (“normal < X; se > Y, prosseguir”) e um passo de escalonamento com prazo (“se não resolver em 30min, acionar Z”).
  3. Entregue o runbook a alguém que não conhece o sistema e peça que o siga. Onde a pessoa travar, o runbook está incompleto.
  4. No próximo post-mortem, exija ao menos uma lição acionável. Se surgir “ter mais cuidado”, reescreva-a como teste, alerta ou guard-rail concreto.
Meta: um runbook que um plantonista sem contexto execute do início ao fim, e um post-mortem que sempre deixe o sistema melhor do que antes.
✓ Checklist do tópico
  • Cada alerta tem um runbook executável com passos verificáveis
  • Runbooks incluem diagnóstico, contenção, recuperação e escalonamento
  • Todo incidente gera post-mortem com causa sistêmica, sem culpados
  • Todo post-mortem produz ao menos uma lição acionável (Kaizen)
  • O conhecimento operacional vive em runbooks versionados
Meta Dados · Engenharia de Plataformas · Capítulo VIII · §8.5Fim do Capítulo VIII · voltar ao início
Meta Dados · Engenharia de Plataformas
Capítulo IX

IA Corporativa
e Agentes

Colocar modelos de linguagem para trabalhar dentro de uma plataforma séria — sem que eles contornem o domínio, inventem fatos ou ajam sem rastro. O agente como cidadão da arquitetura, não como exceção a ela.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo IX
Capítulo IX · IA Corporativa e Agentes
9.1

O Agente como Cidadão

Um agente de IA é sedutor justamente por parecer capaz de fazer qualquer coisa. É essa a armadilha: dar a ele acesso direto ao banco, ou um "modo deus" que contorna o domínio. Um agente não é exceção à arquitetura — é mais um cidadão dela, sujeito às mesmas regras.

Pré-requisitos · Cap II Casos de uso · Cap III §3.2 · Cap VII §7.1Conecta com · 9.3 Determinístico · 9.4 GuardrailsTempo de leitura · ~14 min

O Problema

Um agente de IA — um modelo de linguagem capaz de decidir ações e executá-las através de ferramentas — é sedutor precisamente porque parece capaz de fazer qualquer coisa. Peça, e ele tenta. Essa versatilidade convida a um atalho perigoso: para que o agente "consiga resolver", dá-se a ele acesso direto ao banco de dados, ou uma chave de administrador, ou um conjunto de ferramentas que ignora as fronteiras que o resto da plataforma respeita. No afã de torná-lo capaz, faz-se dele uma exceção à arquitetura.

E uma exceção à arquitetura é uma exceção à segurança. Um agente com acesso direto ao banco contorna o isolamento multiempresa (§4.2 Cap IV): nada o impede de ler dados de um cliente ao responder para outro. Um agente com permissões irrestritas viola o menor privilégio (§7.1 Cap VII): se ele for induzido — por um prompt malicioso, por uma instrução ambígua — a fazer algo destrutivo, nada o detém. Um agente que muta o estado diretamente burla a validação, a auditoria e a idempotência que toda operação legítima atravessa. O modelo é não-determinístico por natureza; dar a esse não-determinismo acesso irrestrito ao núcleo é convidar o caos a operar sem supervisão. O problema não é o agente ser capaz — é ele ser capaz por fora das regras.

O agente que contorna a arquitetura

Acesso direto ao banco ou poder de administrador dá ao agente uma rota que ignora isolamento, permissão, validação e auditoria. Um modelo não-determinístico com acesso irrestrito é um risco sistêmico. Correção: o agente age pelas mesmas portas que todo o resto — ações nomeadas e casos de uso, sob as mesmas regras.

A Decisão

O agente é um cidadão da plataforma, não uma exceção. Ele age exclusivamente através das ações nomeadas (§3.2) e dos casos de uso — sujeito a permissão, isolamento, validação e auditoria como qualquer outro chamador.

Um agente não recebe poderes especiais; recebe ferramentas que são as ações nomeadas e os casos de uso da plataforma. Quando o modelo decide "aprovar o pedido", ele não toca o banco — ele invoca a ação nomeada aprovar_pedido (§3.2 Cap III), exatamente a mesma que um botão na tela invocaria. E essa ação, ao ser chamada pelo agente, atravessa tudo o que atravessaria de qualquer forma: a verificação de permissão (§7.1 — negar por padrão vale para o agente), o isolamento por tenant (§4.2 — o agente opera no contexto de uma empresa e não enxerga outras), a validação de domínio, e o registro de auditoria. O agente é, arquiteturalmente, mais um chamador das mesmas portas. Ele decide o quê fazer com a inteligência do modelo; como isso acontece é o caminho disciplinado de sempre. Assim, toda a fortaleza construída nos volumes anteriores protege a plataforma do agente tanto quanto de qualquer outro ator — e o não-determinismo do modelo fica contido pelas fronteiras determinísticas do domínio.

Arquitetura lógica O agente age pelas portas da plataforma
AGENTE (LLM)decide o quê fazer AÇÕES NOMEADAS + CASOS DE USOa MESMA porta de toda a plataformapermissão · tenant · validação · auditoria DOMÍNIOintocado, íntegro ✕ acesso direto ao banco / god-mode o agente é só mais um chamador — sujeito às MESMAS regras
Figura 9.1-a. O agente decide, mas age pelas ações nomeadas e casos de uso — a mesma porta de todos. Permissão, tenant, validação e auditoria valem para ele. O acesso direto ao banco é proibido.

As ferramentas do agente são ações nomeadas

FerramentasDoAgente.phpPHP
// As "ferramentas" do agente SÃO as ações nomeadas (§3.2) — nada mais.
final class FerramentasDoAgente
{
    public function __construct(
        private readonly RegistroDeAcoes $acoes,       // §3.2
        private readonly Autorizador $autz,           // §7.1
    ) {}

    public function executar(Agente $agente, string $nomeAcao, Contexto $ctx): Resultado
    {
        // O agente atua SOB a identidade/permissões de um usuário — não acima.
        if (! $this->autz->pode($agente->usuarioDelegante(), $nomeAcao, $ctx->recurso())) {
            throw new AcessoNegado();  // negar por padrão vale para a IA
        }

        // Decide o LLM; executa a ação nomeada — a MESMA porta de todos.
        return $this->acoes->resolver($nomeAcao)->executar($ctx);
    }
}
O agente só pode invocar ações registradas, sob a permissão do usuário que ele representa. Não há rota para o banco; não há poder acima do delegante.
contexto_do_agente.phpPHP
// O agente opera SEMPRE no contexto de uma empresa (tenant) — §4.2.
final class SessaoDeAgente
{
    public function iniciar(Usuario $delegante): Contexto
    {
        // Herda tenant e permissões do usuário. O RLS do banco (§4.2)
        // isola o agente exatamente como isolaria o próprio usuário.
        return Contexto::para(
            tenant: $delegante->tenantId(),
            usuario: $delegante,
            origem: Origem::AGENTE,   // auditável: foi a IA quem agiu
        );
    }
}
A sessão do agente herda o tenant e as permissões do usuário delegante — o isolamento do Cap IV vale para ele. E a origem "AGENTE" deixa rastro.

A Verificação

AgenteCidadaoTest.phpPHP
final class AgenteCidadaoTest extends TestCase
{
    public function test_agente_nao_excede_a_permissao_do_delegante(): void
    {
        // Usuário só pode LER. O agente que o representa também só pode ler.
        $agente = $this->agenteDelegadoPor($this->usuarioComPermissao('ler'));

        $this->expectException(AcessoNegado::class);
        $this->ferramentas->executar($agente, 'aprovar_pedido', $this->ctx());  // negado
    }

    public function test_agente_nao_ve_outro_tenant(): void
    {
        // O agente do tenant 1 não enxerga dados do tenant 2 (§4.2 RLS).
        $this->comoTenant(2)->criarPedido();
        $agente = $this->agenteDoTenant(1);
        $this->assertEmpty($this->ferramentas->executar($agente, 'listar_pedidos', $this->ctx())->itens());
    }
}
Os testes provam que o agente herda — não excede — as permissões e o isolamento do delegante. A IA não é uma brecha nas defesas existentes.
Tabela de decisão Agente com god-mode vs agente cidadão
DimensãoAgente com acesso diretoAgente como cidadão
Acesso a dadosBanco direto, sem isolamentoCasos de uso, RLS ativo (§4.2)
PermissãoIrrestrita / adminA do delegante (§7.1)
Validação de domínioContornadaAtravessada como sempre
AuditoriaAd hoc ou nenhumaRegistrada, origem AGENTE
Prompt maliciosoPode causar estrago amploLimitado ao privilégio mínimo

Estudo de Caso

Caso aplicado

O assistente que não virou uma porta dos fundos

Contexto. A plataforma ganhou um assistente de IA capaz de executar ações em nome do usuário — consultar, criar, aprovar. A forma mais rápida de implementá-lo seria dar ao assistente acesso amplo ao banco e às operações, para que ele "conseguisse fazer tudo".

God-mode
IA contorna as defesas
Cidadão
IA sujeita às mesmas regras
Ganho
Capaz, sem ser uma brecha

Decisão. Tratar o assistente como um cidadão da plataforma: suas ferramentas são as ações nomeadas (§3.2), ele opera sob a identidade e as permissões do usuário que o invoca, e toda chamada atravessa autorização (§7.1), isolamento por tenant (§4.2), validação e auditoria.

Resultado. O assistente é capaz — decide com a inteligência do modelo o que fazer — mas jamais excede o que o próprio usuário poderia fazer manualmente. Um prompt malicioso, na pior das hipóteses, só consegue o que o usuário delegante já tinha permissão de fazer. A IA tornou-se uma nova forma de acionar a plataforma, não uma porta dos fundos que contorna suas defesas.

Incidente Real

Severidade · Alta

A ferramenta ampla demais que o agente recebeu

  • Antes Num protótipo, um agente recebeu uma ferramenta genérica de "executar consulta" sobre o banco, por conveniência, para acelerar o desenvolvimento.
  • Risco Essa ferramenta contornava o isolamento por tenant e a permissão: induzido por uma instrução ambígua, o agente poderia, em tese, alcançar dados além do escopo do usuário.
  • Diagnóstico A ferramenta genérica era uma exceção à arquitetura — uma rota que ignorava as defesas que toda operação legítima atravessa.
  • Correção A ferramenta genérica foi removida e substituída por ações nomeadas específicas, cada uma sujeita a permissão, isolamento e auditoria.
  • Kaizen Vira regra: agentes nunca recebem acesso bruto ao banco; suas ferramentas são ações nomeadas, sob o privilégio do delegante.

Decisão Arquitetural

ADR-0046

O agente age pelas ações nomeadas e casos de uso, nunca por fora

Aceito
Contexto

Um agente de IA parece capaz de tudo, o que convida a dar-lhe acesso direto ao banco ou poder de administrador. Mas isso o torna uma exceção à arquitetura — contornando isolamento (§4.2), permissão (§7.1), validação e auditoria. Um modelo não-determinístico com acesso irrestrito é risco sistêmico.

Decisão

O agente é um cidadão da plataforma: suas ferramentas são exclusivamente as ações nomeadas (§3.2) e os casos de uso (Cap II). Ele opera sob a identidade e as permissões do usuário delegante, e toda chamada atravessa autorização, isolamento por tenant, validação e auditoria (origem AGENTE). O modelo decide o quê; o caminho disciplinado define o como.

Alternativas consideradas

Acesso direto ao banco — contorna todas as defesas. Permissões de admin para o agente — viola menor privilégio. Ferramenta genérica de query — rota não-isolada e não-auditada.

Consequências
PositivasA IA herda, não excede, as defesas existentes. Prompt malicioso limitado ao privilégio do delegante. Ações do agente auditáveis. Não-determinismo contido pelo domínio.
NegativasExige expor as capacidades do agente como ações nomeadas e propagar a identidade do delegante.
RiscosRestringir o agente às ações nomeadas limita o que ele faz; aceitável — capacidades novas viram novas ações, deliberadamente.

Antipadrões

Acesso direto ao banco para o agente

Dar ao agente uma rota que ignora isolamento e permissão. Correção: ferramentas são ações nomeadas; o banco só pelo caminho de sempre.

Agente com identidade própria de superusuário

O agente agindo acima de qualquer usuário. Correção: o agente representa um usuário delegante e herda suas permissões — nunca as excede.

Exercício

Audite os poderes do seu agente

  1. Se o seu sistema tem (ou terá) um agente de IA, liste as ferramentas que ele pode usar. Cada uma passa pelas mesmas defesas (permissão, isolamento, validação, auditoria) que uma operação normal?
  2. Procure a ferramenta mais ampla que o agente possui. Ela é uma ação específica e auditável, ou um acesso genérico que contorna as fronteiras?
  3. Faça o teste do delegante: o agente pode fazer algo que o usuário que o invocou não poderia fazer manualmente? Se sim, ele excede o privilégio — uma brecha.
  4. Faça o teste do prompt malicioso: se alguém induzisse o agente a agir mal, qual seria o alcance máximo do estrago? Está limitado ao privilégio mínimo do delegante?
Meta: garantir que o seu agente seja um cidadão da arquitetura — capaz, mas sujeito às mesmas defesas que todo o resto, sem rota para contorná-las.
✓ Checklist do tópico
  • As ferramentas do agente são ações nomeadas e casos de uso, não acesso bruto
  • O agente opera sob a identidade e as permissões de um usuário delegante
  • Autorização, isolamento por tenant e validação valem para o agente
  • Toda ação do agente é auditada, com origem identificada como IA
  • Um prompt malicioso fica limitado ao privilégio mínimo do delegante
Meta Dados · Engenharia de Plataformas · Capítulo IX · §9.1Próximo → 9.2 RAG Governado
Capítulo IX · IA Corporativa e Agentes
9.2

RAG Governado

Ancorar as respostas da IA nos dados da própria plataforma resolve a invenção de fatos — mas abre um abismo: se a recuperação não respeitar permissões, o agente responde a um usuário com dados que ele jamais poderia ver. A recuperação herda o isolamento, ou vira um vazamento.

Pré-requisitos · 9.1 · Cap IV §4.2 RLS · §4.4 Event StoreConecta com · 9.4 Guardrails · Cap VII §7.4Tempo de leitura · ~14 min

O Problema

Um modelo de linguagem, perguntado sobre o estado da plataforma — "qual o saldo deste cliente?", "o que aconteceu com este pedido?" —, responde do jeito que sabe: gerando texto plausível. E texto plausível não é texto verdadeiro. O modelo inventa um número, fabrica um histórico, alucina uma resposta confiante e errada. Para uma plataforma corporativa, onde as respostas precisam ser factuais, isso é inaceitável. A solução conhecida é o RAG (geração ancorada em recuperação): em vez de o modelo inventar, ele recupera os dados reais da plataforma e responde com base neles.

Mas o RAG, mal feito, troca um problema por outro pior. Para ancorar a resposta, é preciso recuperar dados — e se essa recuperação não respeitar as permissões do usuário, abre-se um abismo. O agente, ao responder uma pergunta do usuário A, pode recuperar e revelar dados do usuário B, ou de outra empresa inteira. O isolamento multiempresa (§4.2 Cap IV), tão arduamente protegido no banco, é contornado pela camada de recuperação da IA: a busca semântica varre um índice que mistura os dados de todos os tenants, e devolve ao agente — e ao usuário — o que não deveria. RAG sem governança de permissão é o vazamento entre empresas reencenado por uma porta nova, e ainda mais traiçoeira, porque vem embrulhado numa resposta útil. Ancorar nos dados resolve a invenção; não filtrar os dados cria a exposição.

O RAG que vaza entre empresas

Se a recuperação que ancora a resposta não respeitar as permissões do usuário, o agente entrega dados de outro tenant. É o vazamento multiempresa (§4.2) por uma nova porta — embrulhado numa resposta útil. Correção: a recuperação herda o isolamento e a permissão, sempre.

A Decisão

A recuperação é governada: só recupera o que o usuário poderia ver, com o mesmo isolamento do banco (§4.2). E toda afirmação ancorada carrega sua proveniência — a fonte verificável de onde o fato veio.

O RAG da plataforma é governado por permissão em cada recuperação. Antes de qualquer busca, o contexto do usuário (tenant, permissões) é aplicado como filtro: a recuperação semântica só considera os documentos e dados que aquele usuário poderia acessar pelas vias normais. O mesmo isolamento que o Row-Level Security (§4.2) impõe no banco é imposto na camada de recuperação — um agente representando o usuário A jamais recupera dados do tenant B, porque o índice é consultado sob o escopo do delegante. A fonte primária dessa recuperação são os dados próprios da plataforma, com o Event Store (§4.4) como verdade dos fatos. Além disso, toda afirmação que o agente faz vem com sua proveniência: a citação da fonte de onde o fato foi recuperado. O usuário pode verificar; a auditoria pode rastrear; e a invenção fica exposta, porque uma afirmação sem fonte é, por construção, suspeita. Assim, o RAG entrega o melhor dos dois mundos — respostas factuais, ancoradas nos dados reais, e seguras, respeitando exatamente o que cada um pode ver.

Fluxograma operacional Recuperação filtrada por permissão, com proveniência
Pergunta RecuperaçãoFILTRADA por permissão só os dadosque o usuário pode ver RESPOSTA ancoradacom proveniência (citações)não inventada recuperar sem filtrar = vazar entre empresas
Figura 9.2-a. A recuperação é filtrada pela permissão do usuário antes de ancorar a resposta — o isolamento do §4.2 vale aqui. A resposta vem com proveniência; recuperar sem filtrar vazaria entre empresas.

Recuperar sob o escopo do usuário

RecuperacaoGovernada.phpPHP
// A recuperação SÓ vê o que o usuário poderia ver — isolamento (§4.2).
final class RecuperacaoGovernada
{
    public function recuperar(string $consulta, Contexto $ctx): array
    {
        // O escopo do usuário (tenant + permissões) FILTRA a busca.
        $trechos = $this->indice->buscarSemantico(
            consulta: $consulta,
            filtro: Filtro::doEscopo($ctx->tenantId(), $ctx->permissoes()),
        );

        // Defesa em profundidade: re-checa cada trecho contra a permissão.
        return array_filter($trechos,
            fn($t) => $this->autz->podeVer($ctx->usuario(), $t->fonte()));
    }
}
O filtro de escopo entra na busca, e cada trecho é re-checado contra a permissão — defesa em profundidade (§4.2). O agente só vê o que o usuário veria.
resposta_com_proveniencia.phpPHP
// Toda afirmação vem com sua FONTE. Sem fonte = suspeita.
final class RespostaAncorada
{
    public function montar(string $consulta, Contexto $ctx): Resposta
    {
        $trechos = $this->recuperacao->recuperar($consulta, $ctx);

        // O modelo responde ANCORADO nos trechos, citando cada fonte.
        $resposta = $this->modelo->gerar(
            instrucao: 'Responda SOMENTE com base nos trechos. Cite a fonte de cada fato.',
            contexto: $trechos,
        );

        // A proveniência acompanha a resposta — verificável, auditável.
        return new Resposta($resposta->texto(), proveniencia: $trechos);
    }
}
O modelo é instruído a responder só com base no recuperado e a citar fontes. A proveniência viaja junto — o usuário verifica de onde veio cada fato.

A Verificação

RagGovernadoTest.phpPHP
final class RagGovernadoTest extends TestCase
{
    public function test_recuperacao_nao_cruza_tenant(): void
    {
        // Documento sensível pertence ao tenant 2.
        $this->indexarDocumento(tenant: 2, texto: 'segredo do tenant 2');

        // Recuperação no escopo do tenant 1 NÃO pode trazê-lo.
        $trechos = $this->recuperacao->recuperar('segredo', $this->ctxDoTenant(1));

        $this->assertEmpty($trechos, 'VAZAMENTO: RAG cruzou tenants!');
    }

    public function test_toda_afirmacao_tem_proveniencia(): void
    {
        $resposta = $this->ancorada->montar('qual o status?', $this->ctx());
        // Resposta sem fontes é rejeitada — não se confia no não-ancorado.
        $this->assertNotEmpty($resposta->proveniencia());
    }
}
O primeiro teste é o guardião do isolamento na IA: a recuperação não cruza tenants. O segundo exige proveniência — sem fonte, sem confiança.
Tabela de decisão RAG ingênuo vs RAG governado
AspectoRAG sem governançaRAG governado
Isolamento por tenantÍndice misturado, vazaFiltrado pelo escopo (§4.2)
Permissão do usuárioIgnorada na recuperaçãoAplicada e re-checada
Invenção de fatosModelo "acha"Ancorado nos dados reais
VerificabilidadeResposta sem fonteProveniência em cada fato
AuditoriaDifícilFontes registradas

Estudo de Caso

Caso aplicado

O assistente que cita a fonte e respeita o tenant

Contexto. A plataforma passou a oferecer um assistente que responde perguntas sobre os dados do usuário — pedidos, históricos, status. Sem cuidado, o assistente poderia inventar respostas (alucinação) ou, pior, recuperar dados de outra empresa para responder.

RAG ingênuo
Vaza e/ou inventa
RAG governado
Ancorado e isolado
Ganho
Factual e seguro

Decisão. Ancorar as respostas nos dados reais via RAG, mas com recuperação governada: o filtro de tenant e permissão do usuário entra em cada busca (espelhando o RLS do §4.2), e toda afirmação vem com a citação de sua fonte. O Event Store (§4.4) serve de verdade dos fatos.

Resultado. O assistente responde com fatos reais, não inventados, e cada resposta pode ser verificada pela fonte citada. E o isolamento multiempresa é preservado na camada de IA: um usuário jamais recebe, na resposta do assistente, um dado que não poderia ver pelas vias normais. A IA tornou-se factual e segura — ancorada e governada ao mesmo tempo.

Incidente Real

Severidade · Crítica

A busca semântica que ignorava o tenant

  • Antes Num protótipo de RAG, os documentos de todos os tenants foram indexados juntos, e a busca semântica não aplicava filtro de escopo por empresa.
  • Risco Uma pergunta de um usuário poderia recuperar — e a resposta poderia revelar — trechos de dados pertencentes a outra empresa, exatamente o vazamento multiempresa que o §4.2 evita no banco.
  • Diagnóstico A camada de recuperação da IA contornava o isolamento por tenant; o cuidado existia no banco, mas não no índice semântico.
  • Correção A recuperação passou a aplicar o filtro de tenant e permissão do usuário em cada busca, com re-checagem de cada trecho — o isolamento do §4.2 estendido à IA.
  • Kaizen Vira regra: toda recuperação é governada pelo escopo do usuário; o índice semântico respeita o mesmo isolamento do banco.

Decisão Arquitetural

ADR-0047

RAG governado: recuperação filtrada por permissão, com proveniência

Aceito
Contexto

Ancorar respostas da IA nos dados próprios (RAG) resolve a invenção de fatos, mas a recuperação, se não respeitar permissões, vaza dados entre usuários e tenants — o vazamento multiempresa (§4.2) por uma porta nova, embrulhado numa resposta útil.

Decisão

Toda recuperação é governada pelo escopo do usuário (tenant + permissões), espelhando o RLS do §4.2, com re-checagem de cada trecho (defesa em profundidade). As respostas são ancoradas nos dados reais (Event Store §4.4 como verdade) e carregam proveniência — a fonte verificável de cada fato. Verificado por teste anti-vazamento.

Alternativas consideradas

Índice misturado sem filtro — vaza entre tenants. Modelo respondendo sem ancoragem — inventa fatos. Resposta sem proveniência — não verificável, não auditável.

Consequências
PositivasRespostas factuais e seguras. Isolamento multiempresa preservado na IA. Cada fato verificável pela fonte. Auditoria por proveniência.
NegativasExige propagar o escopo do usuário à recuperação e indexar com metadados de tenant/permissão.
RiscosFiltrar a recuperação pode reduzir o recall; aceitável — segurança e isolamento vêm antes de cobertura.

Antipadrões

Recuperação que ignora o escopo do usuário

Buscar num índice que mistura tenants e permissões. Vaza dados entre empresas. Correção: filtrar a recuperação pelo escopo do usuário (§4.2), com re-checagem.

Resposta sem proveniência

O agente afirma sem citar a fonte. Correção: toda afirmação ancorada carrega sua fonte; sem fonte, a afirmação é suspeita.

Exercício

Teste o isolamento do seu RAG

  1. Se o seu sistema usa (ou usará) RAG, a recuperação respeita as permissões do usuário? Ou busca num índice que mistura os dados de todos?
  2. Faça o teste do vazamento na IA: indexe um dado de um tenant e pergunte, como outro tenant, algo que o recuperaria. A resposta o entrega? Se sim, é o vazamento do §4.2 pela porta da IA.
  3. Verifique a proveniência: as respostas do seu agente citam de onde vêm os fatos? Um usuário consegue verificar uma afirmação?
  4. Pense na ancoragem: quando o agente não encontra dados para responder, ele admite ("não encontrei"), ou inventa uma resposta plausível?
Meta: garantir que a recuperação do seu RAG seja governada — ancorando respostas nos dados reais sem jamais vazar o que o usuário não poderia ver, e com proveniência verificável.
✓ Checklist do tópico
  • A recuperação é filtrada pelo escopo (tenant + permissões) do usuário
  • O isolamento do §4.2 é estendido à camada de recuperação da IA
  • Cada trecho recuperado é re-checado contra a permissão (defesa em profundidade)
  • As respostas são ancoradas nos dados reais, não inventadas
  • Toda afirmação carrega proveniência — a fonte verificável do fato
Meta Dados · Engenharia de Plataformas · Capítulo IX · §9.2Próximo → 9.3 Determinístico Onde Importa
Capítulo IX · IA Corporativa e Agentes
9.3

Determinístico Onde Importa

A maior tentação ao construir com IA é deixar o modelo fazer tudo — inclusive mover dinheiro e mudar estado. Mas um modelo é probabilístico: pode agir diferente para a mesma entrada. Onde o efeito importa, o modelo decide, mas quem executa é código determinístico.

Pré-requisitos · 9.1 · Cap III §3.2 · Cap VI §6.2Conecta com · 9.4 Guardrails · Cap IV Event StoreTempo de leitura · ~13 min

O Problema

A capacidade de um modelo de linguagem de gerar não só texto, mas ações, leva a uma tentação irresistível: deixar o modelo fazer tudo, de ponta a ponta. Que ele decida cobrar o cliente e execute a cobrança; que ele decida dar baixa no estoque e faça a baixa; que ele decida e aja sobre o estado do sistema diretamente, sem intermediários. Parece o ápice da automação inteligente. É, na verdade, uma fragilidade grave inserida no coração da operação.

O problema é a natureza probabilística do modelo. Um modelo de linguagem não é determinístico: para a mesma entrada, ele pode produzir saídas diferentes em execuções diferentes; pode interpretar uma instrução de um jeito hoje e de outro amanhã; pode, por uma flutuação, calcular um valor errado ou repetir uma ação. Para tarefas onde a variação é tolerável — sugerir uma redação, resumir um texto, propor uma ideia —, isso é aceitável, até desejável. Mas para a operação que move dinheiro ou muda estado de forma irreversível, a variação é inaceitável. Cobrar o valor errado, executar a mesma transferência duas vezes, dar baixa no item errado: são erros com consequências reais, e um sistema que os comete de forma não-reproduzível é impossível de confiar, depurar ou auditar. Onde o efeito importa, "às vezes funciona" é o mesmo que "não funciona".

O modelo probabilístico no comando do efeito

Deixar o modelo executar diretamente operações que movem dinheiro ou mudam estado insere o não-determinismo no núcleo: a mesma situação pode produzir efeitos diferentes, irreproduzíveis. Correção: o modelo decide a ação; código determinístico e idempotente a executa.

A Decisão

Onde o efeito importa, separamos decidir de executar. O modelo decide qual ação tomar e com quais parâmetros; a execução é uma ação nomeada (§3.2), determinística e idempotente (§6.2). A inteligência escolhe; o código de confiança realiza.

A plataforma separa rigorosamente a decisão da execução nas operações que importam. O modelo faz o que faz bem: dada uma situação, ele decide — qual ação tomar, com quais parâmetros, em que ordem. Mas a saída do modelo não é o efeito; é uma proposta estruturada: "execute a ação cobrar com valor X para o cliente Y". Essa proposta é então executada por uma ação nomeada (§3.2 Cap III) — código determinístico, testado, que sempre se comporta igual para a mesma entrada. E, porque um modelo pode repetir uma decisão (propor a mesma cobrança duas vezes), a execução é idempotente (§6.2 Cap VI): a mesma ação proposta com a mesma chave acontece uma vez só. Assim, a inteligência do modelo é aproveitada onde ela agrega — na decisão, no julgamento, na escolha — enquanto o efeito sobre dinheiro e estado fica nas mãos de código previsível, reproduzível e auditável. O modelo propõe; o domínio dispõe. A variação do modelo fica isolada na camada de decisão, e nunca contamina a camada de efeito.

Arquitetura lógica O modelo decide, o código executa
O MODELO decide"qual ação? quais parâmetros?" O CÓDIGO executaação nomeada · determinística · idempotente propõe a ação o modelo NÃO muta estado diretamente dinheiro e estado: efeito reproduzível, não probabilísticoa inteligência escolhe; a execução é de confiança (§3.2, §6.2)
Figura 9.3-a. O modelo decide qual ação e com quais parâmetros, mas não muta estado. Uma ação nomeada (§3.2), determinística e idempotente (§6.2), executa. A inteligência escolhe; a execução é de confiança.

A decisão do modelo vira proposta, não efeito

DecisaoDoModelo.phpPHP
// O modelo PROPÕE uma ação estruturada — não executa o efeito.
final class PropostaDeAcao
{
    public function __construct(
        public readonly string $acao,        // 'cobrar' (nome, §3.2)
        public readonly array  $parametros,   // {valor, cliente}
        public readonly string $chaveIdem,    // idempotência (§6.2)
    ) {}
}

final class Agente
{
    public function decidir(Situacao $s): PropostaDeAcao
    {
        // A inteligência do modelo escolhe a ação e os parâmetros...
        return $this->modelo->proporAcao($s);
        // ...mas isso é uma PROPOSTA. Nenhum estado mudou ainda.
    }
}
A saída do modelo é uma proposta estruturada com chave de idempotência — não um efeito. A inteligência escolhe; nada mudou no estado ainda.
ExecucaoDeterministica.phpPHP
// A execução é DETERMINÍSTICA e IDEMPOTENTE — código de confiança.
final class ExecutorDeProposta
{
    public function executar(PropostaDeAcao $p, Contexto $ctx): Resultado
    {
        // Idempotência (§6.2): a mesma proposta (chave) executa UMA vez.
        return $this->idempotente->processar($p->chaveIdem, function () use ($p, $ctx) {
            // A ação nomeada (§3.2) — determinística, testada — realiza o efeito.
            return $this->acoes->resolver($p->acao)
                ->comParametros($p->parametros)
                ->executar($ctx);
        });
    }
}
A proposta é executada por uma ação nomeada determinística, sob idempotência (§6.2). Se o modelo propuser a mesma cobrança duas vezes, ela ocorre uma vez.

A Verificação

DeterministicoTest.phpPHP
final class DeterministicoOndeImportaTest extends TestCase
{
    public function test_proposta_repetida_tem_efeito_unico(): void
    {
        // O modelo, por flutuação, propõe a MESMA cobrança duas vezes.
        $proposta = new PropostaDeAcao('cobrar', ['valor' => 100], chaveIdem: 'c-1');

        $this->executor->executar($proposta, $this->ctx());
        $this->executor->executar($proposta, $this->ctx());  // repetida

        // O efeito (a cobrança) aconteceu UMA vez — não duas.
        $this->assertSame(1, $this->cobrancas->total());
    }

    public function test_execucao_e_deterministica(): void
    {
        // A MESMA proposta produz SEMPRE o mesmo efeito — reproduzível.
        $r1 = $this->executarNova('cobrar', ['valor' => 50]);
        $r2 = $this->executarNova('cobrar', ['valor' => 50]);
        $this->assertEquals($r1->efeito(), $r2->efeito());
    }
}
Os testes garantem o que o modelo sozinho não garantiria: efeito único para proposta repetida, e efeito reproduzível para a mesma entrada.
Tabela de decisão Onde o modelo executa vs onde o código executa
TarefaNaturezaQuem executa
Resumir um textoVariação tolerávelO modelo (direto)
Sugerir uma respostaVariação tolerávelO modelo (direto)
Cobrar um clienteEfeito irreversívelCódigo determinístico (§3.2)
Mudar estadoPrecisa reprodutibilidadeAção nomeada, idempotente
Decidir qual açãoJulgamentoO modelo (decide, não executa)

Estudo de Caso

Caso aplicado

A automação inteligente que não erra o valor

Contexto. A plataforma quis automatizar uma operação que envolvia decisões de julgamento (qual ação tomar diante de cada situação) seguidas de efeitos financeiros (cobranças, ajustes). A tentação era deixar o modelo conduzir tudo, de ponta a ponta.

Modelo executa tudo
Efeito não-reproduzível
Modelo decide, código executa
Efeito determinístico
Ganho
Inteligente e confiável

Decisão. Separar decidir de executar: o modelo decide a ação e os parâmetros, produzindo uma proposta estruturada; a execução do efeito é uma ação nomeada (§3.2), determinística e idempotente (§6.2). A variação do modelo fica contida na decisão.

Resultado. A inteligência do modelo é aproveitada no julgamento — escolher a ação certa para cada caso —, enquanto o efeito sobre o dinheiro é executado por código previsível, que sempre se comporta igual e nunca cobra duas vezes pela mesma proposta. A automação ficou inteligente na decisão e confiável no efeito, sem que a natureza probabilística do modelo contaminasse a operação financeira.

Incidente Real

Severidade · Alta

A ação que o modelo propôs duas vezes

  • Antes Num fluxo onde o modelo conduzia uma operação com efeito, uma flutuação fez o modelo propor a mesma ação de mudança de estado duas vezes em sequência.
  • Sintoma Sem uma camada determinística e idempotente, o efeito foi aplicado duas vezes — uma duplicação indevida que precisou ser detectada e corrigida.
  • Diagnóstico A execução do efeito estava acoplada à decisão do modelo, sem a barreira de idempotência (§6.2) que conteria a repetição probabilística.
  • Correção Separação de decisão e execução: a proposta do modelo passou a ser executada por uma ação nomeada idempotente, tornando o efeito único e reproduzível.
  • Kaizen Vira regra: o modelo decide, o código determinístico e idempotente executa; o não-determinismo nunca alcança o efeito sobre dinheiro ou estado.

Decisão Arquitetural

ADR-0048

Separar decisão (modelo) de execução (código determinístico e idempotente)

Aceito
Contexto

Um modelo de linguagem é probabilístico: a mesma entrada pode gerar saídas diferentes. Deixá-lo executar diretamente operações que movem dinheiro ou mudam estado insere o não-determinismo no núcleo, tornando o efeito irreproduzível, difícil de depurar e de confiar.

Decisão

Onde o efeito importa, decisão e execução são separadas: o modelo decide a ação e os parâmetros, produzindo uma proposta estruturada com chave de idempotência; a execução é uma ação nomeada (§3.2), determinística e testada, sob idempotência (§6.2). A variação do modelo fica contida na decisão e nunca contamina o efeito.

Alternativas consideradas

Modelo executa o efeito — não-determinismo no núcleo, irreproduzível. Execução sem idempotência — uma decisão repetida duplica o efeito. Tudo determinístico (sem modelo) — perde o julgamento que o modelo agrega.

Consequências
PositivasInteligência na decisão, confiança no efeito. Operações reproduzíveis e auditáveis. Repetição do modelo sem duplicar efeito. Não-determinismo isolado.
NegativasExige estruturar a saída do modelo como proposta e executá-la por ações nomeadas idempotentes.
RiscosA separação adiciona uma camada; aceitável — é o preço de manter o efeito previsível onde ele importa.

Antipadrões

O modelo executando o efeito diretamente

Deixar o modelo mover dinheiro ou mudar estado sem intermediário determinístico. Correção: o modelo propõe; uma ação nomeada idempotente executa.

Execução de proposta sem idempotência

Executar a decisão do modelo sem proteção contra repetição. Correção: toda proposta carrega chave de idempotência (§6.2); o efeito acontece uma vez.

Exercício

Separe decisão de execução no seu sistema

  1. Se o seu sistema usa IA para agir, identifique as ações que têm efeito real (movem dinheiro, mudam estado irreversível). O modelo as executa diretamente, ou propõe e um código as executa?
  2. Faça o teste da repetição: se o modelo, por flutuação, decidisse a mesma ação de efeito duas vezes, o que aconteceria? O efeito duplicaria?
  3. Faça o teste da reprodutibilidade: a mesma situação produz sempre o mesmo efeito, ou pode variar conforme o modelo?
  4. Para uma operação de efeito, esboce a separação: o que o modelo decidiria (ação + parâmetros) e qual ação nomeada idempotente executaria?
Meta: separar, nas operações que importam, a decisão inteligente do modelo da execução determinística do código — para que a variação probabilística nunca contamine o efeito sobre dinheiro e estado.
✓ Checklist do tópico
  • Decisão e execução são separadas nas operações com efeito real
  • O modelo decide a ação e os parâmetros, produzindo uma proposta
  • A execução é uma ação nomeada (§3.2), determinística e testada
  • Toda proposta de efeito é idempotente (§6.2) — repetição não duplica
  • O não-determinismo do modelo fica contido na decisão, nunca no efeito
Meta Dados · Engenharia de Plataformas · Capítulo IX · §9.3Próximo → 9.4 Avaliação e Guardrails
Capítulo IX · IA Corporativa e Agentes
9.4

Avaliação e Guardrails

Como testar um sistema que pode responder diferente para a mesma entrada? Os testes binários do código não bastam para a IA. É preciso medir a qualidade num conjunto de casos (avaliação) antes de subir, e vigiar cada saída (guardrails) em produção — sempre falhando seguro na dúvida.

Pré-requisitos · 9.1–9.3 · Cap VII §7.1 · Cap VIII VerificaçãoConecta com · 9.5 Observabilidade · Cap VIII SLOsTempo de leitura · ~14 min

O Problema

Todo este livro construiu sobre uma ideia: uma decisão não comprovada é só uma esperança, e por isso cada tópico tem sua seção de Verificação, cada comportamento seu teste. Mas os testes do código são binários e determinísticos: dada a entrada X, a saída deve ser exatamente Y, sempre. Esse modelo quebra diante da IA. Um modelo de linguagem pode produzir saídas diferentes — e ainda assim corretas, ou sutilmente erradas — para a mesma entrada. "A saída é exatamente Y?" deixa de ser a pergunta certa. Como, então, se verifica um sistema cuja própria natureza é não ser sempre igual?

Ignorar essa pergunta é o caminho para um sistema de IA em que ninguém confia. Sem uma forma de medir a qualidade do agente, sobe-se uma versão sem saber se ela é melhor ou pior que a anterior; ajusta-se um prompt no escuro; descobre-se uma regressão só quando um cliente reclama. E sem nenhuma vigilância em produção, a primeira saída perigosa — uma resposta mal-formada que quebra a integração, um conteúdo que não deveria existir, uma ação proposta absurda — chega ao usuário ou ao efeito sem nada a barrá-la. Um sistema não-determinístico sem avaliação e sem guardrails é um sistema operando no escuro, à espera de que a variação inevitável produza, mais cedo ou mais tarde, um resultado inaceitável.

O não-determinístico sem rede

Os testes binários do código não capturam a qualidade de um sistema que varia. Sem avaliação (medir antes) e guardrails (vigiar durante), sobe-se IA no escuro e a primeira saída perigosa chega sem barreira. Correção: medir a qualidade num conjunto de casos e validar cada saída em produção, falhando seguro.

A Decisão

A IA é verificada em duas frentes: avaliação offline — um conjunto de casos com gabarito que mede a qualidade antes de subir — e guardrails online, que validam cada saída em produção. Na incerteza, o agente falha seguro: recusa ou escala.

A verificação da IA se adapta à sua natureza, em duas camadas. A primeira é a avaliação (eval), a versão para IA da seção de Verificação deste livro: um conjunto curado de casos representativos, cada um com um gabarito ou critério de qualidade, contra o qual o agente é medido antes de qualquer mudança ir para produção. A métrica não é "passou/falhou", mas uma taxa de qualidade — quantos casos o agente acerta, quão bem. Trocar o modelo, ajustar um prompt, mudar a recuperação (§9.2): nada sobe sem rodar contra o conjunto de avaliação e provar que não regrediu. É o CI da IA. A segunda camada são os guardrails, que operam em produção, ao vivo, sobre cada saída: validam que a resposta tem o formato esperado, que não contém o que não deveria, que a ação proposta é coerente e está dentro dos limites. Uma saída que não passa pelo guardrail é barrada antes de causar efeito. E sobre tudo isso paira um princípio herdado do §7.1 (Cap VII): a IA falha seguro. Diante de incerteza — baixa confiança, caso fora do esperado, validação que não passa —, o agente não arrisca: ele recusa a ação ou escala para um humano. Num efeito que importa, não agir é sempre melhor que agir errado. A avaliação mede a qualidade antes; os guardrails a vigiam durante; e a falha segura garante que, no pior caso, o sistema erra para o lado da prudência.

Arquitetura lógica Avaliação antes, guardrails durante, falha segura
AVALIAÇÃO (offline)conjunto de casos com gabaritomede qualidade antes de subir GUARDRAILS (online)validam cada saída em produçãona dúvida: recusar / escalar FALHA SEGURA (§7.1)incerto → escala para o humano, não arrisca testar o não-determinístico: medir antes, vigiar durante
Figura 9.4-a. A avaliação offline mede a qualidade contra um conjunto de casos antes de subir; os guardrails validam cada saída em produção. Na incerteza, o agente falha seguro — recusa ou escala (§7.1).

Avaliação: medir a qualidade contra casos

avaliacao.phpPHP
// AVALIAÇÃO (offline): o "CI da IA". Mede qualidade antes de subir.
final class Avaliacao
{
    public function rodar(VersaoDoAgente $versao): Resultado
    {
        $acertos = 0;
        foreach ($this->conjuntoDeCasos() as $caso) {
            $saida = $versao->responder($caso->entrada);
            // Critério de qualidade por caso (gabarito, regra, ou juiz).
            if ($caso->satisfeito($saida)) { $acertos++; }
        }
        // Métrica é uma TAXA, não passou/falhou. Não pode regredir.
        return new Resultado(taxa: $acertos / $this->total());
    }
}
A avaliação mede uma taxa de qualidade contra casos curados. Nenhuma mudança de modelo ou prompt sobe sem provar, aqui, que não regrediu.
guardrail.phpPHP
// GUARDRAIL (online): valida CADA saída em produção. Falha seguro.
final class Guardrail
{
    public function validar(Saida $saida, Contexto $ctx): Decisao
    {
        // Formato esperado? Conteúdo permitido? Ação dentro dos limites?
        if (! $saida->temFormatoValido()
            || $saida->contemConteudoProibido()
            || $saida->confianca() < $this->limiar) {
            // FALHA SEGURA (§7.1): não arrisca — escala para humano.
            return Decisao::escalarParaHumano($saida, $ctx);
        }
        return Decisao::prosseguir($saida);
    }
}
O guardrail valida cada saída ao vivo. O que não passa — formato, conteúdo, baixa confiança — não vira efeito: escala para o humano, falhando seguro.

A Verificação

GuardrailTest.phpPHP
final class GuardrailTest extends TestCase
{
    public function test_saida_invalida_e_barrada_e_escalada(): void
    {
        // Uma saída mal-formada NÃO pode prosseguir para o efeito.
        $decisao = $this->guardrail->validar($this->saidaMalFormada(), $this->ctx());
        $this->assertTrue($decisao->foiEscalada());
    }

    public function test_baixa_confianca_falha_seguro(): void
    {
        // Confiança abaixo do limiar → escala, não arrisca (§7.1).
        $decisao = $this->guardrail->validar($this->saidaComConfianca(0.3), $this->ctx());
        $this->assertFalse($decisao->prosseguiu());
    }

    public function test_avaliacao_detecta_regressao(): void
    {
        // Uma versão pior é reprovada antes de subir.
        $this->assertLessThan($this->baseline, $this->avaliacao->rodar($this->versaoPior())->taxa());
    }
}
Os testes provam as duas redes: o guardrail barra e escala o inválido e o incerto; a avaliação reprova uma versão que regrediu antes que ela suba.
Tabela de decisão Avaliação (offline) e guardrails (online)
AspectoAvaliaçãoGuardrails
QuandoAntes de subirEm produção, cada saída
O que medeTaxa de qualidade vs casosValidade de cada resposta
Análogo no códigoO CI / a suíte de testesA validação de runtime
Na incertezaReprova a versãoFalha seguro: recusa/escala (§7.1)
Sem elaSobe no escuroSaída perigosa passa

Estudo de Caso

Caso aplicado

A IA que sobe medida e opera vigiada

Contexto. A plataforma precisava evoluir seu agente — trocar modelos, ajustar prompts, refinar a recuperação — sem regredir, e sem que uma saída perigosa chegasse ao usuário ou a um efeito. Os testes binários do código não bastavam para um sistema que varia.

Sem rede
Sobe no escuro, saída perigosa passa
Avaliação + guardrails
Medida antes, vigiada durante
Ganho
Evoluível e segura

Decisão. Montar um conjunto de avaliação com casos representativos e gabaritos, rodado a cada mudança como um CI da IA; e instalar guardrails que validam cada saída em produção, com falha segura (§7.1) — recusar ou escalar na incerteza.

Resultado. Nenhuma mudança no agente sobe sem provar, na avaliação, que melhorou ou ao menos não regrediu. E em produção, toda saída passa pelos guardrails: o que não tem formato válido, ou tem baixa confiança, ou propõe algo fora dos limites, é barrado e escalado para um humano em vez de virar efeito. A IA passou a ser evoluível com segurança e a operar com uma rede — medida antes, vigiada durante.

Incidente Real

Severidade · Alta

A saída mal-formada que quebrou a integração

  • Antes Sem guardrails, a saída do agente era consumida diretamente por uma integração. Numa flutuação, o modelo produziu uma resposta fora do formato esperado.
  • Sintoma A integração que consumia a saída quebrou ao receber algo que não esperava — uma falha que se propagou a partir de uma única resposta mal-formada.
  • Diagnóstico Não havia validação da saída em produção; a primeira resposta fora do padrão chegou ao consumidor sem nada a barrá-la.
  • Correção Instalação de guardrails que validam o formato de cada saída antes do consumo, escalando o que não passa em vez de propagá-lo.
  • Kaizen Vira regra: toda saída de IA passa por guardrails antes de virar efeito; o inválido falha seguro, e mudanças sobem só após avaliação.

Decisão Arquitetural

ADR-0049

Avaliação offline e guardrails online, com falha segura

Aceito
Contexto

Os testes binários do código não verificam um sistema não-determinístico. Sem avaliação, sobe-se IA sem saber se melhorou ou regrediu; sem guardrails, a primeira saída perigosa chega ao usuário ou ao efeito sem barreira.

Decisão

A IA é verificada em duas frentes: avaliação offline (conjunto de casos com gabarito, medindo uma taxa de qualidade que não pode regredir — o CI da IA) e guardrails online (validação de cada saída em produção: formato, conteúdo, limites). Na incerteza, o agente falha seguro (§7.1): recusa ou escala para um humano.

Alternativas consideradas

Só testes binários — não capturam a qualidade do que varia. Sem guardrails — saída perigosa vira efeito. Agir na incerteza — arrisca onde não deveria; melhor recusar/escalar.

Consequências
PositivasIA evoluível sem regressão. Saída perigosa barrada antes do efeito. Erro para o lado da prudência. Qualidade medida e vigiada.
NegativasExige curar e manter o conjunto de avaliação e implementar os guardrails de saída.
RiscosA avaliação não cobre todo caso real; mitigada por enriquecê-la com casos de produção e pelos guardrails ao vivo.

Antipadrões

Subir IA sem avaliação

Trocar modelo ou prompt sem medir contra um conjunto de casos. Regressões passam despercebidas. Correção: avaliação como CI da IA; nada sobe sem provar que não regrediu.

Agir na incerteza

O agente tomar uma ação de efeito com baixa confiança. Correção: falha segura (§7.1) — na dúvida, recusar ou escalar para um humano.

Exercício

Construa a rede de segurança da sua IA

  1. Se o seu sistema usa IA, como você sabe que uma mudança (de modelo, de prompt) melhorou e não piorou? Há um conjunto de casos contra o qual medir, ou se sobe no escuro?
  2. Monte um esboço de conjunto de avaliação: que casos representativos, com que critério de qualidade, você usaria para medir o agente antes de subir?
  3. Faça o teste da saída perigosa: o que acontece hoje se o seu agente produzir uma resposta mal-formada ou inadequada? Algo a barra antes do efeito?
  4. Defina a falha segura: diante de baixa confiança ou de um caso fora do esperado, o seu agente recusa/escala, ou arrisca a ação mesmo assim?
Meta: construir as duas redes que verificam um sistema não-determinístico — avaliação para medir antes, guardrails para vigiar durante — com a falha segura como princípio na incerteza.
✓ Checklist do tópico
  • Há um conjunto de avaliação com casos e critérios de qualidade
  • Nenhuma mudança no agente sobe sem rodar a avaliação e provar que não regrediu
  • Guardrails validam cada saída em produção (formato, conteúdo, limites)
  • Saídas inválidas ou de baixa confiança são barradas antes do efeito
  • Na incerteza, o agente falha seguro: recusa ou escala para um humano (§7.1)
Meta Dados · Engenharia de Plataformas · Capítulo IX · §9.4Próximo → 9.5 Observabilidade e Custo
Capítulo IX · IA Corporativa e Agentes
9.5

Observabilidade e Custo

Um agente que decide e age sem deixar rastro é uma caixa-preta dentro da plataforma — e este livro recusa caixas-pretas. Cada execução precisa ser rastreável, cada decisão auditável, e o custo, que na IA é real e variável, precisa ser uma métrica de primeira classe.

Pré-requisitos · 9.1–9.4 · Cap VIII Três Pilares · §4.4 Event StoreConecta com · Cap VIII SLOs · O ManifestoTempo de leitura · ~13 min

O Problema

O Manifesto deste livro recusa o conforto ilusório das caixas-pretas, e o Capítulo VIII ensinou a tornar o invisível visível. Um agente de IA ameaça reintroduzir, no coração da plataforma, exatamente a caixa-preta que tanto trabalho custou a eliminar. Ele recebe uma situação, "pensa", e age — e se nada disso deixa rastro, ninguém sabe por que o agente decidiu o que decidiu, o que ele recuperou para fundamentar a resposta, ou como chegou àquela ação. Quando algo dá errado — uma decisão estranha, uma ação indevida —, não há por onde investigar. O agente vira um oráculo opaco dentro de um sistema que se orgulha de sua clareza.

E há um problema que o código tradicional não tem: custo por uso. Uma função PHP, uma vez escrita, custa praticamente o mesmo a cada execução. Um agente de IA, não: cada execução consome tokens, faz chamadas que se pagam, e esse custo varia — com o tamanho da entrada, com o número de passos que o agente dá, com a recuperação que ele faz. Sem observar esse custo, ele se torna uma surpresa: uma operação que parecia barata se multiplica por milhões de execuções e estoura o orçamento; um agente que entra em laço consome recursos sem limite; a fatura chega no fim do mês como um susto. Um agente sem observabilidade é uma caixa-preta que não se pode depurar; um agente sem controle de custo é uma torneira aberta que não se pode prever. Ambos violam a disciplina de operar com clareza.

A caixa-preta cara

Um agente que decide e age sem rastro não pode ser depurado nem auditado, e seu custo por uso, se não observado, vira surpresa na fatura. Correção: rastrear cada execução, auditar cada decisão, e tratar o custo como métrica de primeira classe.

A Decisão

Cada execução de agente é rastreável de ponta a ponta (os três pilares do Cap VIII estendidos à IA), cada decisão é auditável via Event Store (§4.4), o custo é uma métrica de primeira classe com orçamento, e o prompt é configuração versionada, não código.

A plataforma trata a IA com a mesma disciplina de observabilidade de tudo o mais — adaptada à sua natureza. Rastreabilidade: cada execução de agente é um traço completo, estendendo os três pilares do Capítulo VIII à IA. O traço registra o prompt usado, o que foi recuperado (§9.2), a decisão do modelo, a ação proposta e o efeito executado — de modo que qualquer execução pode ser reconstruída e entendida. Auditoria: as decisões e ações do agente entram no Event Store (§4.4) como fatos, com origem identificada como IA — a mesma fonte de verdade que audita todo o resto audita também o agente, e "por que a IA fez isso?" tem resposta. Custo como métrica de primeira classe: os tokens e o custo monetário de cada execução são medidos como qualquer métrica do Capítulo VIII, com orçamentos e alertas — um agente que consome demais dispara alarme, como qualquer recurso que sai do esperado, e o custo deixa de ser surpresa para ser uma grandeza observada e governada. E, fechando o círculo com a tese central do livro, o prompt e a versão do modelo são configuração, não código: vivem como dado versionado (§Cap III — o que varia é dado), podem ser ajustados sem reimplantar a aplicação, e cada versão é rastreável até as execuções que a usaram. O agente deixa de ser um oráculo opaco e caro, e passa a ser um componente observável, auditável e governado como qualquer outro — clareza diante do caos, inclusive para a IA.

Arquitetura lógica A execução de agente rastreável e governada
UMA EXECUÇÃO DE AGENTE é rastreável de ponta a ponta prompt + versão recuperação decisão ação + efeito CUSTO é métrica de 1ª classetokens · $ por execução · orçamento PROMPT é config, não códigoversionado, trocável sem deploy os três pilares (Cap VIII) estendidos à IA — e o custo entre eles
Figura 9.5-a. Cada execução é um traço completo — prompt, recuperação, decisão, ação. O custo é métrica de primeira classe; o prompt é configuração versionada. Os três pilares do Cap VIII, estendidos à IA.

Rastrear e medir cada execução

TracoDeExecucao.phpPHP
// Cada execução é um TRAÇO completo — os três pilares (Cap VIII) na IA.
final class TracoDeAgente
{
    public function registrar(ExecucaoDeAgente $e): void
    {
        $this->traco->emitir([
            'prompt_versao' => $e->promptVersao(),   // config, não código
            'modelo'       => $e->modelo(),
            'recuperacao'  => $e->fontesRecuperadas(),  // §9.2
            'decisao'      => $e->acaoProposta(),       // §9.3
            'tokens'       => $e->tokensConsumidos(), // CUSTO de 1ª classe
            'custo'        => $e->custoMonetario(),
        ]);

        // A DECISÃO entra no Event Store (§4.4): auditável, origem IA.
        $this->eventStore->registrar(DecisaoDeAgente::de($e));
    }
}
O traço captura prompt, modelo, recuperação, decisão e custo de cada execução; a decisão vai ao Event Store. Nada do que o agente faz fica opaco.
custo_orcado.phpPHP
// CUSTO como métrica de 1ª classe: orçamento e alerta (Cap VIII).
final class GovernancaDeCusto
{
    public function registrarEVigiar(Custo $custo, Contexto $ctx): void
    {
        $this->metricas->incrementar('agente.custo', $custo->valor(), $ctx->tenantId());

        // Como qualquer recurso: estourou o orçamento → alerta acionável.
        if ($this->orcamento->excedido($ctx->tenantId())) {
            $this->alertas->acionavel('orcamento_de_ia_excedido', $ctx);
        }
    }
}
O custo é medido por tenant, com orçamento e alerta acionável (Cap VIII §8.2). Um agente que consome demais dispara alarme — o custo deixa de surpreender.

A Verificação

ObservabilidadeIaTest.phpPHP
final class ObservabilidadeDeAgenteTest extends TestCase
{
    public function test_execucao_deixa_traco_completo(): void
    {
        $this->agente->responder($this->situacao(), $this->ctx());

        // O traço registra prompt, recuperação, decisão e custo.
        $traco = $this->ultimoTraco();
        $this->assertNotNull($traco->promptVersao());
        $this->assertGreaterThan(0, $traco->tokens());
    }

    public function test_decisao_e_auditavel_no_event_store(): void
    {
        $this->agente->responder($this->situacao(), $this->ctx());
        // "Por que a IA fez isso?" tem resposta no Event Store (§4.4).
        $this->assertTrue($this->eventStore->temDecisaoDeAgente());
    }
}
Os testes garantem que nenhuma execução fica opaca: há traço completo com custo, e a decisão é auditável no Event Store. A caixa-preta não se forma.
Tabela de decisão Agente opaco vs agente observável
AspectoAgente caixa-pretaAgente observável
Por que decidiu X?Sem respostaTraço + Event Store (§4.4)
O que recuperou?InvisívelRegistrado no traço (§9.2)
Custo por execuçãoSurpresa na faturaMétrica com orçamento
PromptCravado no códigoConfig versionada
Agente em laçoConsome sem limiteAlerta de orçamento

Estudo de Caso

Caso aplicado

O agente que não é uma caixa-preta

Contexto. Ao colocar um agente para decidir e agir, a plataforma corria o risco de reintroduzir uma caixa-preta — um componente que age sem que se saiba por quê — e de incorrer em custos de IA imprevisíveis, que poderiam estourar o orçamento sem aviso.

Caixa-preta cara
Opaco e imprevisível
Observável e governado
Rastreável, auditável, orçado
Ganho
IA com clareza diante do caos

Decisão. Estender a observabilidade do Capítulo VIII à IA: cada execução vira um traço completo (prompt, recuperação, decisão, ação), as decisões entram no Event Store (§4.4) como fatos auditáveis, o custo (tokens, valor) é medido como métrica de primeira classe com orçamento e alerta, e o prompt vira configuração versionada, não código.

Resultado. Quando uma decisão do agente precisa ser entendida, o traço e o Event Store contam a história inteira — por que decidiu, o que recuperou, o que fez. Quando o custo cresce, um alerta avisa antes da fatura. E ajustar um prompt não exige deploy. O agente deixou de ser um oráculo opaco e caro para ser um componente observável, auditável e governado como qualquer outro — fiel ao Manifesto, que recusa caixas-pretas.

Incidente Real

Severidade · Alta

O agente em laço que consumiu sem limite

  • Antes Sem o custo como métrica observada, um agente entrou num laço inesperado, repetindo passos e consumindo tokens muito além do previsto.
  • Sintoma O consumo cresceu silenciosamente; o estouro só foi percebido tarde, quando o custo acumulado já era expressivo — uma torneira aberta sem ninguém vendo.
  • Diagnóstico O custo da IA não era tratado como métrica de primeira classe; não havia orçamento nem alerta para um consumo anômalo.
  • Correção O custo (tokens, valor) passou a ser medido por execução e por tenant, com orçamento e alerta acionável — um agente que consome demais dispara alarme.
  • Kaizen Vira regra: o custo da IA é métrica de primeira classe, com orçamento e alerta; nenhum consumo anômalo passa despercebido.

Decisão Arquitetural

ADR-0050

Observabilidade, auditoria e custo de primeira classe para agentes

Aceito
Contexto

Um agente que decide e age sem rastro reintroduz a caixa-preta que o Manifesto recusa e o Cap VIII eliminou. E o custo por uso da IA, variável e não observado, vira surpresa na fatura — uma torneira aberta sem medição.

Decisão

A observabilidade do Cap VIII é estendida à IA: cada execução é um traço completo (prompt, recuperação §9.2, decisão §9.3, ação); as decisões entram no Event Store (§4.4) como fatos auditáveis com origem IA; o custo (tokens, valor) é métrica de primeira classe com orçamento e alerta; e o prompt/versão do modelo é configuração versionada (§Cap III), não código.

Alternativas consideradas

Agente sem rastro — caixa-preta indepurável. Custo não observado — surpresa na fatura, agente em laço sem alarme. Prompt no código — ajuste exige deploy, perde rastreabilidade de versão.

Consequências
PositivasToda decisão da IA é explicável e auditável. Custo previsível e governado. Prompt ajustável sem deploy. Caixa-preta evitada — clareza inclusive para a IA.
NegativasExige instrumentar as execuções, integrar o custo às métricas e versionar prompts como dado.
RiscosTracing e medição adicionam overhead; mitigado por amostragem consciente, preservando rastreabilidade do que importa.

Antipadrões

Agente sem traço nem auditoria

Um agente que age sem registrar por quê, o quê recuperou, o que fez. Caixa-preta. Correção: traço completo por execução + decisões no Event Store (§4.4).

Custo de IA não observado

Não medir tokens nem custo por execução. Estouro de orçamento vira surpresa. Correção: custo como métrica de primeira classe, com orçamento e alerta.

Exercício

Ilumine a sua IA

  1. Se o seu sistema usa um agente, pegue uma execução recente. Você consegue reconstruir por que ele decidiu o que decidiu, o que recuperou e o que fez? Ou é uma caixa-preta?
  2. Faça o teste do custo: você sabe quanto cada execução do seu agente custa (tokens, valor)? Há um orçamento e um alerta para consumo anômalo, ou a fatura é uma surpresa?
  3. Verifique a auditoria: as decisões do seu agente entram numa fonte de verdade (como o Event Store), com a origem identificada como IA?
  4. Olhe o prompt: ele está cravado no código (mudar exige deploy) ou é configuração versionada que se pode ajustar e rastrear?
Meta: estender a clareza do seu sistema à IA — tornando cada execução de agente rastreável e auditável, e o custo, uma grandeza observada e governada, fiel à recusa das caixas-pretas.
✓ Checklist do tópico
  • Cada execução de agente é um traço completo (prompt, recuperação, decisão, ação)
  • As decisões do agente entram no Event Store (§4.4), auditáveis, com origem IA
  • O custo (tokens, valor) é métrica de primeira classe, com orçamento e alerta
  • Um consumo anômalo (ex.: agente em laço) dispara alarme
  • O prompt e a versão do modelo são configuração versionada, não código cravado
Meta Dados · Engenharia de Plataformas · Capítulo IX · §9.5Fim do Capítulo IX · voltar ao início
Meta Dados · Engenharia de Plataformas
Capítulo X

Cloud Native
Platform
Engineering

Operar uma frota de servidores virtuais como uma plataforma, não como um amontoado de máquinas: infraestrutura como código, contêineres reprodutíveis, entrega contínua segura e a malha de serviços que os mantém conversando.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo X
Capítulo X · Cloud Native Platform Engineering
10.1

Infraestrutura como Código

Um servidor configurado à mão é um floco de neve: único, irrepetível e, quando cai, um mistério. Operar uma frota assim é apostar que nada quebrará — e quando algo quebra, a reconstrução é arqueologia. A infraestrutura precisa ser descrita em código, não improvisada no terminal.

Pré-requisitos · Cap VII §7.5 Rebuild · Cap IV ReprodutibilidadeConecta com · 10.2 Contêineres · 10.4 EntregaTempo de leitura · ~14 min

O Problema

Operar uma frota de servidores virtuais — dezenas de máquinas servindo aplicações, bancos, monitoramento — começa de forma inocente: cria-se um servidor, conecta-se a ele pelo terminal, instala-se o que precisa, ajusta-se uma configuração aqui, um pacote ali, até funcionar. Funciona. Cria-se o próximo da mesma maneira. E, sem que se perceba, cada servidor da frota tornou-se um floco de neve: único, configurado por uma sequência de comandos manuais que ninguém registrou, ligeiramente diferente de todos os outros por causa de ajustes feitos no calor do momento e esquecidos.

O custo dessa abordagem não aparece enquanto tudo funciona — aparece no pior momento possível. Um servidor cai, ou é comprometido (como o caso do minerador no §7.5 Cap VII), e precisa ser reconstruído. Mas como? A configuração daquele servidor existia apenas no próprio servidor, fruto de meses de ajustes manuais não documentados. Recriá-lo idêntico vira arqueologia: tentar lembrar o que foi instalado, em que versão, com qual configuração, descobrindo as peças que faltam só quando algo não funciona. Pior ainda é a deriva silenciosa: servidores que deveriam ser idênticos divergem ao longo do tempo, e um bug aparece em um e não em outro, sem explicação, porque a diferença mora em algum ajuste manual esquecido. Uma frota de flocos de neve é uma frota que não se pode reconstruir com confiança, não se pode auditar e não se pode escalar — cada nova máquina é um novo trabalho artesanal, e cada falha, uma escavação.

O servidor que ninguém sabe reconstruir

Um servidor configurado à mão guarda sua configuração só em si mesmo. Quando cai, recriá-lo idêntico é arqueologia — e servidores "iguais" derivam silenciosamente. Correção: a infraestrutura é descrita em código versionado; reconstruir é reexecutar a definição.

A Decisão

A infraestrutura é descrita em código: cada servidor, rede e recurso é uma definição versionada, revisável e reproduzível. Reconstruir não é redescobrir uma configuração perdida — é reexecutar a definição que sempre produz o mesmo resultado.

A plataforma trata a infraestrutura como código (IaC): em vez de configurar servidores à mão, descreve-se o estado desejado de cada recurso — quais servidores existem, com que software, em que configuração, em qual rede — em arquivos de definição versionados. Uma ferramenta lê essas definições e realiza a infraestrutura descrita: cria os servidores, instala o software, aplica a configuração. A diferença é transformadora. A configuração deixa de morar opacamente dentro de cada máquina e passa a viver, explícita e legível, no controle de versão — onde pode ser revisada (uma mudança na infra passa pela mesma revisão de código que uma mudança na aplicação), reproduzida (reexecutar a definição produz sempre o mesmo servidor, idêntico) e auditada (o histórico mostra quem mudou o quê, quando e por quê). Reconstruir um servidor comprometido, como o §7.5 exigiu, deixa de ser arqueologia e vira uma operação trivial: aplica-se a definição a uma máquina nova e limpa, e obtém-se um servidor idêntico ao original — sem o comprometimento. A deriva silenciosa desaparece, porque a fonte de verdade não é mais o estado mutável de cada máquina, mas a definição declarativa que as governa. É a mesma lição da reprodutibilidade que percorre o livro, aplicada à infraestrutura: o que importa é descrito, não improvisado.

Diagrama conceitual O servidor descrito em código, não configurado à mão
SERVIDOR-FLOCO DE NEVEconfigurado à mão, únicocaiu? ninguém sabe reconstruir SERVIDOR DESCRITOdefinição versionada em códigoreconstruível, idêntico, sempre A INFRAESTRUTURA É CÓDIGOrevisável · reproduzível · auditável ✕ mudança manual não rastreada
Figura 10.1-a. O servidor-floco-de-neve guarda sua configuração só em si; quando cai, é um mistério. A infraestrutura como código vive versionada — revisável, reproduzível, auditável. Reconstruir é reexecutar.

A definição declarativa do servidor

servidor.tfHCL
# A infraestrutura é DESCRITA, não configurada à mão. Versionada.
resource "servidor" "aplicacao" {
  imagem_base = "base-limpa-v3"   # ponto de partida conhecido
  cpu         = 2
  memoria_gb  = 4
  rede        = var.rede_privada    # referência, não IP cravado

  # O que instalar e configurar — explícito, reproduzível.
  provisionamento = [
    "instalar runtime-app v=8.x",
    "aplicar hardening-base",        # §7.5: seguro por padrão
    "configurar coletor-de-metricas", # Cap VIII desde o início
  ]
}
# Reexecutar isto produz SEMPRE o mesmo servidor — idêntico.
O servidor é uma definição legível e versionada: imagem-base, recursos, rede e provisionamento. Aplicá-la produz sempre a mesma máquina.
rebuild.shBash
# Reconstruir um servidor (ex.: comprometido, §7.5) — trivial com IaC.

# 1) Descartar a máquina comprometida (não é mais confiável).
infra destruir servidor.aplicacao

# 2) Reexecutar a MESMA definição → servidor idêntico, limpo.
infra aplicar servidor.aplicacao

# Não há arqueologia: a definição É a configuração. Minutos, não dias.
Reconstruir é destruir a máquina e reaplicar a definição. O que no §7.5 foi um rebuild manual vira uma operação de duas linhas, reproduzível.

A Verificação

infra_check.shBash
# A infra como código é VERIFICÁVEL antes de aplicar.

# 1) A definição é válida e o plano de mudança é o esperado?
infra planejar servidor.aplicacao || falhar "Definição inválida"

# 2) Não há DERIVA: o servidor real bate com a definição?
test "$(infra detectar-deriva)" = "nenhuma" \
  || falhar "DERIVA: máquina divergiu da definição (ajuste manual?)"

# 3) Dois servidores "iguais" são REALMENTE idênticos?
test "$(comparar servidor.app-1 servidor.app-2)" = "identicos" \
  || falhar "Servidores divergiram"
A IaC permite verificar a infra: validar o plano, detectar deriva (ajustes manuais que divergiram da definição) e confirmar que máquinas iguais são idênticas.
Tabela de decisão Servidor-floco-de-neve vs infraestrutura como código
AspectoConfigurado à mãoDescrito em código
Onde mora a configSó na máquina, opacaVersionada, legível
Reconstruir após quedaArqueologia, diasReexecutar, minutos
Servidores "iguais"Derivam silenciosamenteIdênticos, verificável
Mudança na infraComando manual não rastreadoRevisada e auditada
Escalar a frotaTrabalho artesanal por máquinaAplicar a definição

Estudo de Caso

Caso aplicado

A frota que se reconstrói sozinha

Contexto. A plataforma opera uma frota de dezenas de servidores virtuais — aplicações, bancos, monitoramento — distribuídos por múltiplos ambientes de clientes. Configurar e manter cada um à mão tornava a frota um conjunto de flocos de neve: difícil de reconstruir, propenso a deriva, custoso de escalar.

Flocos de neve
Cada máquina única, frágil
Infra como código
Definições reproduzíveis
Ganho
Frota reconstruível e auditável

Decisão. Descrever toda a infraestrutura como código versionado: cada servidor é uma definição declarativa (imagem-base, recursos, provisionamento, rede), e uma ferramenta a realiza. Reconstruir, escalar ou auditar passa a ser operar sobre essas definições.

Resultado. Quando um servidor precisa ser reconstruído — por falha, por comprometimento (§7.5), ou para escalar —, basta reaplicar a definição: obtém-se uma máquina idêntica, limpa, em minutos, sem arqueologia. A deriva entre servidores "iguais" desaparece, porque a verdade está na definição, não no estado mutável de cada máquina. A frota deixou de ser um conjunto de artesanatos frágeis e tornou-se uma infraestrutura reproduzível, revisável e auditável.

Incidente Real

Severidade · Alta

A deriva que fez o bug aparecer só em um servidor

  • Antes Numa frota configurada manualmente, dois servidores que deveriam ser idênticos receberam, ao longo do tempo, ajustes manuais diferentes, não registrados.
  • Sintoma Um comportamento (um bug) aparecia em um servidor e não no outro, sem explicação aparente, consumindo horas de investigação — a diferença morava num ajuste manual esquecido.
  • Diagnóstico A deriva silenciosa entre máquinas "iguais", causada por configuração manual não versionada, tornava o ambiente irreprodutível e o bug, um mistério.
  • Correção A configuração dos servidores foi descrita em código versionado; a deriva foi eliminada ao reaplicar a definição única a ambos, tornando-os idênticos.
  • Kaizen Vira regra: nenhuma configuração de servidor é feita à mão; toda mudança passa pela definição versionada, e a deriva é detectada automaticamente.

Decisão Arquitetural

ADR-0051

A infraestrutura é descrita em código versionado

Aceito
Contexto

Servidores configurados à mão são flocos de neve: a configuração mora opaca em cada máquina, reconstruir após uma queda é arqueologia (como o rebuild do §7.5), e máquinas "iguais" derivam silenciosamente, gerando bugs irreprodutíveis. Uma frota assim não se escala nem se audita.

Decisão

Toda a infraestrutura é descrita em código declarativo e versionado: cada servidor, rede e recurso é uma definição que uma ferramenta realiza. Mudanças passam por revisão; reconstruir é reexecutar a definição (idêntico, em minutos); a deriva é detectada automaticamente. A verdade está na definição, não no estado mutável da máquina.

Alternativas consideradas

Configuração manual — opaca, irreprodutível, deriva. Scripts imperativos não-idempotentes — frágeis a reexecução. Imagens douradas sem definição — melhor que manual, mas a origem da imagem precisa ser código.

Consequências
PositivasFrota reconstruível e idêntica. Mudanças revisadas e auditadas. Deriva eliminada. Rebuild (§7.5) trivial. Escala por aplicação de definição.
NegativasExige adotar uma ferramenta de IaC e a disciplina de nunca configurar à mão.
RiscosA definição pode divergir da realidade se alguém mexer à mão; mitigado pela detecção de deriva e por tornar a definição a única via de mudança.

Antipadrões

Configurar servidores à mão

Ajustar máquinas pelo terminal sem registrar. Gera flocos de neve irreprodutíveis. Correção: toda configuração é código versionado; nada manual.

Mudar a máquina sem atualizar a definição

Um ajuste manual de emergência que diverge da definição. Correção: a definição é a única via; a deriva é detectada e reconciliada.

Exercício

Encontre os flocos de neve na sua frota

  1. Pegue um servidor que você opera. Se ele caísse agora, você conseguiria reconstruí-lo idêntico? A configuração dele existe em algum lugar além da própria máquina?
  2. Faça o teste da deriva: dois servidores que deveriam ser iguais no seu ambiente são realmente idênticos, ou acumularam ajustes manuais diferentes?
  3. Faça o teste do rebuild: quanto tempo levaria para recriar do zero o seu servidor mais crítico? Minutos (definição) ou dias (arqueologia)?
  4. Esboce a definição como código de um servidor: imagem-base, software, configuração, rede. O que hoje está só "na cabeça" ou só na máquina?
Meta: identificar os servidores-floco-de-neve da sua frota e planejar descrevê-los como código — para que reconstruir, escalar e auditar deixem de depender de configuração manual e memória.
✓ Checklist do tópico
  • A configuração de cada servidor é descrita em código versionado
  • Nenhuma configuração de infraestrutura é feita manualmente no terminal
  • Reconstruir um servidor é reexecutar a definição (idêntico, reproduzível)
  • A deriva entre a máquina real e a definição é detectada automaticamente
  • Mudanças na infraestrutura passam por revisão e ficam auditadas
Meta Dados · Engenharia de Plataformas · Capítulo X · §10.1Próximo → 10.2 Contêineres e Imutabilidade
Capítulo X · Cloud Native Platform Engineering
10.2

Contêineres e Imutabilidade

"Funciona na minha máquina" é o epitáfio de incontáveis horas perdidas. O código roda em um ambiente e falha em outro porque os ambientes diferem. A solução é empacotar a aplicação e seu ambiente numa imagem imutável que roda idêntica em todo lugar.

Pré-requisitos · 10.1 IaC · Cap IX §9.3 DeterminismoConecta com · 10.3 Orquestração · 10.4 EntregaTempo de leitura · ~13 min

O Problema

"Funciona na minha máquina" é, talvez, a frase mais cara da engenharia de software. Por trás dela há um abismo de horas perdidas: o código que roda perfeitamente no ambiente de desenvolvimento e falha misteriosamente em produção; o bug que não se reproduz na máquina de quem o investiga; a aplicação que se comporta diferente em cada servidor da frota. A causa é quase sempre a mesma — os ambientes diferem. Uma versão de biblioteca distinta aqui, uma variável de sistema ali, uma dependência presente em uma máquina e ausente em outra. O código é o mesmo, mas o ambiente em que ele roda não.

Essa divergência de ambientes é um veneno para tudo o que o livro defende. Ela destrói a reprodutibilidade (Cap IV, Cap IX §9.3): se o comportamento depende de em qual máquina o código roda, nada é confiável nem depurável. Ela transforma cada implantação numa aposta: o que funcionou em homologação pode falhar em produção porque os ambientes não eram idênticos. Ela faz da frota um campo minado, onde cada servidor pode ter uma diferença sutil esperando para causar um incidente. E ela desperdiça um tempo imenso em investigações que terminam na constatação frustrante de que "o ambiente estava diferente". Enquanto a aplicação e o ambiente onde ela roda forem coisas separadas — o código versionado, o ambiente improvisado em cada máquina —, a discrepância é inevitável, e com ela o "funciona na minha máquina".

O ambiente que difere entre máquinas

Quando o código viaja mas o ambiente fica para trás, cada máquina tem um ambiente ligeiramente diferente — e o código que funciona em uma falha em outra. Correção: empacotar aplicação e ambiente numa imagem imutável que roda idêntica em todo lugar.

A Decisão

A aplicação e seu ambiente são empacotados juntos numa imagem imutável, construída uma vez. A mesma imagem roda em desenvolvimento, homologação e produção — idêntica. O ambiente deixa de diferir, e "funciona na minha máquina" deixa de existir.

A plataforma empacota cada aplicação, junto com todo o seu ambiente, numa imagem de contêiner imutável. A imagem contém não só o código, mas tudo de que ele precisa para rodar: as bibliotecas nas versões exatas, as dependências, a configuração do ambiente. Essa imagem é construída uma vez, e é essa mesma imagem — bit por bit idêntica — que roda em desenvolvimento, em homologação e em produção. Como o ambiente viaja dentro da imagem junto com o código, ele não pode mais diferir entre máquinas: o que roda na máquina de quem desenvolve é exatamente o que roda em produção. A discrepância que causava o "funciona na minha máquina" simplesmente não tem mais onde existir. E a imagem é imutável: uma vez construída, não se altera. Precisa mudar algo? Constrói-se uma nova imagem, versionada, e substitui-se a anterior — nunca se remenda a imagem que está rodando. Isso traz a reprodutibilidade que o livro persegue ao nível da implantação: a imagem que passou nos testes é exatamente a que vai para produção, sem possibilidade de divergência. É a infraestrutura como código (§10.1) levada ao próprio artefato executável — o ambiente deixa de ser algo que se configura em cada máquina e passa a ser algo que se constrói uma vez e se reproduz em todo lugar.

Diagrama conceitual A mesma imagem imutável em todos os ambientes
UMA IMAGEMconstruída uma vez · imutável Desenvolvimentoa mesma imagem Homologaçãoa mesma imagem Produçãoa mesma imagem "funciona na minha máquina" deixa de existir
Figura 10.2-a. Uma imagem, construída uma vez e imutável, roda idêntica em desenvolvimento, homologação e produção. Como o ambiente viaja na imagem, ele não difere — e "funciona na minha máquina" deixa de existir.

A imagem que empacota o ambiente

ContainerfileDockerfile
# A IMAGEM empacota a aplicação E seu ambiente — versões exatas.
FROM runtime-app:8.2.1          # base fixa, não "a mais recente"

# As dependências, em versões TRAVADAS (reprodutível).
COPY dependencias.trava .
RUN instalar --das-travas dependencias.trava

# O código da aplicação.
COPY ./app /app

# A imagem resultante é IMUTÁVEL e roda idêntica em todo lugar.
ENTRYPOINT ["iniciar-app"]
A imagem fixa a base e trava as dependências: o ambiente inteiro é capturado. Construída uma vez, roda idêntica onde quer que vá.
imutabilidade.shBash
# IMUTÁVEL: a imagem é construída UMA vez, identificada por hash.
construir-imagem app:$(git rev-parse --short HEAD)
# → app:a1b2c3d  (uma versão imutável, rastreável ao commit)

# A MESMA imagem percorre os ambientes — nunca reconstruída por ambiente.
publicar app:a1b2c3d homologacao
# ...passou nos testes em homologação...
publicar app:a1b2c3d producao   # BIT POR BIT a mesma imagem

# Mudar algo? Constrói-se uma NOVA imagem. Nunca se remenda a atual.
A imagem é construída uma vez, identificada pelo commit, e a mesma percorre os ambientes. O que foi testado é exatamente o que vai a produção.

A Verificação

imagem_check.shBash
# A imutabilidade e a igualdade entre ambientes são VERIFICÁVEIS.

# 1) A imagem em produção é a MESMA que passou em homologação?
test "$(hash-imagem producao)" = "$(hash-imagem homologacao)" \
  || falhar "Imagens DIFERENTES entre ambientes!"

# 2) A imagem é imutável? (reconstruir o mesmo commit dá o mesmo hash)
test "$(reconstruir $COMMIT)" = "$(hash-imagem $COMMIT)" \
  || falhar "Build não reproduzível"
A verificação garante o essencial: a imagem em produção é idêntica à testada, e o build é reproduzível — a mesma entrada gera a mesma imagem.
Tabela de decisão Ambiente por máquina vs imagem imutável
AspectoAmbiente configurado por máquinaImagem imutável
Onde está o ambienteEm cada máquina, divergenteNa imagem, idêntico
"Na minha máquina"Bug que não reproduzNão existe mais
Testado vs produçãoPodem diferirBit por bit igual
Mudar o ambienteAjuste manual por máquinaNova imagem versionada
ReprodutibilidadeFrágilGarantida (§9.3)

Estudo de Caso

Caso aplicado

O fim do "funciona na minha máquina"

Contexto. As aplicações da plataforma rodavam em servidores cujos ambientes, configurados ao longo do tempo, divergiam sutilmente. O resultado eram bugs que apareciam em produção mas não em desenvolvimento, e implantações que funcionavam num ambiente e falhavam em outro — horas perdidas em "estava diferente aqui".

Ambiente por máquina
Diverge, bug não reproduz
Imagem imutável
Idêntica em todo lugar
Ganho
Reprodutibilidade na implantação

Decisão. Empacotar cada aplicação e todo o seu ambiente — bibliotecas em versões travadas, dependências, configuração — numa imagem de contêiner imutável, construída uma vez e identificada pelo commit. A mesma imagem percorre desenvolvimento, homologação e produção.

Resultado. A discrepância de ambientes desapareceu: o que roda na máquina de quem desenvolve é exatamente o que roda em produção, porque é a mesma imagem. "Funciona na minha máquina" deixou de ser uma desculpa possível — se funciona na imagem, funciona em todo lugar. E a imagem testada em homologação é, bit por bit, a que vai a produção, eliminando a aposta de cada implantação. A reprodutibilidade que o livro persegue chegou ao artefato executável.

Incidente Real

Severidade · Média

A dependência que existia só em um ambiente

  • Antes Numa fase sem contêineres, uma aplicação dependia de uma biblioteca de sistema que estava presente no ambiente de desenvolvimento mas ausente (ou em outra versão) em produção.
  • Sintoma A aplicação funcionava perfeitamente em desenvolvimento e falhava em produção — um "funciona na minha máquina" clássico, que custou horas até a diferença de ambiente ser descoberta.
  • Diagnóstico O ambiente não viajava com o código; cada máquina tinha o seu, e a divergência só se manifestava em produção.
  • Correção A aplicação e seu ambiente foram empacotados numa imagem imutável; a mesma imagem passou a rodar em todos os ambientes, eliminando a divergência.
  • Kaizen Vira regra: aplicações rodam a partir de imagens imutáveis que empacotam o ambiente; nenhum ambiente é configurado por máquina.

Decisão Arquitetural

ADR-0052

Aplicações rodam a partir de imagens imutáveis que empacotam o ambiente

Aceito
Contexto

Quando o ambiente é configurado por máquina, os ambientes divergem, e o código que funciona em um falha em outro — o "funciona na minha máquina". Isso destrói a reprodutibilidade (§9.3), torna cada implantação uma aposta e desperdiça horas em investigações de discrepância.

Decisão

Cada aplicação e todo o seu ambiente (bibliotecas em versões travadas, dependências, configuração) são empacotados numa imagem de contêiner imutável, construída uma vez e identificada pelo commit. A mesma imagem roda idêntica em todos os ambientes. Mudar é construir uma nova imagem; nunca se remenda a que roda.

Alternativas consideradas

Ambiente por máquina — diverge, "na minha máquina". Reconstruir a imagem por ambiente — quebra a garantia de igualdade. Imagem mutável remendada em produção — perde reprodutibilidade e rastreabilidade.

Consequências
PositivasFim do "na minha máquina". Reprodutibilidade na implantação. O testado é o que vai a produção. Ambiente versionado, não improvisado.
NegativasExige adotar contêineres e construir imagens reprodutíveis.
RiscosImagens grandes ou builds lentos; mitigados por camadas bem definidas e cache de build.

Antipadrões

Configurar o ambiente em cada máquina

Instalar dependências e ajustar o ambiente servidor a servidor. Garante divergência. Correção: o ambiente viaja na imagem imutável.

Remendar a imagem que está rodando

Alterar um contêiner em produção em vez de construir uma nova imagem. Correção: imagens são imutáveis; mudar é construir e substituir.

Exercício

Elimine o "funciona na minha máquina"

  1. Pegue uma aplicação sua. O ambiente em que ela roda (bibliotecas, dependências, configuração) é o mesmo em desenvolvimento e produção, ou pode diferir?
  2. Faça o teste da reprodução: quando um bug aparece em produção, você consegue reproduzi-lo localmente com facilidade, ou "o ambiente é diferente"?
  3. Faça o teste do testado-vs-produção: o que passou nos seus testes é bit por bit o que vai para produção, ou são construídos separadamente?
  4. Esboce a imagem: o que precisaria ser empacotado junto com o código para que o ambiente viajasse com a aplicação e nunca divergisse?
Meta: eliminar a divergência de ambientes empacotando aplicação e ambiente numa imagem imutável — para que "funciona na minha máquina" deixe de ser uma frase possível no seu sistema.
✓ Checklist do tópico
  • Cada aplicação e seu ambiente são empacotados numa imagem imutável
  • A mesma imagem roda em desenvolvimento, homologação e produção
  • As dependências e versões são travadas na imagem (reprodutível)
  • Mudar significa construir uma nova imagem, nunca remendar a que roda
  • A imagem que passou nos testes é exatamente a que vai a produção
Meta Dados · Engenharia de Plataformas · Capítulo X · §10.2Próximo → 10.3 Orquestração e Escala
Capítulo X · Cloud Native Platform Engineering
10.3

Orquestração e Escala

Dizer ao sistema "rode esta aplicação agora" é fácil — até a aplicação cair às três da manhã e ninguém estar lá para reerguê-la. A operação madura não dá ordens imperativas; ela declara o estado desejado, e um orquestrador trabalha sem cessar para que a realidade o alcance.

Pré-requisitos · 10.2 Imagens · Cap III Declarativo · Cap VII §7.5Conecta com · 10.4 Entrega · Cap VIIITempo de leitura · ~14 min

O Problema

A forma intuitiva de operar uma aplicação é imperativa: dá-se uma ordem — "inicie este processo", "rode esta aplicação neste servidor", "suba mais uma instância" —, a ordem é executada, e pronto. Funciona no momento em que a ordem é dada. Mas uma aplicação não vive só no momento em que é iniciada; ela precisa continuar rodando, dia após dia, através de falhas de máquina, picos de carga e o caos inevitável da operação real. E é aí que o modelo imperativo falha: ele responde "o que fazer agora", mas não "como o sistema deve permanecer".

A consequência é uma operação frágil e exaustiva. Uma aplicação iniciada por uma ordem imperativa, quando o processo morre ou a máquina cai, simplesmente fica fora do ar — até que um humano perceba e dê a ordem de reiniciá-la. E essa percepção raramente é imediata: a falha acontece às três da manhã, no fim de semana, no pior momento, e a aplicação permanece caída pelo tempo que levar até alguém notar e agir. Escalar manualmente diante de um pico é igualmente reativo e tardio. Operar uma frota inteira assim — vigiando cada aplicação, reerguendo cada queda à mão, ajustando cada escala manualmente — não escala humanamente: exige uma presença constante e sobre-humana, e ainda assim deixa lacunas em que o sistema fica fora do ar sem que ninguém saiba. O modelo imperativo trata a operação como uma série de ordens pontuais, quando ela é, na verdade, a manutenção contínua de um estado.

A ordem que não se sustenta

Uma aplicação iniciada por uma ordem imperativa, quando cai, fica fora do ar até um humano perceber e reerguê-la — às três da manhã, tarde demais. Correção: declarar o estado desejado e deixar um orquestrador convergir a realidade a ele, continuamente.

A Decisão

Não se dão ordens imperativas; declara-se o estado desejado ("3 réplicas desta imagem rodando"). Um orquestrador compara continuamente o desejado com o real e converge — recriando o que cai, ajustando a escala — sem intervenção humana.

A plataforma opera por estado desejado declarativo, não por ordens imperativas. Em vez de mandar "inicie a aplicação", declara-se o que deve ser verdade: "devem existir 3 réplicas desta imagem (§10.2) rodando, com estes recursos, nesta rede". Um orquestrador recebe essa declaração e assume a responsabilidade de torná-la realidade — e de mantê-la realidade. Ele compara, continuamente, o estado desejado (o que foi declarado) com o estado real (o que de fato está rodando), e age para eliminar qualquer divergência. Se uma réplica cai — porque o processo morreu, porque a máquina falhou —, o orquestrador percebe que o real (2 réplicas) diverge do desejado (3) e recria a réplica que falta, sem que nenhum humano precise perceber ou agir. É a autocura: o sistema se reergue sozinho, às três da manhã ou a qualquer hora, porque a manutenção do estado é responsabilidade do orquestrador, não da vigilância humana. Escalar é mudar o número declarado (de 3 para 10), e o orquestrador converge. Esse é exatamente o padrão que percorre o livro: o estado desejado é dado declarativo — uma descrição do que deve ser —, e o orquestrador é o motor que o realiza, assim como o metadado é dado e o engine o executa (Cap III). Declara-se a intenção; a máquina cuida da realização e da manutenção contínua.

Fluxograma operacional O orquestrador converge o real ao estado desejado
ESTADO DESEJADO"quero 3 réplicas rodando" ORQUESTRADORcompara desejado × reale converge continuamente REAL3 rodando caiu uma? o orquestrador recria — autocura imperativo: "faça X agora" — e quando X cair?
Figura 10.3-a. Declara-se o estado desejado; o orquestrador compara com o real e converge continuamente. Se uma réplica cai, ele a recria — autocura, sem intervenção humana às três da manhã.

Declarar o estado, não a ordem

estado_desejado.yamlYAML
# DECLARATIVO: o que deve ser verdade — não "faça isto agora".
aplicacao:
  imagem: app:a1b2c3d        # a imagem imutável (§10.2)
  replicas: 3                # DEVEM existir 3 rodando, sempre
  recursos:
    cpu: "500m"
    memoria: "512Mi"
  saude:
    verificacao: /saude      # como o orquestrador sabe que está viva

# O orquestrador MANTÉM isto verdadeiro. Caiu uma? Ele recria.
# Escalar = mudar 'replicas' para 10. Ele converge.
O arquivo declara o estado desejado — 3 réplicas saudáveis — não uma ordem pontual. O orquestrador mantém essa verdade, recriando o que cai.
convergencia.txttexto
  CICLO DE RECONCILIAÇÃO (o orquestrador, continuamente)

  desejado: 3 réplicas saudáveis de app:a1b2c3d
  ┌─────────────────────────────────────────────┐
  │ 1. observar o REAL ......... 3 rodando  ✓    │
  │ 2. comparar com DESEJADO ... bate           │
  │ 3. nada a fazer → esperar e repetir         │
  └─────────────────────────────────────────────┘
            ⋮  (uma réplica cai às 03:00)
  ┌─────────────────────────────────────────────┐
  │ 1. observar o REAL ......... 2 rodando  ✗    │
  │ 2. comparar com DESEJADO ... FALTA 1        │
  │ 3. AGIR → recriar a réplica (autocura)      │
  └─────────────────────────────────────────────┘
  nenhum humano foi acordado. o estado foi restaurado.
O orquestrador roda um laço perpétuo: observar o real, comparar com o desejado, agir na divergência. A queda às 3h é resolvida sem acordar ninguém.

A Verificação

AutocuraTest.phpPHP
final class AutocuraTest extends TestCase
{
    public function test_replica_que_cai_e_recriada(): void
    {
        $this->declarar(replicas: 3);
        $this->orquestrador->convergir();
        $this->assertSame(3, $this->replicasRodando());

        // Uma réplica cai (simulando falha de máquina).
        $this->derrubarUmaReplica();

        // O orquestrador converge: recria a que faltou — autocura.
        $this->orquestrador->convergir();
        $this->assertSame(3, $this->replicasRodando());  // de volta a 3
    }

    public function test_escalar_e_mudar_o_numero_desejado(): void
    {
        $this->declarar(replicas: 10);  // só muda a declaração
        $this->orquestrador->convergir();
        $this->assertSame(10, $this->replicasRodando());  // convergiu
    }
}
Os testes provam a autocura (uma réplica derrubada volta sozinha) e a escala declarativa (mudar o número desejado faz o orquestrador convergir).
Tabela de decisão Imperativo vs estado desejado
AspectoOrdem imperativaEstado desejado declarativo
O que se diz"Faça X agora""X deve ser verdade"
Réplica cai às 3hFica fora até humano agirOrquestrador recria (autocura)
EscalarSubir instâncias à mãoMudar o número declarado
Operar a frotaVigilância humana constanteConvergência automática
ModeloOrdens pontuaisManutenção contínua (Cap III)

Estudo de Caso

Caso aplicado

A aplicação que se reergue sozinha às três da manhã

Contexto. As aplicações da plataforma, iniciadas por procedimentos imperativos, ficavam fora do ar quando um processo morria ou uma máquina caía — até que alguém percebesse e as reiniciasse. As falhas no meio da madrugada significavam horas de indisponibilidade até a intervenção humana.

Imperativo
Caiu = fora até humano agir
Estado desejado
Caiu = orquestrador recria
Ganho
Autocura, sem vigília humana

Decisão. Operar por estado desejado declarativo: declara-se quantas réplicas de cada imagem (§10.2) devem rodar, e um orquestrador converge continuamente o real ao desejado, recriando o que cai e ajustando a escala conforme o número declarado.

Resultado. Quando uma réplica cai — a qualquer hora, inclusive às três da manhã —, o orquestrador percebe a divergência e a recria sozinho, sem acordar ninguém. A indisponibilidade que antes durava até a intervenção humana passou a ser resolvida em segundos pela autocura. Escalar tornou-se mudar um número. A operação deixou de exigir vigilância sobre-humana e passou a ser a manutenção contínua de um estado declarado — exatamente o espírito do humano às três da manhã que o Manifesto protege: o sistema cuida de si para que a pessoa não precise.

Incidente Real

Severidade · Alta

A queda na madrugada que ninguém viu a tempo

  • Antes Numa operação imperativa, uma aplicação crítica caiu durante a madrugada, quando não havia ninguém de plantão observando ativamente.
  • Sintoma A aplicação permaneceu fora do ar por um longo intervalo — desde a queda até alguém perceber pela manhã e reiniciá-la manualmente —, prolongando a indisponibilidade desnecessariamente.
  • Diagnóstico Sem um orquestrador mantendo o estado desejado, uma queda só era corrigida por intervenção humana, que dependia de alguém perceber — o que não acontece de imediato na madrugada.
  • Correção Adoção de orquestração por estado desejado: a aplicação passou a ser mantida em N réplicas saudáveis, com o orquestrador recriando automaticamente qualquer que caísse.
  • Kaizen Vira regra: aplicações rodam sob um orquestrador que mantém o estado desejado; quedas são curadas automaticamente, sem depender de vigilância humana.

Decisão Arquitetural

ADR-0053

Operação por estado desejado declarativo, com orquestração e autocura

Aceito
Contexto

A operação imperativa ("faça X agora") não se sustenta: uma aplicação que cai fica fora do ar até um humano perceber e reerguê-la — tarde demais na madrugada. Operar uma frota assim exige vigilância humana constante e ainda deixa lacunas de indisponibilidade.

Decisão

A operação é declarativa: declara-se o estado desejado (N réplicas saudáveis de uma imagem §10.2). Um orquestrador compara continuamente o desejado com o real e converge — recriando o que cai (autocura), ajustando a escala conforme o número declarado. O estado desejado é dado; o orquestrador é o motor que o realiza (como em Cap III).

Alternativas consideradas

Ordens imperativas — não se sustentam; queda fica até humano agir. Scripts de reinício reativos — dependem de detecção e disparo manuais. Vigilância humana 24/7 — não escala e ainda falha.

Consequências
PositivasAutocura sem intervenção. Indisponibilidade resolvida em segundos. Escala declarativa. Operação como manutenção contínua, não ordens pontuais. Protege o humano às 3h.
NegativasExige adotar um orquestrador e expressar a operação como estado desejado.
RiscosO orquestrador vira componente crítico; mitigado por sua própria alta disponibilidade e por estado desejado bem definido.

Antipadrões

Operar por ordens imperativas

Iniciar aplicações com ordens pontuais, sem nada que mantenha o estado. Uma queda fica fora do ar até intervenção. Correção: declarar o estado desejado; o orquestrador o mantém.

Escalar manualmente subindo instâncias

Ajustar a capacidade à mão, de forma reativa e tardia. Correção: escalar é mudar o número desejado; o orquestrador converge.

Exercício

Torne a sua operação declarativa

  1. Pegue uma aplicação sua. Como ela é mantida no ar hoje? Se o processo morrer agora, algo a reergue automaticamente, ou ela fica fora até alguém perceber?
  2. Faça o teste das três da manhã: se a sua aplicação mais crítica caísse na madrugada, quanto tempo ficaria fora do ar antes de alguém agir?
  3. Reescreva a operação como estado desejado: em vez de "iniciar a aplicação", o que você declararia que deve ser verdade (quantas réplicas, com que recursos, com que verificação de saúde)?
  4. Faça o teste da escala: aumentar a capacidade no seu sistema é mudar um número declarado, ou um procedimento manual de subir instâncias?
Meta: transformar a operação de pelo menos uma aplicação de ordens imperativas em estado desejado declarativo — para que quedas sejam curadas automaticamente e escalar seja mudar um número.
✓ Checklist do tópico
  • A operação é expressa como estado desejado declarativo, não ordens imperativas
  • Um orquestrador compara continuamente o desejado com o real e converge
  • Uma réplica que cai é recriada automaticamente (autocura), sem intervenção
  • Escalar é mudar o número declarado; o orquestrador converge a ele
  • A disponibilidade não depende de vigilância humana constante
Meta Dados · Engenharia de Plataformas · Capítulo X · §10.3Próximo → 10.4 Entrega Contínua Segura
Capítulo X · Cloud Native Platform Engineering
10.4

Entrega Contínua Segura

Quando implantar dá medo, implanta-se menos — e cada implantação acumula mais mudanças, ficando ainda mais arriscada. É um ciclo vicioso. A saída é tornar a entrega tão segura e automática que ela deixe de assustar: portões que barram o ruim, liberação gradual, e a volta atrás sem drama.

Pré-requisitos · 10.2 Imagens · 10.3 Orquestração · Cap VII §7.1 · Cap IX §9.4Conecta com · 10.5 Plataforma · Cap VIIITempo de leitura · ~14 min

O Problema

Há uma relação perversa entre o medo de implantar e o risco de implantar, e ela se retroalimenta. Quando colocar uma mudança em produção é um evento manual, tenso e arriscado — feito à mão, sem rede, na esperança de que nada quebre —, a reação natural é implantar o menos possível. Acumulam-se mudanças para "soltar tudo de uma vez", para não repetir o transtorno com frequência. Mas isso é exatamente o oposto do que deveria acontecer: cada implantação passa a carregar um lote enorme de mudanças, o que a torna mais arriscada e mais difícil de diagnosticar quando algo dá errado — o que, por sua vez, aumenta o medo. Quanto mais se teme implantar, menos se implanta; quanto menos se implanta, mais arriscado fica; quanto mais arriscado, mais se teme. É um ciclo vicioso que paralisa a evolução do sistema.

E os perigos que justificam o medo são reais. Uma implantação manual pode subir código que não passou nos testes, porque alguém pulou uma etapa na pressa. Pode vazar um segredo (§7.2), porque a verificação não foi feita. Pode levar à produção uma versão com uma regressão que só se manifesta sob carga real. E quando o problema aparece em produção, a única saída costuma ser uma correção de emergência sob pânico, porque não há um caminho rápido e seguro de voltar atrás. Cada um desses perigos é uma boa razão para temer implantar — mas o medo, ao reduzir a frequência, só os agrava. O problema de fundo é que a entrega é tratada como um ato heroico e arriscado, quando deveria ser uma rotina segura e mundana.

O ciclo vicioso do medo de implantar

Implantar com medo leva a implantar raramente, o que acumula mudanças e aumenta o risco — que realimenta o medo. Mudanças grandes e raras são mais perigosas, não menos. Correção: tornar a entrega segura e automática, para que seja frequente e mundana.

A Decisão

A entrega é um caminho automático e seguro: do commit à produção, portões barram o que é ruim (testes, avaliação §9.4, segredos §7.2); a liberação é gradual (canário); e uma piora dispara rollback automático. Errar fica barato, e implantar deixa de assustar.

A plataforma transforma a entrega de um ato heroico numa rotina segura e automática, montando um caminho que vai do commit à produção com defesas em cada etapa. Primeiro, uma sequência de portões: nenhuma mudança avança sem passar por todos. Os testes precisam passar; a avaliação da IA (§9.4) precisa provar que não houve regressão; a verificação de segredos (§7.2) precisa confirmar que nada sensível vaza; as checagens de segurança precisam estar verdes. Qualquer portão que reprove impede a subida — é o "negar por padrão" do §7.1 aplicado à entrega: só passa o que prova estar bom. Segundo, a liberação é gradual: em vez de trocar toda a produção de uma vez, a nova versão recebe primeiro uma fração do tráfego (um "canário"), enquanto a observabilidade (Cap VIII) vigia se as métricas pioram. Terceiro, e decisivo, há rollback automático: se o canário mostra qualquer degradação, o sistema volta sozinho à versão anterior — que, sendo uma imagem imutável (§10.2), está intacta e pronta — sem correção de emergência, sem pânico, sem drama. O efeito combinado quebra o ciclo vicioso: como errar agora é barato (o portão barra a maioria dos erros antes; o canário limita o alcance dos que passam; o rollback desfaz em segundos), implantar deixa de assustar. E como implantar não assusta, implanta-se com frequência — mudanças pequenas, fáceis de diagnosticar, de baixo risco. O medo dá lugar à confiança, e a confiança a um fluxo contínuo e saudável de evolução.

Fluxograma operacional Do commit à produção: portões, canário, rollback
commit testes+ avaliação §9.4 segurançasegredos §7.2 canário% do tráfego produção qualquer portão reprova → NÃO sobe canário piora? ROLLBACK AUTOMÁTICOvolta à versão anterior (imutável §10.2) — sem drama
Figura 10.4-a. Portões (testes, avaliação §9.4, segredos §7.2) barram o ruim antes de subir; a liberação é gradual (canário); e uma piora dispara rollback automático para a versão anterior imutável. Errar fica barato.

O caminho com portões e rollback

entrega.yamlYAML
# A entrega é um caminho com PORTÕES — negar por padrão (§7.1).
pipeline:
  portoes:                    # qualquer um que reprove → NÃO sobe
    - testes_unitarios
    - testes_integracao
    - avaliacao_ia            # §9.4: a IA não regrediu
    - varredura_de_segredos   # §7.2: nada sensível vaza
    - checagem_seguranca

  liberacao:
    estrategia: canario       # gradual, não tudo de uma vez
    passos: [5%, 25%, 100%]   # fração crescente do tráfego
    vigia: metricas-vol-viii  # observabilidade acompanha
    rollback_automatico: true  # piorou? volta sozinho
A entrega declara portões (que barram o ruim) e uma liberação gradual com rollback automático. Só sobe o que passa; o que piora, volta.
rollback.shBash
# ROLLBACK automático: trivial porque as imagens são imutáveis (§10.2).

# O canário a 5% piorou as métricas (Cap VIII detectou)?
if metricas-pioraram canario; then
  # Volta ao estado desejado (§10.3) com a versão ANTERIOR — intacta.
  declarar-estado imagem=app:VERSAO_ANTERIOR
  # Em segundos, sem correção de emergência, sem pânico.
  alertar "rollback automático: canário reprovado"
fi
# Errar ficou barato → implantar deixou de assustar.
O rollback é só declarar o estado desejado com a versão anterior (§10.3) — que, imutável (§10.2), está pronta. Voltar atrás vira rotina, não crise.

A Verificação

EntregaSeguraTest.phpPHP
final class EntregaSeguraTest extends TestCase
{
    public function test_portao_reprovado_impede_a_subida(): void
    {
        // Um teste falhando é um portão reprovado.
        $resultado = $this->pipeline->executar($this->commitComTesteQuebrado());

        // Negar por padrão (§7.1): NÃO chega a produção.
        $this->assertFalse($resultado->chegouAProducao());
    }

    public function test_canario_pior_dispara_rollback(): void
    {
        $this->iniciarCanario($this->versaoComRegressao());
        $this->metricas->degradar();  // o canário piora

        $this->pipeline->avaliarCanario();

        // Voltou sozinho à versão anterior — sem intervenção.
        $this->assertSame($this->versaoAnterior(), $this->versaoEmProducao());
    }
}
Os testes provam as duas defesas: um portão reprovado impede a subida, e um canário que piora dispara rollback automático para a versão anterior.
Tabela de decisão Entrega heroica vs entrega segura
AspectoImplantação manual e arriscadaEntrega contínua segura
FrequênciaRara, lotes grandesFrequente, mudanças pequenas
Código não testadoPode subir (pulou etapa)Portão barra (§7.1)
Alcance de um erroToda a produção de uma vezLimitado pelo canário
Voltar atrásCorreção sob pânicoRollback automático (§10.2)
SentimentoMedoRotina mundana

Estudo de Caso

Caso aplicado

Quando implantar deixou de ser um evento

Contexto. Colocar mudanças em produção era um procedimento manual e tenso, evitado sempre que possível. O resultado eram implantações raras, carregando grandes lotes de mudanças — mais arriscadas e mais difíceis de diagnosticar, alimentando ainda mais o receio de implantar.

Entrega heroica
Rara, arriscada, temida
Entrega segura
Frequente, mundana, confiável
Ganho
Evolução desimpedida

Decisão. Montar um caminho de entrega automático e seguro: portões obrigatórios (testes, avaliação §9.4, varredura de segredos §7.2) que barram o que é ruim antes de subir; liberação gradual por canário, vigiada pela observabilidade (Cap VIII); e rollback automático para a versão anterior imutável (§10.2) ao primeiro sinal de degradação.

Resultado. Errar tornou-se barato: a maioria dos problemas é barrada nos portões, os que passam têm alcance limitado pelo canário, e qualquer degradação é desfeita em segundos pelo rollback. Como implantar deixou de assustar, passou a ser frequente — mudanças pequenas e fáceis de diagnosticar, em vez de grandes lotes arriscados. O ciclo vicioso do medo deu lugar a um fluxo contínuo e saudável: a entrega virou rotina mundana, e a evolução do sistema, desimpedida.

Incidente Real

Severidade · Alta

O lote enorme que ninguém conseguia diagnosticar

  • Antes Por medo de implantar, as mudanças foram acumuladas e soltas de uma vez num único lote grande, após um longo período sem implantações.
  • Sintoma Algo quebrou em produção, mas com dezenas de mudanças no mesmo lote, identificar qual delas causou o problema foi demorado e difícil — o tamanho do lote transformou um bug num mistério.
  • Diagnóstico A baixa frequência de entrega, motivada pelo medo, produziu lotes grandes em que a causa de qualquer falha fica diluída e difícil de isolar.
  • Correção Adoção de entrega contínua segura (portões, canário, rollback), permitindo implantações pequenas e frequentes, em que cada falha é fácil de atribuir e reverter.
  • Kaizen Vira regra: implantar é frequente e em pequenos incrementos; o caminho seguro de entrega torna cada mudança barata de subir e de reverter.

Decisão Arquitetural

ADR-0054

Entrega contínua segura: portões, liberação gradual e rollback automático

Aceito
Contexto

Quando implantar dá medo, implanta-se raramente, acumulando mudanças em lotes grandes que são mais arriscados e mais difíceis de diagnosticar — o que realimenta o medo. A entrega tratada como ato heroico paralisa a evolução e agrava os próprios riscos que a assustam.

Decisão

A entrega é um caminho automático e seguro do commit à produção: portões obrigatórios (testes, avaliação §9.4, varredura de segredos §7.2, segurança) barram o ruim — negar por padrão (§7.1); a liberação é gradual (canário) vigiada pela observabilidade (Cap VIII); e uma degradação dispara rollback automático para a versão anterior imutável (§10.2). Errar fica barato, e implantar deixa de assustar.

Alternativas consideradas

Implantação manual heroica — rara, arriscada, lotes grandes. Sem portões — sobe código não testado ou com segredo. Troca total sem canário — um erro atinge toda a produção. Sem rollback — correção sob pânico.

Consequências
PositivasImplantação frequente e mundana. Erros barrados ou de alcance limitado. Reversão em segundos. Lotes pequenos, fáceis de diagnosticar. Fim do ciclo do medo.
NegativasExige construir o pipeline com portões, canário e rollback automatizados.
RiscosPipeline mal calibrado pode barrar demais ou de menos; mitigado por ajustar portões e métricas de canário com base na experiência.

Antipadrões

Implantação manual sem portões

Subir à produção à mão, podendo pular testes ou verificações. Correção: portões automáticos obrigatórios; só sobe o que prova estar bom (§7.1).

Troca total sem canário nem rollback

Substituir toda a produção de uma vez, sem volta fácil. Correção: liberação gradual vigiada, com rollback automático à versão anterior imutável.

Exercício

Quebre o seu ciclo do medo

  1. Pergunte-se honestamente: implantar no seu sistema dá medo? Com que frequência vocês implantam — várias vezes ao dia, ou raramente, em lotes grandes?
  2. Faça o teste dos portões: o que impede hoje que código não testado, ou um segredo, chegue à produção? Há portões automáticos, ou depende de alguém lembrar?
  3. Faça o teste do alcance: quando uma mudança ruim sobe, ela atinge toda a produção de uma vez, ou há uma liberação gradual que limita o estrago?
  4. Faça o teste da volta: se uma implantação piora as coisas, quanto tempo leva para voltar à versão anterior? É automático, ou uma correção de emergência?
Meta: desenhar um caminho de entrega que torne errar barato — com portões, liberação gradual e rollback — para quebrar o ciclo vicioso do medo e tornar implantar uma rotina mundana.
✓ Checklist do tópico
  • Há um caminho automático do commit à produção, com portões obrigatórios
  • Os portões incluem testes, avaliação da IA (§9.4) e varredura de segredos (§7.2)
  • Qualquer portão reprovado impede a subida (negar por padrão, §7.1)
  • A liberação é gradual (canário), vigiada pela observabilidade (Cap VIII)
  • Uma degradação dispara rollback automático para a versão anterior imutável
Meta Dados · Engenharia de Plataformas · Capítulo X · §10.4Próximo → 10.5 A Plataforma como Produto Interno
Capítulo X · Cloud Native Platform Engineering
10.5

A Plataforma como Produto Interno

Reúne-se tudo deste capítulo — infraestrutura como código, contêineres, orquestração, entrega segura — e pergunta-se: quem o usa? Se cada time precisa montar isso sozinho, a complexidade se multiplica e diverge. A engenharia de plataforma trata quem constrói como cliente, e pavimenta o caminho para ele.

Pré-requisitos · 10.1–10.4 · Cap V Experiência · O ManifestoConecta com · encerra o Cap XTempo de leitura · ~13 min

O Problema

Este volume montou um arsenal: infraestrutura como código (§10.1), imagens imutáveis (§10.2), orquestração por estado desejado (§10.3), entrega contínua segura (§10.4). Cada peça é poderosa. Mas surge uma pergunta que decide se todo esse poder ajuda ou atrapalha: quem precisa usar tudo isso? Se a resposta for "cada time, por conta própria", então cada equipe que quer entregar uma aplicação precisa, antes, dominar e montar toda essa maquinaria — escrever sua própria infraestrutura como código, configurar seu próprio pipeline, definir sua própria orquestração. A complexidade que deveria ser uma fundação vira um fardo distribuído.

E esse fardo distribuído tem custos severos. Há duplicação: cada time reinventa as mesmas soluções, gastando esforço repetido naquilo que já foi resolvido em outro canto da organização. Há divergência: cada reinvenção é um pouco diferente, então a organização acaba com dezenas de pipelines, padrões de infraestrutura e configurações de segurança ligeiramente incompatíveis — um zoológico difícil de manter, auditar e proteger. Há risco: nem todo time domina segurança, observabilidade ou entrega segura com a mesma profundidade, então alguns reinventam mal, deixando brechas e fragilidades que uma fundação comum teria evitado. E há desperdício de foco: os times que deveriam estar resolvendo problemas de negócio gastam seu tempo lutando com encanamento de infraestrutura. A alternativa ingênua — uma equipe central que faz tudo por todos — só troca um problema por outro: vira um gargalo, onde cada time fica esperando na fila a equipe central atender seu pedido. Nem o "cada um por si" nem o "tudo centralizado" funcionam; ambos desperdiçam o poder que o volume construiu.

O poder que vira fardo

Se cada time precisa montar sozinho a infraestrutura, contêineres, orquestração e entrega, a complexidade se duplica, diverge e abre riscos — e o foco vai para o encanamento, não o negócio. Correção: tratar a plataforma como um produto, pavimentando o caminho para quem constrói sobre ela.

A Decisão

A infraestrutura interna é um produto, e quem constrói sobre ela é o cliente. Em vez de cada time montar tudo, oferece-se um caminho pavimentado — padrões prontos e autosserviço — em que o jeito mais fácil de fazer algo é também o jeito certo, seguro e observável.

A engenharia de plataforma resolve isso com uma mudança de perspectiva: a infraestrutura interna deixa de ser um amontoado de ferramentas e passa a ser tratada como um produto, cujos clientes são os times que constroem sobre ela. E, como todo bom produto, ela é desenhada a partir das necessidades de seus usuários — aqui, a necessidade de entregar aplicações sem ter que dominar e remontar toda a maquinaria deste capítulo. A entrega central dessa plataforma-produto é o caminho pavimentado (golden path): um trajeto bem cuidado, autosserviço, pelo qual um time consegue criar, implantar e operar uma aplicação seguindo padrões prontos — infraestrutura como código já modelada, imagens já estruturadas, orquestração já configurada, entrega segura (§10.4) já montada — sem reinventar nada. O princípio que torna isso poderoso é este: no caminho pavimentado, o jeito mais fácil de fazer algo é também o jeito certo. Seguir o padrão — que já incorpora segurança (Cap VII), observabilidade (Cap VIII) e entrega segura — é o caminho de menor esforço, então os times naturalmente o seguem, e ganham essas qualidades de graça, sem precisar ser especialistas em cada uma. Quem precisa sair do caminho pavimentado pode, mas o padrão atende à grande maioria. E, crucialmente, a plataforma é autosserviço: o time usa o caminho sozinho, sem depender de uma fila numa equipe central — o que elimina tanto o "cada um reinventa" quanto o gargalo do "tudo centralizado". No fundo, isto é a empatia do Manifesto voltada para dentro: assim como projetamos pensando no humano solitário que opera o sistema às três da manhã, projetamos a plataforma pensando no colega que constrói sobre ela — poupando-lhe o fardo do encanamento para que ele foque no que importa. A plataforma deixa de ser um obstáculo entre o time e a entrega, e passa a ser uma alavanca que o impulsiona.

Diagrama conceitual O caminho pavimentado: o jeito fácil é o jeito certo
CADA TIME REINVENTAinfra, pipeline, padrõesduplicação, divergência, risco CAMINHO PAVIMENTADOo jeito fácil é o jeito certoautosserviço, padrões prontos A PLATAFORMA É UM PRODUTOquem a usa (quem constrói) é o cliente a empatia do Manifesto, voltada para dentro
Figura 10.5-a. Em vez de cada time reinventar (duplicação, divergência, risco), a plataforma-produto oferece um caminho pavimentado: autosserviço, com padrões prontos em que o jeito fácil é o jeito certo. A empatia do Manifesto, para dentro.

O caminho pavimentado, autosserviço

caminho_pavimentado.yamlYAML
# O time descreve só o ESSENCIAL do seu serviço — o resto é padrão.
servico:
  nome: cobrancas
  time: financeiro
  caminho: padrao-web   # golden path: tudo abaixo vem pronto

# Vindo de graça com 'padrao-web' (o jeito fácil = o jeito certo):
#   ✓ infraestrutura como código (§10.1) já modelada
#   ✓ imagem imutável (§10.2) com build reprodutível
#   ✓ orquestração com autocura (§10.3)
#   ✓ entrega segura: portões + canário + rollback (§10.4)
#   ✓ observabilidade (Cap VIII) e hardening (Cap VII) embutidos
O time declara o essencial e escolhe um caminho pavimentado; toda a maquinaria do capítulo vem pronta. Seguir o padrão é o caminho de menor esforço.
autosservico.shBash
# AUTOSSERVIÇO: o time cria seu serviço sozinho — sem fila, sem gargalo.
plataforma criar-servico --caminho padrao-web --nome cobrancas
# → repositório, pipeline, infra e observabilidade provisionados.

# O time foca no NEGÓCIO; o encanamento já está resolvido.
# A plataforma é PRODUTO: medimos se ela serve bem seus clientes.
plataforma metricas-de-adocao   # quantos usam o caminho? onde travam?
Um comando provisiona tudo, sem depender de uma equipe central. E a plataforma, como produto, mede sua própria adoção — se serve bem quem a usa.

A Verificação

PlataformaProdutoTest.phpPHP
final class CaminhoPavimentadoTest extends TestCase
{
    public function test_caminho_padrao_traz_seguranca_e_observabilidade(): void
    {
        // Um serviço criado pelo caminho pavimentado...
        $servico = $this->plataforma->criarServico('padrao-web', 'cobrancas');

        // ...vem com as qualidades certas DE GRAÇA (o jeito fácil = certo).
        $this->assertTrue($servico->temObservabilidade());   // Cap VIII
        $this->assertTrue($servico->temHardening());         // Cap VII
        $this->assertTrue($servico->temEntregaSegura());      // §10.4
    }

    public function test_e_autosservico_sem_equipe_central(): void
    {
        // O time provisiona sozinho — sem fila numa equipe central.
        $this->assertFalse($this->plataforma->exigiuAprovacaoManual('cobrancas'));
    }
}
Os testes garantem o cerne: o caminho padrão entrega segurança e observabilidade de graça, e é autosserviço — sem gargalo de uma equipe central.
Tabela de decisão Cada um por si vs plataforma como produto
AspectoCada time reinventaTudo centralizadoPlataforma-produto
EsforçoDuplicadoFila centralCaminho pronto
ConsistênciaDivergeUniformePadrão comum
Segurança/Obs.Depende do timeCentralDe graça no caminho
VelocidadeLenta (remontar)GargaloAutosserviço
Foco do timeEncanamentoEsperaNegócio

Estudo de Caso

Caso aplicado

Pavimentar o caminho em vez de distribuir o fardo

Contexto. Com toda a maquinaria cloud native disponível (§10.1–§10.4), surgiu a questão de como os times a usariam. Deixar cada um montar tudo sozinho duplicaria esforço, geraria divergência e abriria riscos onde algum time reinventasse mal a segurança ou a observabilidade; centralizar tudo numa equipe criaria um gargalo.

Fardo distribuído
Duplicação, divergência, risco
Plataforma-produto
Caminho pavimentado, autosserviço
Ganho
Times focados no negócio

Decisão. Tratar a infraestrutura interna como um produto, com os times como clientes, e oferecer um caminho pavimentado autosserviço: padrões prontos que já embutem infraestrutura como código, imagens imutáveis, orquestração com autocura, entrega segura, observabilidade e hardening. No caminho, o jeito mais fácil é o jeito certo.

Resultado. Os times entregam aplicações seguindo o caminho pavimentado, sem reinventar a maquinaria nem esperar numa fila — e ganham segurança e observabilidade de graça, por seguir o padrão. O esforço deixou de ser duplicado, a divergência deu lugar a um padrão comum, e o foco voltou para o negócio em vez do encanamento. A plataforma deixou de ser um obstáculo entre o time e a entrega para ser a alavanca que o impulsiona — a empatia do Manifesto, voltada para os próprios colegas que constroem.

Incidente Real

Severidade · Média

A reinvenção que deixou um serviço sem observabilidade

  • Antes Sem um caminho pavimentado, cada time montava sua própria infraestrutura e entrega; um serviço acabou sem observabilidade adequada porque o time que o construiu não a priorizou ao reinventar sua base.
  • Sintoma Quando esse serviço teve um problema, faltava a instrumentação para diagnosticá-lo — uma lacuna que um padrão comum teria evitado, mas que a reinvenção individual deixou passar.
  • Diagnóstico A ausência de um caminho pavimentado fez cada time depender da própria profundidade em cada área; onde faltou, surgiram lacunas como a observabilidade ausente.
  • Correção Criação de um caminho pavimentado autosserviço que já embute observabilidade, segurança e entrega segura; o serviço foi migrado para o padrão e ganhou as qualidades que faltavam.
  • Kaizen Vira regra: há um caminho pavimentado onde o jeito fácil é o jeito certo; seguir o padrão traz segurança e observabilidade por construção, sem depender de cada time reinventá-las.

Decisão Arquitetural

ADR-0055

A plataforma interna é um produto, com caminho pavimentado e autosserviço

Aceito
Contexto

Com a maquinaria cloud native (§10.1–§10.4) disponível, deixar cada time montá-la sozinho gera duplicação, divergência e risco (reinvenções malfeitas), e desvia o foco do negócio para o encanamento. Centralizar tudo numa equipe vira gargalo. Ambos desperdiçam o poder construído.

Decisão

A infraestrutura interna é tratada como um produto, cujos clientes são os times que constroem sobre ela. Oferece-se um caminho pavimentado (golden path) autosserviço, com padrões prontos que embutem IaC, imagens imutáveis, orquestração, entrega segura (§10.4), observabilidade (Cap VIII) e hardening (Cap VII). No caminho, o jeito mais fácil é o jeito certo. É a empatia do Manifesto voltada para dentro.

Alternativas consideradas

Cada time reinventa — duplicação, divergência, risco. Equipe central faz tudo — gargalo, fila. Padrões só recomendados (não pavimentados) — sem o caminho fácil, são ignorados.

Consequências
PositivasTimes focados no negócio, não no encanamento. Segurança e observabilidade de graça no padrão. Sem duplicação nem gargalo. Consistência por construção.
NegativasExige uma equipe que trate a plataforma como produto e construa/mantenha os caminhos pavimentados.
RiscosUm caminho rígido demais sufoca casos legítimos; mitigado por permitir sair do padrão quando necessário, mantendo-o atraente para a maioria.

Antipadrões

Distribuir o fardo da infraestrutura

Cada time montando sozinho toda a maquinaria. Duplica, diverge e abre riscos. Correção: um caminho pavimentado autosserviço onde o jeito fácil é o jeito certo.

A equipe de plataforma como gargalo

Centralizar tudo numa fila que os times precisam esperar. Correção: a plataforma é autosserviço; ela habilita, não intermedia cada pedido.

Exercício

Avalie a sua plataforma interna

  1. Na sua organização, como um time coloca uma nova aplicação no ar? Ele segue um caminho pronto, ou monta a infraestrutura, o pipeline e a observabilidade do zero a cada vez?
  2. Faça o teste da duplicação: quantas versões ligeiramente diferentes de "pipeline de entrega" ou "configuração de servidor" existem? Cada divergência é um custo de manutenção e um risco.
  3. Faça o teste do jeito fácil: no seu ambiente, seguir as boas práticas (segurança, observabilidade) é o caminho de menor esforço, ou exige trabalho extra que os times tendem a pular?
  4. Faça o teste do gargalo: para provisionar algo, um time consegue se servir sozinho, ou depende de esperar numa fila uma equipe central atender?
Meta: avaliar se a sua infraestrutura interna é um fardo distribuído ou um produto com caminho pavimentado — e planejar como tornar o jeito fácil também o jeito certo, em autosserviço.
✓ Checklist do tópico
  • A infraestrutura interna é tratada como um produto, com os times como clientes
  • Há um caminho pavimentado (golden path) que evita cada time reinventar a maquinaria
  • No caminho pavimentado, o jeito mais fácil de fazer algo é também o jeito certo
  • Seguir o padrão traz segurança e observabilidade por construção, de graça
  • A plataforma é autosserviço — habilita os times sem virar um gargalo central
Meta Dados · Engenharia de Plataformas · Capítulo X · §10.5Fim do Capítulo X · voltar ao início
Meta Dados · Engenharia de Plataformas
Capítulo XI

Data Engineering
e Analytics

Mover dados em escala sem confundir o que registra com o que analisa: pipelines reprodutíveis, ingestão em massa, a separação entre operação e análise, a qualidade como contrato e a verdade que sustenta a decisão.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo XI
Capítulo XI · Data Engineering e Analytics
11.1

O Pipeline Reprodutível

Um pipeline de dados que produz um resultado diferente cada vez que roda é um pipeline em que ninguém pode confiar. Se reprocessar a mesma fonte dá números distintos, qual está certo? A reprodutibilidade que o livro exige do código vale, igualmente, para os dados em movimento.

Pré-requisitos · Cap IV Reprodutibilidade · Cap VI §6.2 · Cap IX §9.3Conecta com · 11.2 Ingestão · 11.4 QualidadeTempo de leitura · ~14 min

O Problema

Um pipeline de dados move informação de uma fonte para um destino, transformando-a no caminho — lê de um sistema de origem, limpa, agrega, e grava num repositório onde será analisada. A tentação, ao construí-lo, é focar apenas em fazê-lo funcionar uma vez: rodou, os dados apareceram no destino, pronto. Mas um pipeline de dados não roda uma vez; ele roda repetidamente — todo dia, toda hora — e precisa, às vezes, reprocessar dados antigos (um backfill) porque a lógica mudou ou uma falha exigiu refazer. E é aí que a falta de reprodutibilidade se revela catastrófica.

Um pipeline não-reprodutível produz resultados diferentes para a mesma entrada. Reprocessa-se a mesma fonte de ontem e obtêm-se números distintos dos que se obteve antes — porque o pipeline depende da ordem em que processou as linhas, ou do momento em que rodou, ou de algum estado mutável que mudou no meio. Isso é veneno para a análise: se a mesma fonte gera resultados diferentes a cada execução, qual está certo? Não há fonte de verdade — há apenas a última execução, que pode estar tão errada quanto a anterior. Pior ainda é quando o pipeline falha no meio: sem reprodutibilidade e sem idempotência, reprocessar pode duplicar dados (as linhas que já tinham sido gravadas antes da falha são gravadas de novo) ou corromper o destino. O backfill, que deveria ser uma operação rotineira, vira um campo minado em que cada reprocessamento ameaça a integridade dos dados. Construir análise, relatórios e decisões sobre um pipeline irreprodutível é construir sobre areia movediça — os números mudam debaixo dos pés, e ninguém sabe em qual confiar.

O pipeline que dá números diferentes a cada execução

Se reprocessar a mesma fonte produz resultados distintos, não há fonte de verdade — e reprocessar após uma falha pode duplicar ou corromper dados. Correção: o pipeline é determinístico e idempotente; a mesma entrada produz sempre o mesmo resultado, e reexecutar é seguro.

A Decisão

O pipeline é determinístico e idempotente: a mesma entrada produz sempre o mesmo resultado, independentemente da ordem ou do momento, e reexecutá-lo é seguro — não duplica nem corrompe. O backfill vira rotina, e os dados ganham uma fonte de verdade.

A plataforma constrói pipelines de dados com as mesmas garantias que exige do resto do sistema: determinismo e idempotência. Determinismo significa que o resultado depende apenas da entrada, não de fatores acidentais: a mesma fonte, processada hoje ou daqui a um mês, na ordem A ou na ordem B, produz exatamente o mesmo resultado. Para isso, o pipeline evita depender da ordem de processamento, do relógio do momento em que roda, ou de qualquer estado mutável externo — tudo o que ele precisa vem da entrada, explicitamente. Idempotência (§6.2 Cap VI) significa que reexecutar o pipeline sobre a mesma fonte é seguro: ele não duplica os dados já processados nem corrompe o destino; processar a mesma partição de dados uma vez ou três vezes leva ao mesmo estado final. Tipicamente, isso se faz processando os dados em partições bem definidas (por exemplo, por dia) e tornando a gravação de cada partição idempotente — reprocessar o dia substitui aquela partição de forma limpa, em vez de acumular. As consequências são transformadoras. Os dados ganham uma fonte de verdade: a mesma fonte sempre gera o mesmo resultado, então o número está certo porque é reproduzível, não porque foi o último a ser calculado. O backfill torna-se uma operação trivial e segura: mudou a lógica? Reprocessa-se o histórico inteiro com confiança, sabendo que o resultado será consistente e que nada será duplicado. E a recuperação de falhas deixa de assustar: o pipeline falhou no meio? Roda-se de novo, e a idempotência garante que o estado final estará correto. É a reprodutibilidade que o Capítulo IV estabeleceu para os dados em repouso e o Capítulo IX exigiu da execução — aqui aplicada aos dados em movimento.

Diagrama conceitual O pipeline determinístico e idempotente
MESMA ENTRADAa fonte, num instante PIPELINEdeterminístico · idempotente · versionadorodar de novo = mesmo resultado MESMO RESULTADOsempre, reexecutável rodou de novo e deu diferente? não dá pra confiar falhou no meio? reprocessar é seguro (idempotente)
Figura 11.1-a. A mesma entrada produz sempre o mesmo resultado, independentemente da ordem ou do momento — e reexecutar após uma falha é seguro (idempotente). É a reprodutibilidade aplicada aos dados em movimento.

Processar partições de forma idempotente

pipeline_idempotente.pyPython
# Pipeline DETERMINÍSTICO e IDEMPOTENTE: por partição (ex.: por dia).
def processar_particao(data: date, fonte: Fonte, destino: Destino) -> None:
    # 1) Lê SÓ a partição daquela data — entrada explícita, sem "agora".
    linhas = fonte.ler(particao=data)

    # 2) Transforma de forma determinística (sem depender de ordem).
    resultado = transformar(linhas)   # mesma entrada → mesmo resultado

    # 3) Grava SUBSTITUINDO a partição — idempotente (§6.2).
    #    Reprocessar o dia troca a partição limpa; nunca acumula.
    destino.substituir_particao(particao=data, dados=resultado)
    # Rodar 1x ou 3x para a mesma data → MESMO estado final.
O pipeline processa partições explícitas e grava substituindo a partição — não acumulando. Reprocessar um dia é seguro: o estado final é sempre o mesmo.
backfill.pyPython
# BACKFILL: reprocessar o histórico vira rotina segura.
def backfill(de: date, ate: date) -> None:
    # A lógica mudou? Reprocessa cada dia do período com confiança.
    for dia in intervalo_de_dias(de, ate):
        # Idempotente: reprocessar não duplica nem corrompe.
        processar_particao(dia, fonte, destino)
    # Resultado consistente em todo o histórico — reproduzível.

# Falhou no meio do backfill? Rode de novo. A idempotência cuida.
Como cada partição é idempotente, o backfill do histórico inteiro é seguro e reexecutável. Mudar a lógica e reprocessar tudo deixa de ser arriscado.

A Verificação

pipeline_test.pyPython
class TestPipelineReprodutivel(TestCase):
    def test_mesma_entrada_mesmo_resultado(self):
        # A MESMA fonte, processada duas vezes, dá o MESMO resultado.
        r1 = processar_particao(self.dia, self.fonte, self.destino_a)
        r2 = processar_particao(self.dia, self.fonte, self.destino_b)
        self.assertEqual(self.destino_a.ler(), self.destino_b.ler())

    def test_reprocessar_nao_duplica(self):
        # Processar a mesma partição 3x → estado final único (idempotente).
        for _ in range(3):
            processar_particao(self.dia, self.fonte, self.destino)
        self.assertEqual(self.destino.contar(), self.linhas_esperadas)  # sem duplicar
Os testes provam as duas garantias: a mesma entrada gera o mesmo resultado, e reprocessar três vezes não duplica — o estado final é único.
Tabela de decisão Pipeline frágil vs pipeline reprodutível
AspectoNão-reprodutívelDeterminístico e idempotente
Reprocessar a fonteNúmeros diferentesMesmo resultado
Fonte de verdadeSó a última execuçãoReproduzível, confiável
Falha no meioDuplica ou corrompeReexecutar é seguro
BackfillCampo minadoRotina trivial
Depende deOrdem, momento, estadoSó da entrada

Estudo de Caso

Caso aplicado

O pipeline que se pode reprocessar sem medo

Contexto. A plataforma executa pipelines que ingerem e transformam grandes volumes de dados de fontes externas. Inicialmente focados em "funcionar uma vez", alguns produziam resultados que variavam ao serem reprocessados, e reexecutá-los após uma falha ameaçava duplicar dados — tornando o backfill perigoso e a análise pouco confiável.

Pipeline frágil
Números mudam, backfill arriscado
Reprodutível
Fonte de verdade, reprocessável
Ganho
Dados em que se confia

Decisão. Reconstruir os pipelines com determinismo e idempotência: processar em partições explícitas (por dia), depender apenas da entrada (não da ordem nem do momento), e gravar substituindo cada partição de forma idempotente (§6.2). Reprocessar uma partição passa a levar sempre ao mesmo estado.

Resultado. Os dados ganharam uma fonte de verdade: a mesma fonte produz sempre o mesmo resultado, então um número está certo porque é reproduzível. O backfill — reprocessar o histórico após uma mudança de lógica — tornou-se uma operação rotineira e segura, e a recuperação de uma falha no meio deixou de assustar: roda-se de novo, e a idempotência garante a integridade. A análise deixou de ser areia movediça e passou a repousar sobre dados confiáveis.

Incidente Real

Severidade · Alta

O reprocessamento que duplicou os dados

  • Antes Um pipeline sem gravação idempotente foi reexecutado após uma falha no meio do processamento, para completar o que faltava.
  • Sintoma As linhas que já haviam sido gravadas antes da falha foram gravadas novamente na reexecução, duplicando dados no destino e distorcendo as análises que dele dependiam.
  • Diagnóstico A ausência de idempotência fez o reprocessamento acumular em vez de substituir; sem partições idempotentes, reexecutar não era seguro.
  • Correção O pipeline passou a processar partições e a gravar substituindo cada uma de forma idempotente; reprocessar passou a levar sempre ao mesmo estado, sem duplicar.
  • Kaizen Vira regra: todo pipeline é determinístico e idempotente por partição; reprocessar e fazer backfill são operações seguras por construção.

Decisão Arquitetural

ADR-0056

Pipelines de dados determinísticos e idempotentes por partição

Aceito
Contexto

Um pipeline não-reprodutível dá resultados diferentes para a mesma entrada — sem fonte de verdade — e, sem idempotência, reprocessar após uma falha duplica ou corrompe dados. O backfill vira um campo minado e a análise repousa sobre areia movediça.

Decisão

Os pipelines são determinísticos (o resultado depende só da entrada, não da ordem, do momento ou de estado mutável) e idempotentes (§6.2): processam em partições explícitas e gravam substituindo cada partição, de modo que reexecutar leva sempre ao mesmo estado. Verificado por testes de reprodutibilidade e não-duplicação.

Alternativas consideradas

Foco em "funcionar uma vez" — irreprodutível, backfill perigoso. Gravação acumulativa sem idempotência — reprocessar duplica. Depender de ordem ou do relógio — resultado varia entre execuções.

Consequências
PositivasFonte de verdade reproduzível. Backfill trivial e seguro. Recuperação de falhas sem medo. Dados confiáveis para a análise.
NegativasExige projetar o pipeline em partições e tornar a gravação idempotente.
RiscosO particionamento precisa casar com o domínio dos dados; mitigado por escolher a granularidade certa (ex.: por dia) desde o início.

Antipadrões

Pipeline que depende de ordem ou do momento

O resultado muda conforme a ordem de processamento ou a hora em que roda. Correção: determinismo — o resultado depende apenas da entrada explícita.

Gravação que acumula em vez de substituir

Reprocessar adiciona dados em cima dos antigos, duplicando. Correção: gravar substituindo a partição, de forma idempotente (§6.2).

Exercício

Teste a reprodutibilidade do seu pipeline

  1. Pegue um pipeline de dados seu. Se você o reexecutasse sobre a mesma fonte de ontem, o resultado seria idêntico ao de antes, ou poderia diferir?
  2. Faça o teste do backfill: se a lógica de transformação mudasse, você conseguiria reprocessar o histórico inteiro com confiança, ou teme duplicar ou corromper dados?
  3. Faça o teste da falha: se o pipeline falhasse na metade, reexecutá-lo seria seguro, ou poderia gravar de novo o que já tinha sido gravado?
  4. Identifique as dependências acidentais: o resultado depende da ordem das linhas, do relógio, ou de algum estado externo? Cada uma é uma ameaça à reprodutibilidade.
Meta: avaliar se os seus pipelines são reprodutíveis — e planejar torná-los determinísticos e idempotentes por partição, para que reprocessar e fazer backfill deixem de ser arriscados.
✓ Checklist do tópico
  • O resultado do pipeline depende apenas da entrada, não da ordem, do momento ou de estado
  • O pipeline processa dados em partições explícitas e bem definidas
  • A gravação de cada partição é idempotente (substitui, não acumula)
  • Reexecutar o pipeline sobre a mesma fonte produz sempre o mesmo resultado
  • Backfill e recuperação de falhas são operações seguras por construção
Meta Dados · Engenharia de Plataformas · Capítulo XI · §11.1Próximo → 11.2 Ingestão em Massa
Capítulo XI · Data Engineering e Analytics
11.2

Ingestão em Massa

Ingerir milhões de linhas uma a uma é o caminho lento que trava tudo: cada linha é uma ida ao banco, e os milhões viram horas — às vezes dias. Mover dados em escala exige carregá-los em massa, sem competir com a operação ao vivo. A diferença é entre 36 minutos e três dias.

Pré-requisitos · 11.1 · Cap IV §4.6 OLTP/OLAP · §4.3 ÍndicesConecta com · 11.3 Separação · 11.4 QualidadeTempo de leitura · ~14 min

O Problema

Mover dados em escala — importar uma base inteira, ingerir milhões de registros de uma fonte externa, popular um repositório analítico — esbarra num problema de magnitude. A forma intuitiva de inserir dados num banco é uma linha de cada vez: para cada registro, executa-se um comando de inserção. Funciona perfeitamente para dezenas ou centenas de linhas. Mas quando são milhões, essa abordagem desmorona sob o próprio peso. Cada inserção individual é uma ida completa ao banco — com sua transação, seu overhead, sua latência de rede —, e multiplicar esse custo por milhões transforma o que deveria levar minutos em horas, às vezes dias.

E o custo não é só de tempo. Inserir milhões de linhas uma a uma trava o banco: a torrente de pequenas transações gera contenção, consome recursos e pode degradar — ou paralisar — a operação ao vivo que compartilha o mesmo banco. Há ainda um custo oculto e brutal: os índices (§4.3 Cap IV). Se a tabela de destino tem índices, cada uma dos milhões de inserções precisa, além de gravar a linha, atualizar todos os índices — um trabalho que, repetido milhões de vezes, multiplica o custo total. O resultado é um processo de ingestão tão lento e tão pesado que se torna impraticável: importações que levam dias, janelas de carga que invadem o horário de operação, bancos que engasgam sob a inserção. Em escala, a ingestão ingênua não é apenas lenta — é uma operação que não termina a tempo e que ameaça a estabilidade do sistema enquanto tenta.

A ingestão linha por linha que não termina

Inserir milhões de registros um a um transforma minutos em dias, trava o banco com a contenção de milhões de transações, e paga o custo de atualizar índices a cada linha. Correção: carregar em massa, em lotes, com os índices criados depois da carga.

A Decisão

A ingestão é em massa: os dados são carregados em lotes ou em bloco, com poucas operações de grande volume em vez de milhões de pequenas, os índices são criados após a carga, e o caminho de ingestão não compete com a operação ao vivo.

A plataforma move dados em escala com técnicas de ingestão em massa, que trocam milhões de operações pequenas por poucas operações grandes. Em vez de inserir uma linha por vez, os dados são agrupados em lotes e inseridos juntos, ou carregados em bloco por mecanismos de carga em massa que o banco oferece justamente para esse fim — capazes de ingerir grandes volumes com uma fração do overhead. A diferença de magnitude é dramática: o que levaria dias linha por linha leva minutos em massa. Uma decisão crucial é o tratamento dos índices: em vez de manter os índices durante a carga (pagando a atualização a cada inserção), criam-se os índices depois que os dados estão carregados — construir um índice uma vez sobre a tabela cheia é muito mais rápido do que atualizá-lo milhões de vezes durante a inserção. (E, uma vez criados, esses índices têm um efeito desproporcional na velocidade das consultas — um único índice no campo certo pode reduzir o tempo de uma consulta em mais de noventa por cento, como o Capítulo IV demonstrou.) Por fim, o caminho de ingestão é isolado da operação ao vivo: a carga em massa não compete pelos recursos do sistema transacional que serve os usuários, seja porque corre em janelas apropriadas, seja porque ocorre num repositório separado (a separação que o próximo tópico aprofunda). O resultado é que mover dados em escala deixa de ser uma operação heroica de dias e passa a ser rápida, previsível e segura — uma importação de centenas de milhões de registros completa-se na ordem de minutos, sem ameaçar a estabilidade do sistema. A magnitude deixa de ser um obstáculo intransponível e vira uma questão de usar a técnica certa.

Diagrama conceitual Carga em massa, não linha por linha
LINHA POR LINHAmilhões de idas ao bancolento, trava, horas (ou dias) EM MASSA (lotes)carga em bloco, poucas idasmilhões em minutos a ingestão NÃO compete com a operação ao vivo índices criados DEPOIS da carga, não durante
Figura 11.2-a. Inserir linha por linha (milhões de idas ao banco) é lento e trava tudo; a carga em massa em lotes faz milhões de linhas em minutos. Os índices são criados depois da carga, e a ingestão não compete com a operação ao vivo.

Carregar em lotes, indexar depois

ingestao_massa.pyPython
# INGESTÃO EM MASSA: lotes grandes, não uma linha por vez.
def ingerir_em_massa(fonte: Fonte, destino: Banco) -> None:
    # ERRADO: for linha in fonte: destino.inserir(linha)  ← milhões de idas

    # CERTO: agrupar em lotes e carregar em bloco.
    for lote in fonte.em_lotes(tamanho=50_000):
        destino.copiar_em_massa(lote)   # 1 operação, 50k linhas
    # Milhões de linhas em minutos, não dias.
Os dados são agrupados em lotes grandes e carregados em bloco — poucas operações de alto volume em vez de milhões de inserções individuais.
indices_depois.sqlPostgreSQL
-- ÍNDICES DEPOIS da carga, não durante — muito mais rápido.

-- 1) Tabela SEM índices (exceto o essencial). Carga em massa veloz.
COPY destino FROM 'dados.csv' WITH (FORMAT csv);

-- 2) AGORA, com os dados carregados, criar os índices de uma vez.
CREATE INDEX ON destino (campo_de_busca);
-- Construir o índice 1x sobre a tabela cheia >> atualizá-lo a cada linha.
-- (e o índice certo corta o tempo de consulta em >90% — Cap IV §4.3)
A carga em massa ocorre sem índices; eles são criados uma vez ao final. Construir o índice na tabela cheia é muito mais rápido que atualizá-lo por linha.

A Verificação

ingestao_test.pyPython
class TestIngestaoEmMassa(TestCase):
    def test_massa_e_muito_mais_rapida_que_linha_a_linha(self):
        self.gerar_linhas(1_000_000)

        t_linha = self.cronometrar(lambda: self.inserir_linha_a_linha())
        t_massa = self.cronometrar(lambda: self.ingerir_em_massa())

        # A carga em massa é ordens de magnitude mais rápida.
        self.assertLess(t_massa, t_linha / 10)

    def test_ingestao_nao_degrada_operacao_ao_vivo(self):
        # Durante a carga, a operação ao vivo mantém sua latência.
        with self.carga_em_massa_rodando():
            self.assertLatenciaOperacaoDentroDoSLO()  // não compete
Os testes medem o que importa: a carga em massa é ordens de magnitude mais rápida, e não degrada a latência da operação ao vivo enquanto roda.
Tabela de decisão Ingestão linha por linha vs em massa
AspectoLinha por linhaEm massa
Milhões de linhasHoras ou diasMinutos
Idas ao bancoMilhõesPoucas, em bloco
Índices durante a cargaAtualizados a cada linhaCriados 1x ao final
Operação ao vivoTrava por contençãoIsolada, não compete
Em escalaImpraticávelRápida e previsível

Estudo de Caso

Caso aplicado

Centenas de milhões de registros em minutos

Contexto. A plataforma precisava importar uma base pública de dados empresariais com centenas de milhões de registros para um repositório próprio. Feita linha por linha, a importação levaria dias, travaria o banco e seria impraticável de repetir.

Linha por linha
Dias, banco travado
Em massa
Minutos, sem afetar operação
Ganho
Escala que termina a tempo

Decisão. Usar ingestão em massa: carregar os dados em bloco com o mecanismo de carga em massa do banco, criar os índices apenas depois que os dados estivessem carregados, e isolar a carga da operação ao vivo. A transformação anterior (§11.1) garante que reprocessar seja seguro.

Resultado. A importação de centenas de milhões de registros — empresas, estabelecimentos, sócios e mais — completou-se na ordem de minutos, não de dias. Os índices, criados de uma vez sobre as tabelas já carregadas, deram às consultas subsequentes uma velocidade desproporcional (um índice no campo certo cortou o tempo de uma consulta em mais de noventa por cento). E como a carga foi isolada, a operação ao vivo não foi afetada. Mover dados em escala deixou de ser uma operação heroica e tornou-se rápida, repetível e segura.

Incidente Real

Severidade · Alta

A carga que travou o banco operacional

  • Antes Uma importação de grande volume foi executada inserindo linha por linha, no mesmo banco que servia a operação ao vivo.
  • Sintoma A torrente de milhões de pequenas transações gerou contenção e consumiu recursos, degradando a latência da operação ao vivo enquanto a carga, lentíssima, se arrastava por horas.
  • Diagnóstico A ingestão linha por linha, além de lenta, competia diretamente com a operação pelos recursos do banco, e o custo de atualizar índices a cada linha agravava tudo.
  • Correção A ingestão passou a ser em massa (lotes/carga em bloco), com índices criados após a carga e o processo isolado da operação ao vivo.
  • Kaizen Vira regra: ingestão de grande volume é sempre em massa, com índices criados ao final e o caminho isolado da operação transacional.

Decisão Arquitetural

ADR-0057

Ingestão em massa, com índices criados após a carga e isolada da operação

Aceito
Contexto

Inserir milhões de linhas uma a uma transforma minutos em dias, trava o banco com a contenção de milhões de transações, e paga o custo de atualizar índices a cada linha. Em escala, a ingestão linha por linha é impraticável e ameaça a estabilidade do sistema.

Decisão

A ingestão de grande volume é em massa: os dados são carregados em lotes ou em bloco (poucas operações de alto volume), os índices são criados depois da carga (construir 1x na tabela cheia em vez de atualizar por linha), e o caminho de ingestão é isolado da operação ao vivo. Reprocessar é seguro pela idempotência (§11.1).

Alternativas consideradas

Inserir linha por linha — dias, contenção, índices por linha. Carregar com índices ativos — paga atualização a cada inserção. Ingerir no banco operacional sem isolar — degrada a operação ao vivo.

Consequências
PositivasCentenas de milhões de registros em minutos. Banco não trava. Operação ao vivo preservada. Índices criados eficientemente, consultas rápidas (§4.3).
NegativasExige usar mecanismos de carga em massa e orquestrar a criação de índices.
RiscosCarregar sem índices exige cuidado com integridade durante a janela; mitigado por validar (§11.4) e indexar logo após a carga.

Antipadrões

Inserir grandes volumes linha por linha

Um comando de inserção por registro, multiplicado por milhões. Lento e trava o banco. Correção: carga em massa, em lotes ou em bloco.

Carregar em massa com os índices ativos

Manter os índices durante a carga, pagando a atualização a cada linha. Correção: criar os índices depois que os dados estão carregados.

Exercício

Acelere uma ingestão em escala

  1. Pegue uma ingestão de grande volume do seu sistema. Como ela insere os dados — uma linha por vez, ou em lotes/bloco? Quanto tempo leva?
  2. Faça a conta da magnitude: se você inserir um milhão de linhas uma a uma e cada inserção custar poucos milissegundos, quanto tempo isso soma? Compare com uma carga em massa.
  3. Verifique os índices: a tabela de destino tem índices ativos durante a carga? Eles estão sendo atualizados a cada inserção? Criá-los depois seria mais rápido?
  4. Faça o teste do isolamento: a sua ingestão de grande volume roda no mesmo banco que serve a operação ao vivo? Ela degrada essa operação enquanto roda?
Meta: transformar uma ingestão lenta linha por linha em uma carga em massa, com índices criados ao final e isolada da operação — para que mover dados em escala termine em minutos, não em dias.
✓ Checklist do tópico
  • A ingestão de grande volume é em massa (lotes ou carga em bloco), não linha por linha
  • Poucas operações de alto volume substituem milhões de operações pequenas
  • Os índices são criados depois da carga, não atualizados a cada inserção
  • O caminho de ingestão é isolado da operação ao vivo, sem competir por recursos
  • A ingestão é reprocessável com segurança (idempotência, §11.1)
Meta Dados · Engenharia de Plataformas · Capítulo XI · §11.2Próximo → 11.3 Operação e Análise Separadas
Capítulo XI · Data Engineering e Analytics
11.3

Operação e Análise Separadas

O Capítulo IV separou registrar de analisar. Aqui essa separação é levada ao trabalho de dados: o sistema que serve transações ao vivo e o que responde análises pesadas têm necessidades opostas. Forçar os dois no mesmo banco faz a análise sufocar a operação.

Pré-requisitos · Cap IV §4.6 OLTP/OLAP · 11.1 · 11.2Conecta com · 11.4 Qualidade · 11.5 SemânticaTempo de leitura · ~14 min

O Problema

O Capítulo IV (§4.6) estabeleceu um princípio fundamental: registrar e analisar são trabalhos diferentes, com necessidades opostas, e por isso pertencem a sistemas separados — o transacional (OLTP) e o analítico (OLAP). Este tópico leva esse princípio ao terreno da engenharia de dados, onde a tentação de violá-lo é mais forte e o custo de violá-lo, mais brutal. A tentação é esta: os dados já estão no banco operacional, então por que não rodar as análises ali mesmo? Por que copiar para outro lugar se a informação que o relatório precisa já está na base que serve a aplicação?

A resposta é que os dois trabalhos têm exigências que se contradizem. O sistema operacional precisa de transações rápidas sobre poucas linhas (buscar este pedido, atualizar aquele saldo), com o dado atual, num formato normalizado, e — crucialmente — não pode travar, porque serve os usuários ao vivo. O sistema analítico precisa do oposto: consultas que varrem milhões de linhas (somar todas as vendas do ano, cruzar anos de histórico), sobre dados históricos e frequentemente num formato desnormalizado ou colunar otimizado para varredura. Quando se força a análise no banco operacional, a colisão é inevitável e dolorosa: uma única consulta analítica pesada — varrendo milhões de linhas, fazendo agregações enormes — consome os recursos do banco e degrada ou paralisa as transações ao vivo. O relatório que alguém pediu trava o sistema que os clientes estão usando. A análise sufoca a operação. Pior, otimizar o banco para um trabalho prejudica o outro: os índices que aceleram as transações pesam na ingestão analítica; o formato bom para análise é ruim para transações. Tentar servir os dois trabalhos opostos no mesmo sistema é condenar ambos a um desempenho medíocre, e arriscar que o trabalho pesado derrube o trabalho crítico.

A análise que derruba a operação

Uma consulta analítica pesada no banco operacional varre milhões de linhas e consome recursos, degradando ou paralisando as transações ao vivo. Os dois trabalhos têm necessidades opostas. Correção: separar o sistema que registra do que analisa, com os dados fluindo por um caminho controlado.

A Decisão

A operação e a análise vivem em sistemas separados, cada um otimizado para o seu trabalho oposto. Os dados fluem do operacional para o analítico por um caminho controlado (§11.1, §11.2), de modo que a análise pesada nunca compete com as transações ao vivo.

A plataforma mantém a separação entre operação e análise como uma fronteira firme, agora com o aparato de engenharia de dados que a torna prática. O sistema operacional é otimizado para o seu trabalho: transações rápidas sobre poucas linhas, dado atual, formato normalizado, latência baixa para servir os usuários — e protegido de qualquer carga pesada que possa degradá-lo. O sistema analítico é otimizado para o seu trabalho: consultas que varrem grandes volumes, dados históricos acumulados, formato colunar ou desnormalizado que torna a varredura eficiente. Cada um faz bem aquilo para que foi projetado, sem o compromisso impossível de servir os dois. E o elo entre eles é um caminho controlado de dados: a informação flui do operacional para o analítico através de pipelines (§11.1, reprodutíveis) que ingerem em massa (§11.2, sem sobrecarregar), de forma que o sistema analítico tem os dados de que precisa sem nunca ler diretamente do banco operacional ao vivo. Assim, uma análise pesada roda no sistema analítico, varrendo milhões de linhas à vontade, sem tocar — e sem ameaçar — as transações que os usuários estão fazendo no sistema operacional. A consulta que somaria o ano inteiro não trava mais o pedido que o cliente está fazendo agora, porque rodam em sistemas diferentes. É a separação de responsabilidades (um tema que percorre todo o livro) aplicada na maior das escalas: o sistema que registra a verdade do agora e o sistema que analisa a verdade do tempo são coisas distintas, cada uma livre para ser excelente no seu trabalho.

Arquitetura lógica Operação e análise em sistemas separados
OPERAÇÃO (registra)transações rápidas, dado atualnormalizado · serve usuáriosnão pode travar ANÁLISE (analisa)consultas pesadas, históricocolunar · serve relatóriospode varrer tudo cópia controlada consulta pesada no banco operacional = operação trava cada um otimizado para o seu trabalho
Figura 11.3-a. A operação (transações rápidas, dado atual) e a análise (consultas pesadas, histórico) têm necessidades opostas e vivem separadas. Os dados fluem por um caminho controlado — a análise pesada nunca trava a operação ao vivo.

O caminho controlado entre os dois sistemas

separacao.pyPython
# A análise NÃO lê do banco operacional. Os dados FLUEM para o analítico.
def sincronizar_para_analise(operacional: Banco, analitico: Banco) -> None:
    # Pipeline reprodutível (§11.1) + ingestão em massa (§11.2).
    for particao in particoes_novas(operacional):
        dados = ler_em_massa(operacional, particao)   # sem travar o OLTP
        # Grava no analítico em formato otimizado para VARREDURA.
        analitico.carregar_colunar(particao, dados)
    # O analítico fica com o histórico; o operacional, leve e atual.

# Análise pesada → roda no ANALÍTICO, nunca no operacional ao vivo.
Os dados fluem do operacional para o analítico por um caminho controlado. A análise lê do sistema analítico — nunca do banco que serve os usuários.
onde_roda.sqlSQL
-- OPERACIONAL: transação rápida, poucas linhas, dado atual.
-- (roda no OLTP — precisa ser instantânea para o usuário)
SELECT * FROM pedido WHERE id = 12345;          -- 1 linha, <1ms

-- ANALÍTICO: varre milhões, histórico, agregação enorme.
-- (roda no OLAP — varreria o banco operacional se fosse lá: TRAVA)
SELECT mes, SUM(valor)
FROM vendas_historicas                          -- milhões de linhas
WHERE ano BETWEEN 2020 AND 2026
GROUP BY mes;                                   -- no OLTP, derrubaria a operação
A mesma distinção do Cap IV, na prática: a transação instantânea roda no operacional; a varredura de milhões de linhas, no analítico, onde não ameaça ninguém.

A Verificação

separacao_test.pyPython
class TestOperacaoAnaliseSeparadas(TestCase):
    def test_analise_pesada_nao_afeta_operacao(self):
        # Uma consulta analítica pesada roda no analítico...
        with self.consulta_pesada_no_analitico():
            # ...e a operação ao vivo mantém sua latência (não compete).
            latencia = self.medir_transacao_operacional()
            self.assertLess(latencia, self.slo_operacional)

    def test_analitico_tem_o_historico(self):
        # O analítico acumula histórico que o operacional não guarda.
        self.assertTrue(self.analitico.tem_anos_de_historico())
O teste-chave prova o isolamento: uma análise pesada no sistema analítico não degrada a latência da operação ao vivo — porque rodam em sistemas separados.
Tabela de decisão Operação vs análise: necessidades opostas
DimensãoOperação (OLTP)Análise (OLAP)
Consulta típicaPoucas linhas, rápidaVarre milhões
DadosAtualHistórico
FormatoNormalizadoColunar / desnormalizado
PrioridadeLatência (servir usuário)Vazão (varrer volume)
Análise pesada aquiTrava a operaçãoRoda livre

Estudo de Caso

Caso aplicado

A análise que parou de derrubar a operação

Contexto. Relatórios e análises eram executados diretamente no banco operacional da plataforma, porque "os dados já estavam lá". Consultas analíticas pesadas — varrendo grandes volumes e cruzando histórico — competiam pelos recursos do banco com as transações ao vivo dos usuários.

Análise no operacional
Pesada derruba o ao vivo
Sistemas separados
Cada um livre e otimizado
Ganho
Operação estável, análise plena

Decisão. Manter firme a separação do Capítulo IV: um sistema operacional otimizado para transações e um sistema analítico otimizado para varredura, com os dados fluindo do primeiro para o segundo por um caminho controlado — pipelines reprodutíveis (§11.1) com ingestão em massa (§11.2). A análise passa a ler apenas do sistema analítico.

Resultado. As análises pesadas, agora rodando no sistema analítico, deixaram de tocar o banco operacional — e portanto deixaram de degradar as transações ao vivo. Um relatório que soma anos de histórico não trava mais o pedido que um cliente está fazendo agora, porque rodam em sistemas distintos. Cada sistema, livre do compromisso impossível de servir os dois trabalhos opostos, pôde ser otimizado para o seu: a operação ficou rápida e estável, e a análise, capaz de varrer à vontade.

Incidente Real

Severidade · Alta

O relatório que travou a operação ao vivo

  • Antes Uma consulta analítica pesada, que varria um grande volume de dados históricos, foi executada diretamente no banco operacional, durante o horário de uso.
  • Sintoma A consulta consumiu os recursos do banco e degradou severamente a latência das transações ao vivo — os usuários sentiram o sistema travar enquanto o relatório rodava.
  • Diagnóstico A análise pesada competia diretamente com a operação pelos recursos do mesmo banco; os dois trabalhos opostos não cabiam no mesmo sistema.
  • Correção A análise foi movida para um sistema analítico separado, alimentado por um caminho controlado de dados; o banco operacional deixou de receber consultas pesadas.
  • Kaizen Vira regra: nenhuma análise pesada roda no banco operacional; operação e análise vivem em sistemas separados, cada um otimizado para o seu trabalho.

Decisão Arquitetural

ADR-0058

Operação e análise em sistemas separados, com caminho controlado de dados

Aceito
Contexto

Operação e análise têm necessidades opostas: o operacional precisa de transações rápidas sobre poucas linhas sem travar; o analítico, de consultas que varrem milhões. Forçá-los no mesmo banco faz a análise pesada degradar ou paralisar as transações ao vivo, e otimizar para um prejudica o outro.

Decisão

A operação (OLTP, transações rápidas, dado atual, normalizado) e a análise (OLAP, varredura, histórico, colunar) vivem em sistemas separados, cada um otimizado para o seu trabalho. Os dados fluem do operacional para o analítico por um caminho controlado — pipelines reprodutíveis (§11.1) com ingestão em massa (§11.2). A análise nunca lê do banco operacional ao vivo.

Alternativas consideradas

Análise no banco operacional — derruba a operação. Réplica de leitura do OLTP para análise — alivia, mas mantém o formato e o histórico inadequados. Um banco "para tudo" — medíocre nos dois trabalhos.

Consequências
PositivasAnálise pesada nunca trava a operação. Cada sistema otimizado para o seu trabalho. Operação estável e rápida; análise capaz de varrer o histórico inteiro.
NegativasExige manter dois sistemas e o caminho de dados entre eles.
RiscosOs dados analíticos têm latência em relação ao operacional; aceitável — a análise tolera a defasagem que a transação não toleraria.

Antipadrões

Rodar análise pesada no banco operacional

Executar consultas que varrem milhões de linhas no banco que serve os usuários ao vivo. Trava a operação. Correção: a análise roda num sistema analítico separado.

Um único banco otimizado "para tudo"

Tentar servir transações e análise no mesmo sistema com o mesmo formato. Correção: sistemas separados, cada um otimizado para o seu trabalho oposto.

Exercício

Separe operação de análise no seu sistema

  1. No seu sistema, onde rodam os relatórios e análises pesadas? No mesmo banco que serve as transações ao vivo, ou num sistema separado?
  2. Faça o teste da colisão: já aconteceu de um relatório ou consulta pesada degradar o desempenho da operação ao vivo? Esse é o sintoma da falta de separação.
  3. Liste as necessidades opostas: as suas transações precisam de baixa latência sobre poucas linhas; as suas análises precisam varrer grandes volumes. O mesmo banco consegue servir bem aos dois?
  4. Esboce o caminho controlado: como os dados fluiriam da operação para um sistema analítico separado, sem que a análise precise ler do banco ao vivo?
Meta: avaliar se a operação e a análise compartilham indevidamente o mesmo banco no seu sistema — e planejar a separação, para que a análise pesada nunca mais ameace as transações ao vivo.
✓ Checklist do tópico
  • A operação (transações) e a análise (varredura) vivem em sistemas separados
  • Cada sistema é otimizado para o seu trabalho oposto (latência vs vazão)
  • A análise pesada nunca roda no banco operacional ao vivo
  • Os dados fluem da operação para a análise por um caminho controlado (§11.1, §11.2)
  • Uma consulta analítica pesada não degrada a latência das transações ao vivo
Meta Dados · Engenharia de Plataformas · Capítulo XI · §11.3Próximo → 11.4 Qualidade de Dados como Contrato
Capítulo XI · Data Engineering e Analytics
11.4

Qualidade de Dados como Contrato

Lixo que entra é lixo que sai — mas pior, porque flui silenciosamente para baixo e contamina toda análise antes que alguém perceba. A qualidade não pode ser um conserto a jusante; precisa ser um contrato na porta, onde o dado ruim para antes de entrar.

Pré-requisitos · 11.1 · 11.2 · Cap VI §6.1 ACL · Cap IV ContratoConecta com · 11.5 Semântica · Cap VIIITempo de leitura · ~14 min

O Problema

"Lixo que entra é lixo que sai" — o velho aforismo da computação — é ainda mais cruel num pipeline de dados, porque ali o lixo não sai imediatamente: ele flui. Um dado ruim que entra na fonte — um valor fora da faixa esperada, um campo obrigatório vazio, um registro que referencia algo inexistente, um formato corrompido — não causa um erro visível na entrada. Ele segue adiante, silenciosamente, pelas transformações (§11.1), pela ingestão (§11.2), até o repositório analítico, e dali para os relatórios, os dashboards e as decisões. O dado ruim se espalha antes de se revelar.

E quando ele se revela, o estrago já está feito e é difícil de desfazer. O sintoma aparece longe da causa: um número errado num relatório, uma soma que não bate, uma análise que leva a uma conclusão equivocada. Descobrir que a causa foi um dado ruim que entrou semanas atrás — e que desde então contaminou tudo o que dele derivou — é uma investigação dolorosa, e corrigir exige rastrear o dado ruim por todos os lugares para onde ele fluiu e refazer tudo o que ele afetou. A tentação natural é consertar a jusante: ajustar o relatório, corrigir o número no dashboard, remendar onde o erro apareceu. Mas isso é tratar o sintoma, não a doença — o dado ruim continua na fonte e nos lugares intermediários, pronto para reaparecer no próximo relatório. Tratar a qualidade de dados como um conserto a jusante é uma batalha perdida: o lixo já entrou, já se espalhou, e cada conserto pontual deixa a fonte do problema intacta. A análise inteira repousa sobre dados que ninguém pode garantir, e a confiança nos números — o ativo mais precioso de qualquer sistema analítico — evapora.

O lixo que se espalha antes de aparecer

Sem validação na entrada, um dado ruim flui silenciosamente pelo pipeline e contamina toda a análise — e só se revela num relatório errado, longe da causa, depois de já ter se espalhado. Correção: a qualidade é um contrato na porta; o dado ruim para antes de entrar.

A Decisão

A qualidade de dados é um contrato verificado na porta de entrada: cada dado é validado contra expectativas explícitas (esquema, faixas, obrigatoriedade, integridade) antes de entrar. O que passa é confiável; o que falha é barrado e isolado, nunca contamina o que está abaixo.

A plataforma trata a qualidade de dados como um contrato verificado na fronteira de entrada — exatamente o espírito da camada anticorrupção (§6.1 Cap VI) e do contrato canônico (Cap IV), aplicado aos dados que chegam. Na porta do pipeline, antes que qualquer dado entre e flua adiante, ele é validado contra um conjunto de expectativas explícitas: o esquema está correto (os campos certos, nos tipos certos)? Os valores estão dentro das faixas esperadas (uma idade não é negativa, uma data não está no futuro impossível)? Os campos obrigatórios estão preenchidos? As referências apontam para algo que existe (integridade)? Esse contrato de qualidade é a porta: o dado que satisfaz todas as expectativas entra, e tudo o que está abaixo dele no pipeline pode confiar que recebeu dados válidos. O dado que falha em qualquer expectativa é barrado na entrada — não flui adiante, não contamina as transformações, não chega aos relatórios. Em vez de seguir silenciosamente, ele é isolado (separado num lugar onde possa ser inspecionado e tratado) e sinalizado (a observabilidade do Capítulo VIII registra que dados foram rejeitados, com um alerta se o volume de rejeições for anômalo). A consequência é uma inversão profunda: em vez de descobrir o lixo num relatório errado semanas depois e caçá-lo espalhado por todo o sistema, o lixo é detectado e contido na entrada, onde o custo de tratá-lo é mínimo e onde ele ainda não contaminou nada. O dado ruim para na porta. E porque só dados que passaram pelo contrato entram, tudo o que está a jusante — as análises, os relatórios, as decisões — repousa sobre dados confiáveis por construção. A qualidade deixa de ser uma esperança e um conserto perpétuo, e torna-se uma garantia verificada na fronteira.

Fluxograma operacional O contrato de qualidade na porta de entrada
DADOS DA FONTEbons e ruins misturados A PORTAcontrato dequalidade valida DADOS CONFIÁVEISsó o que passou entra o ruim é barrado e isolado sem porta: lixo entra econtamina tudo a jusante
Figura 11.4-a. Na porta, um contrato de qualidade valida cada dado (esquema, faixas, obrigatoriedade, integridade). Só o que passa entra; o ruim é barrado e isolado — nunca flui adiante para contaminar a análise.

O contrato verificado na entrada

contrato_qualidade.pyPython
# A QUALIDADE é um contrato na PORTA. O ruim para antes de entrar.
def validar_na_porta(lote: list[Registro]) -> tuple[list, list]:
    aprovados, rejeitados = [], []
    for r in lote:
        # Expectativas EXPLÍCITAS — o contrato de qualidade.
        if (r.tem_esquema_valido()           # campos e tipos certos
            and r.valores_nas_faixas()         # nada fora do esperado
            and r.obrigatorios_preenchidos()   # nada faltando
            and r.referencias_existem()):      # integridade
            aprovados.append(r)
        else:
            rejeitados.append(r)   # BARRADO — não flui adiante
    return aprovados, rejeitados
O contrato valida cada registro contra expectativas explícitas. Só os aprovados seguem; os rejeitados são separados na entrada, antes de contaminar qualquer coisa.
isolar_e_sinalizar.pyPython
# O dado ruim é ISOLADO e SINALIZADO — não some, não flui.
def ingerir_com_qualidade(lote, destino, observabilidade) -> None:
    aprovados, rejeitados = validar_na_porta(lote)

    # Só o que passou no contrato entra — a jusante CONFIA nos dados.
    destino.carregar_em_massa(aprovados)   # §11.2

    if rejeitados:
        # Isolar para inspeção (não descartar silenciosamente).
        quarentena.guardar(rejeitados)
        # Sinalizar (Cap VIII): alerta se a rejeição for anômala.
        observabilidade.metrica("dados.rejeitados", len(rejeitados))
Os dados aprovados entram; os rejeitados vão para quarentena (inspecionáveis) e a observabilidade (Cap VIII) os registra — um pico de rejeições dispara alerta.

A Verificação

qualidade_test.pyPython
class TestQualidadeNaPorta(TestCase):
    def test_dado_ruim_e_barrado_nao_flui(self):
        lote = [self.registro_valido(), self.registro_com_campo_obrigatorio_vazio()]

        ingerir_com_qualidade(lote, self.destino, self.obs)

        # Só o válido entrou; o ruim NÃO chegou ao destino.
        self.assertEqual(self.destino.contar(), 1)
        self.assertEqual(self.quarentena.contar(), 1)  // isolado

    def test_a_jusante_recebe_so_dados_validos(self):
        # Tudo o que passou da porta satisfaz o contrato — confiável.
        for r in self.destino.tudo():
            self.assertTrue(r.satisfaz_o_contrato())
Os testes garantem o cerne: o dado ruim é barrado e isolado, não chega ao destino, e tudo o que está a jusante satisfaz o contrato — confiável por construção.
Tabela de decisão Conserto a jusante vs contrato na porta
AspectoQualidade como conserto a jusanteContrato na porta
Dado ruimFlui e se espalhaBarrado na entrada
Quando se descobreRelatório errado, tardeNa porta, na hora
Custo de corrigirCaçar espalhado, refazerIsolar na entrada
A jusantePode estar contaminadoConfiável por construção
Confiança nos númerosErodidaGarantida

Estudo de Caso

Caso aplicado

O dado ruim que parou na porta

Contexto. A plataforma ingere dados de fontes externas cuja qualidade varia. Sem validação na entrada, registros ruins — valores fora de faixa, campos obrigatórios vazios, referências inválidas — fluíam pelo pipeline e só se revelavam mais tarde, num relatório com números que não batiam, depois de já terem contaminado análises.

Conserto a jusante
Lixo espalhado, confiança erodida
Contrato na porta
Lixo barrado, dados confiáveis
Ganho
Confiança nos números

Decisão. Estabelecer a qualidade como um contrato na porta de entrada: cada lote é validado contra expectativas explícitas (esquema, faixas, obrigatoriedade, integridade) antes de entrar. O que passa segue; o que falha é barrado, isolado em quarentena e sinalizado pela observabilidade (Cap VIII).

Resultado. O dado ruim deixou de fluir: agora ele para na porta, é isolado para inspeção e dispara alerta se as rejeições forem anômalas. Tudo o que entra satisfaz o contrato, então as análises, os relatórios e as decisões a jusante repousam sobre dados confiáveis por construção. O custo de tratar um problema de qualidade caiu de "caçar o lixo espalhado por todo o sistema e refazer tudo" para "inspecionar o que ficou na quarentena". A confiança nos números — antes erodida por erros que apareciam longe da causa — foi restaurada.

Incidente Real

Severidade · Alta

O número errado no relatório que veio de um dado ruim

  • Antes Sem validação na entrada, um conjunto de registros com valores inconsistentes foi ingerido e fluiu pelo pipeline até as análises.
  • Sintoma Um relatório apresentou números que não batiam; a investigação revelou que a causa era um dado ruim ingerido tempos antes, que desde então contaminara tudo o que dele derivava — longe, no tempo e no espaço, da sua origem.
  • Diagnóstico A ausência de um contrato de qualidade na entrada deixou o dado ruim fluir silenciosamente e se espalhar antes de se revelar, tornando a correção custosa.
  • Correção Instituição de um contrato de qualidade na porta: dados são validados na entrada, e o que falha é barrado e isolado antes de contaminar o pipeline.
  • Kaizen Vira regra: a qualidade de dados é um contrato verificado na entrada; o dado ruim para na porta, é isolado e sinalizado, nunca flui para a análise.

Decisão Arquitetural

ADR-0059

Qualidade de dados como contrato verificado na porta de entrada

Aceito
Contexto

Lixo que entra num pipeline flui silenciosamente e contamina toda a análise antes de se revelar — num relatório errado, longe da causa. Tratar a qualidade como conserto a jusante é uma batalha perdida: o dado ruim já se espalhou, e cada conserto pontual deixa a fonte intacta.

Decisão

A qualidade é um contrato verificado na fronteira de entrada (no espírito da ACL §6.1 e do contrato canônico do Cap IV): cada dado é validado contra expectativas explícitas (esquema, faixas, obrigatoriedade, integridade) antes de entrar. O que passa é confiável; o que falha é barrado, isolado em quarentena e sinalizado (Cap VIII). O dado ruim nunca flui adiante.

Alternativas consideradas

Conserto a jusante — trata o sintoma, deixa a fonte; lixo reaparece. Descartar o ruim silenciosamente — perde o rastro e o alerta. Confiar na fonte — a qualidade da fonte externa varia e não se controla.

Consequências
PositivasDado ruim barrado na entrada, não contamina nada. Problemas detectados na hora e no lugar certos. A jusante confiável por construção. Confiança nos números restaurada.
NegativasExige definir e manter as expectativas de qualidade e a quarentena.
RiscosExpectativas rígidas demais barram dados legítimos; mitigado por calibrar o contrato e inspecionar a quarentena para ajustá-lo.

Antipadrões

Consertar a qualidade a jusante

Remendar o relatório onde o erro apareceu, deixando o dado ruim na fonte e nos intermediários. Correção: validar na porta; o dado ruim para antes de entrar.

Descartar o dado ruim silenciosamente

Jogar fora o que falha sem registrar. Perde-se o rastro e o sinal de um problema na fonte. Correção: isolar em quarentena e sinalizar (Cap VIII).

Exercício

Ponha um contrato na porta dos seus dados

  1. Pegue um pipeline de ingestão seu. O que acontece se um dado ruim (valor fora de faixa, campo vazio, referência inválida) chega da fonte? Ele é barrado, ou flui adiante?
  2. Faça o teste do espalhamento: se um dado ruim entrasse hoje, quão longe ele fluiria antes de alguém perceber? Apareceria onde — num relatório, semanas depois?
  3. Liste as expectativas: que regras explícitas (esquema, faixas, obrigatoriedade, integridade) todo dado deveria satisfazer para entrar? Esse é o seu contrato de qualidade.
  4. Esboce a porta: onde, no pipeline, esse contrato seria verificado, e para onde iria o dado que falhasse (quarentena) e como você seria avisado (alerta)?
Meta: instituir um contrato de qualidade na entrada dos seus dados — para que o lixo pare na porta, seja isolado e sinalizado, e tudo a jusante repouse sobre dados confiáveis.
✓ Checklist do tópico
  • A qualidade é validada na porta de entrada, contra expectativas explícitas
  • As expectativas cobrem esquema, faixas, obrigatoriedade e integridade
  • O dado que falha é barrado na entrada — nunca flui adiante
  • O dado rejeitado é isolado (quarentena) e sinalizado (Cap VIII), não descartado em silêncio
  • Tudo a jusante recebe apenas dados que satisfazem o contrato (confiável por construção)
Meta Dados · Engenharia de Plataformas · Capítulo XI · §11.4Próximo → 11.5 A Camada Semântica
Capítulo XI · Data Engineering e Analytics
11.5

A Camada Semântica

Quando a "receita" é calculada de um jeito num relatório e de outro num dashboard, os números não batem — e ninguém sabe qual está certo. A confiança nos dados morre não por dados errados, mas por definições divergentes. Uma métrica precisa de uma definição, única, viva como dado.

Pré-requisitos · 11.4 · Cap III Metadado · Cap III §3.4 BREConecta com · encerra o Cap XITempo de leitura · ~13 min

O Problema

Um sistema analítico pode ter pipelines reprodutíveis (§11.1), ingestão eficiente (§11.2), separação correta (§11.3) e qualidade garantida na porta (§11.4) — e ainda assim falhar no que mais importa: produzir números em que se possa confiar. A falha mora num lugar sutil: a definição das métricas. O que é "receita"? Inclui ou exclui impostos? Considera vendas canceladas? Em qual momento a receita é reconhecida? Cada uma dessas escolhas é uma definição, e quando essa definição não é única, o sistema produz contradições.

O problema aparece quando a mesma métrica é calculada em lugares diferentes, de formas diferentes. Um relatório calcula a "receita" de um jeito; um dashboard, construído por outra pessoa em outro momento, calcula de outro; uma análise pontual, de um terceiro. Cada um reimplementa a lógica da métrica à sua maneira, com pequenas diferenças nas escolhas — um inclui impostos, outro não; um considera cancelamentos, outro esquece. O resultado é devastador para a confiança: a mesma "receita" aparece com valores diferentes em lugares diferentes, e ninguém sabe qual está certo. Numa reunião, dois relatórios apresentam números que não batem para a mesma coisa, e a discussão deixa de ser sobre a decisão e passa a ser sobre de quem é o número certo. A confiança nos dados — que é o único motivo de existir de um sistema analítico — desaba, não porque os dados estejam errados, mas porque as definições divergem. E o pior é que esse problema se multiplica: cada novo relatório é uma nova oportunidade de reimplementar a métrica de um jeito ligeiramente diferente, e cada divergência mina um pouco mais a credibilidade de todo o sistema. Quando os números não batem, as pessoas param de confiar em todos eles — e um sistema analítico em que ninguém confia é pior que inútil, porque dá uma falsa sensação de fundamento à decisão.

A métrica que dá valores diferentes

Quando cada relatório reimplementa a "receita" à sua maneira, a mesma métrica aparece com valores divergentes — e ninguém sabe qual é o certo. A confiança nos dados desaba por definições divergentes, não por dados errados. Correção: uma métrica, uma definição, viva como dado central.

A Decisão

Cada métrica tem uma única definição, declarada uma vez numa camada semântica central, como dado — não reimplementada em cada relatório. "Receita" significa exatamente o mesmo em todo lugar porque todo lugar consulta a mesma definição. Muda-se uma vez, vale em todos.

A plataforma resolve isso com uma camada semântica: um lugar central onde cada métrica é definida uma única vez, e de onde todos os relatórios, dashboards e análises obtêm a métrica. Quando se define "receita" — com todas as suas escolhas explícitas (inclui isto, exclui aquilo, reconhece naquele momento) —, essa definição vive na camada semântica, e qualquer lugar que precise da receita a consulta dali, em vez de recalculá-la. A consequência é direta e poderosa: "receita" significa exatamente o mesmo em todo lugar, porque todo lugar usa a mesma definição. Os números batem por construção — o relatório, o dashboard e a análise mostram o mesmo valor para a mesma métrica, porque é literalmente o mesmo cálculo, definido num único lugar. E essa camada semântica é a aplicação direta da tese central do livro (Cap III): a definição da métrica é dado, não código reimplementado. Assim como o metadado descreve o comportamento que o engine executa, a definição da métrica descreve o cálculo que a camada semântica realiza — e mudá-la é mudar um dado, num lugar, com o efeito propagando-se automaticamente para todos os relatórios. Se a definição de "receita" precisa mudar (passa a excluir um tipo de transação, digamos), altera-se a definição central, e todos os relatórios refletem a mudança de imediato e de forma consistente — em vez de caçar e reimplementar a mudança em dezenas de lugares, com o risco de esquecer alguns e recriar a divergência. A camada semântica transforma a confiança nos números de uma esperança frágil numa garantia estrutural: os números batem porque não podem divergir, já que vêm todos da mesma fonte de definição. E com a confiança restaurada, a discussão volta a ser sobre a decisão, não sobre de quem é o número certo.

Diagrama conceitual Uma definição central, números que batem em todo lugar
"RECEITA" = 1 definiçãodefinida UMA vez, como dado Relatório Amesmo número Dashboard Bmesmo número Análise Cmesmo número sem camada: cada um recalcula → números que não batem
Figura 11.5-a. A métrica "receita" é definida uma única vez, como dado, na camada semântica. Todo relatório, dashboard e análise a consulta dali — então o número bate em todo lugar. Sem a camada, cada um recalcula e os números divergem.

A métrica definida uma vez, como dado

camada_semantica.pyPython
# A métrica é DEFINIDA UMA VEZ, como DADO (Cap III), na camada semântica.
METRICAS = {
    "receita": {
        "descricao": "Receita reconhecida, líquida de cancelamentos",
        "calculo": "SUM(valor) WHERE status = 'confirmado'",
        "inclui_impostos": False,        # escolhas EXPLÍCITAS
        "exclui": ["cancelado", "estornado"],
    },
    # ... cada métrica, UMA definição central.
}

# Mudar a definição = mudar AQUI, uma vez. Vale em todo lugar.
Cada métrica é uma entrada de dado na camada semântica, com suas escolhas explícitas. A definição vive num único lugar — mudá-la é mudar um dado.
todos_consultam.pyPython
# TODO relatório/dashboard CONSULTA a definição — não recalcula.
def obter_metrica(nome: str, recorte: Recorte) -> Valor:
    definicao = METRICAS[nome]            # a ÚNICA definição
    return executar(definicao["calculo"], recorte)

# Relatório A, Dashboard B, Análise C — todos chamam isto:
receita_a = obter_metrica("receita", recorte_mes)
receita_b = obter_metrica("receita", recorte_mes)
# receita_a == receita_b SEMPRE — mesma definição, mesmo número.
# Os números BATEM por construção; não PODEM divergir.
Todo consumidor obtém a métrica da camada semântica em vez de recalcular. Como a definição é única, o número é o mesmo em todo lugar — por construção.

A Verificação

semantica_test.pyPython
class TestCamadaSemantica(TestCase):
    def test_mesma_metrica_mesmo_valor_em_todo_lugar(self):
        # A "receita" obtida por consumidores diferentes é IDÊNTICA.
        do_relatorio = obter_metrica("receita", self.recorte)
        do_dashboard = obter_metrica("receita", self.recorte)
        self.assertEqual(do_relatorio, do_dashboard)  // batem sempre

    def test_mudar_a_definicao_propaga_a_todos(self):
        antes = obter_metrica("receita", self.recorte)
        # Muda a definição central UMA vez...
        METRICAS["receita"]["exclui"].append("devolvido")
        // ...e TODOS os consumidores refletem a mudança.
        self.assertNotEqual(antes, obter_metrica("receita", self.recorte))
Os testes provam o essencial: a mesma métrica dá o mesmo valor para consumidores diferentes, e mudar a definição central propaga a mudança a todos de uma vez.
Tabela de decisão Métrica reimplementada vs camada semântica
AspectoCada relatório recalculaCamada semântica
Definição da métricaReimplementada em cada lugarÚnica, central, como dado
Mesma métrica, lugares diferentesValores divergemMesmo valor (por construção)
Mudar a definiçãoCaçar e refazer em N lugaresMudar 1 dado, propaga a todos
Reunião com 2 relatórios"Qual número é o certo?"Batem; foco na decisão
Confiança nos dadosDesabaGarantia estrutural

Estudo de Caso

Caso aplicado

Quando os números passaram a bater

Contexto. Na plataforma, métricas importantes eram calculadas de forma independente em cada relatório, dashboard e análise. Pequenas diferenças nas escolhas de cálculo faziam a mesma métrica aparecer com valores divergentes em lugares diferentes, minando a confiança nos dados — reuniões se perdiam discutindo qual número estava certo.

Métrica reimplementada
Números divergem, confiança desaba
Camada semântica
Números batem por construção
Ganho
Confiança estrutural nos dados

Decisão. Criar uma camada semântica onde cada métrica é definida uma única vez, como dado (a tese central do Cap III), com todas as escolhas explícitas. Todos os relatórios, dashboards e análises passam a consultar essa definição central em vez de reimplementar o cálculo.

Resultado. Os números passaram a bater por construção: a mesma métrica mostra o mesmo valor em todo lugar, porque todo lugar usa a mesma definição. As discussões deixaram de ser sobre de quem é o número certo e voltaram a ser sobre a decisão. E quando uma definição precisa mudar, altera-se um único dado na camada semântica, e a mudança propaga-se de imediato e consistentemente para todos os consumidores — sem caçar e reimplementar em dezenas de lugares. A confiança nos dados, antes erodida por definições divergentes, tornou-se uma garantia estrutural.

Incidente Real

Severidade · Média

Os dois relatórios que mostravam números diferentes para a mesma métrica

  • Antes Dois artefatos analíticos — um relatório e um painel — calculavam a mesma métrica de negócio de formas ligeiramente diferentes, cada um com suas próprias escolhas de cálculo, reimplementadas independentemente.
  • Sintoma Os dois apresentavam valores diferentes para a mesma métrica, gerando confusão e desconfiança: numa discussão, ninguém sabia qual número era o correto, e a credibilidade de ambos foi posta em dúvida.
  • Diagnóstico A ausência de uma definição única fez cada artefato reimplementar a métrica à sua maneira; as pequenas divergências nas escolhas produziram números que não batiam.
  • Correção A métrica foi definida uma única vez numa camada semântica central, e ambos os artefatos passaram a consultá-la — fazendo os números convergirem.
  • Kaizen Vira regra: cada métrica tem uma definição única na camada semântica; nenhum relatório reimplementa o cálculo, e os números batem por construção.

Decisão Arquitetural

ADR-0060

Camada semântica: uma métrica, uma definição, viva como dado

Aceito
Contexto

Quando a mesma métrica é reimplementada em cada relatório, pequenas diferenças nas escolhas de cálculo produzem valores divergentes para a mesma coisa — e ninguém sabe qual é o certo. A confiança nos dados desaba não por dados errados, mas por definições divergentes, e cada novo relatório recria o risco.

Decisão

Cada métrica é definida uma única vez numa camada semântica central, como dado (a tese central do Cap III), com escolhas explícitas. Todos os relatórios, dashboards e análises consultam essa definição em vez de recalcular. A mesma métrica dá o mesmo valor em todo lugar por construção; mudar a definição é mudar um dado, que propaga a todos os consumidores.

Alternativas consideradas

Cada relatório recalcula — valores divergem, confiança desaba. Convenção informal de cálculo — não impede a divergência. Definição em código duplicado — reimplementação que volta a divergir.

Consequências
PositivasNúmeros batem por construção (não podem divergir). Mudança de definição propaga a todos de uma vez. Foco volta à decisão, não ao "número certo". Confiança nos dados restaurada.
NegativasExige construir e manter a camada semântica e fazer todos os consumidores passarem por ela.
RiscosA camada vira dependência central de toda análise; mitigado por tratá-la com o rigor de qualquer componente crítico (versão, teste, observabilidade).

Antipadrões

Reimplementar a métrica em cada relatório

Cada artefato calcula a "receita" à sua maneira. Os números divergem e a confiança desaba. Correção: uma definição única na camada semântica, consultada por todos.

Definir a métrica em código duplicado

Copiar a lógica de cálculo entre lugares "para padronizar". As cópias divergem com o tempo. Correção: a definição é dado central único (Cap III), não código duplicado.

Exercício

Unifique as definições das suas métricas

  1. Pegue uma métrica importante do seu sistema (receita, usuários ativos, conversão). Ela é calculada em quantos lugares? Cada lugar usa a mesma definição, ou cada um a reimplementa?
  2. Faça o teste do "número certo": já aconteceu de dois relatórios mostrarem valores diferentes para a mesma métrica? Quanto tempo se perdeu discutindo qual estava certo?
  3. Explicite as escolhas: para uma métrica, liste as decisões de cálculo (inclui impostos? considera cancelamentos? quando reconhece?). São essas escolhas que, divergindo, fazem os números não baterem.
  4. Esboce a camada semântica: como essa métrica seria definida uma única vez, como dado, e como os relatórios a consultariam em vez de recalcular?
Meta: identificar as métricas reimplementadas em vários lugares no seu sistema e planejar uma camada semântica — para que cada métrica tenha uma definição única e os números batam por construção.
✓ Checklist do tópico
  • Cada métrica é definida uma única vez, centralmente, como dado
  • Todos os relatórios, dashboards e análises consultam a definição central
  • Nenhum artefato reimplementa o cálculo de uma métrica por conta própria
  • A mesma métrica produz o mesmo valor em todo lugar (por construção)
  • Mudar a definição de uma métrica propaga a mudança a todos os consumidores de uma vez
Meta Dados · Engenharia de Plataformas · Capítulo XI · §11.5Fim do Capítulo XI · voltar ao início
Meta Dados · Engenharia de Plataformas
Capítulo XII

Security
Engineering

Da segurança que reage para a segurança que se antecipa: modelar a ameaça antes do ataque, empilhar defesas que não dependem de uma só, blindar a cadeia de suprimentos, detectar o invasor que já entrou e provar a postura em vez de presumi-la.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo XII
Capítulo XII · Security Engineering
12.1

Modelagem de Ameaças

O Capítulo VII ensinou a responder ao incidente. Mas responder é chegar depois — o atacante encontrou a falha primeiro. A engenharia de segurança madura inverte a ordem do tempo: pensa como o atacante antes do ataque existir, para achar a falha antes que ele a ache.

Pré-requisitos · Cap VII Segurança · §7.5 Resposta a IncidentesConecta com · 12.2 Camadas · 12.4 DetecçãoTempo de leitura · ~14 min

O Problema

O Capítulo VII deu à plataforma uma capacidade essencial: responder a incidentes (§7.5) — detectar, conter, recuperar e aprender quando um ataque acontece. Mas responder, por definição, é chegar depois. Quando se está respondendo a um incidente, o atacante já encontrou a falha, já a explorou, já causou algum estrago. A resposta é necessária — nenhum sistema é invulnerável —, mas uma postura de segurança construída apenas sobre a resposta tem uma assimetria fatal: ela cede ao atacante a vantagem do tempo e da iniciativa.

Essa assimetria é o cerne do problema. Numa postura puramente reativa, a segurança só é pensada quando algo já deu errado — analisa-se a falha depois que ela foi explorada, corrige-se a brecha depois que ela vazou. Isso significa que o atacante encontra a fraqueza primeiro: ele teve todo o tempo do mundo para sondar o sistema à procura de uma falha, enquanto o defensor só olha para aquela falha específica depois que ela foi usada contra ele. O defensor está sempre um passo atrás, correndo para fechar portas que já foram arrombadas. Pior, cada falha é tratada isoladamente, conforme aparece, sem uma visão do conjunto — então o sistema fica cheio de fraquezas não descobertas, cada uma esperando um atacante que a procure. A segurança vira uma sucessão de surpresas desagradáveis, cada uma cara, cada uma tarde demais. O problema de fundo é uma inversão do tempo: a defesa é projetada em reação ao ataque, quando deveria ser projetada em antecipação a ele. Enquanto o defensor só pensar na ameaça depois que ela se concretiza, ele estará sempre defendendo o ataque de ontem, nunca o de amanhã.

A assimetria de quem só reage

Numa postura puramente reativa, o atacante encontra a falha primeiro — ele teve tempo de procurá-la, o defensor não. A defesa projetada em reação ao ataque está sempre um passo atrás. Correção: modelar a ameaça antes, para achar a falha primeiro.

A Decisão

A ameaça é modelada antes do ataque: sistematicamente, pergunta-se o que precisa ser protegido, por onde um atacante entraria, o que poderia dar errado, e como mitigar — encontrando as fraquezas e projetando as defesas enquanto ainda é barato, no papel, não no incidente.

A plataforma adota a modelagem de ameaças (threat modeling): um exercício sistemático e proativo de pensar como o atacante, antes que o atacante exista. Em vez de esperar um incidente para descobrir uma fraqueza, examina-se o sistema — idealmente ainda no projeto, antes de construir — fazendo, de forma estruturada, as perguntas que o atacante faria. O que precisa ser protegido? (os ativos: dados sensíveis, segredos, a integridade da operação). Por onde um atacante entraria? (as superfícies de ataque: cada entrada, cada integração, cada fronteira). O que poderia dar errado em cada ponto? (as ameaças: o que um adversário tentaria fazer ali — roubar dados, escalar privilégio, negar serviço, falsificar identidade). E, para cada ameaça identificada, como mitigá-la? (a defesa: a contramedida que a neutraliza, projetada agora). O resultado dessa análise é que as fraquezas são encontradas pelo defensor, antes do atacante, e as defesas são projetadas enquanto ainda é barato fazê-lo — no papel, durante o projeto, em vez de no calor de um incidente caro. A modelagem de ameaças não substitui a resposta a incidentes (§7.5) — nenhuma análise prevê tudo, e a capacidade de responder ao que escapar continua essencial —, mas inverte a assimetria: o defensor deixa de estar sempre um passo atrás, correndo para fechar portas arrombadas, e passa a fechar as portas antes de serem arrombadas. É a mesma disciplina que percorre o livro — pensar antes, projetar deliberadamente, decidir com consciência (os ADRs) — aplicada à segurança: a defesa é projetada em antecipação à ameaça, não em reação a ela. Pensar como o atacante, antes dele, é a forma de chegar primeiro à falha que ele procuraria.

Fluxograma operacional Modelar a ameaça antes do ataque existir
REAGIR ao ataquepensar em segurança DEPOISo atacante achou a falha 1º ANTECIPAR a ameaçapensar como o atacante ANTESachar a falha primeiro o que proteger? por onde entram? o que dá errado? como mitigar? modelar a ameaça é projetar a defesa antes do ataque existir
Figura 12.1-a. A postura reativa pensa em segurança depois do ataque — o atacante achou a falha primeiro. A modelagem de ameaças antecipa: o que proteger, por onde entram, o que dá errado, como mitigar — achando a falha antes do atacante.

O exercício estruturado da ameaça

modelagem_ameacas.pyPython
# MODELAR A AMEAÇA antes do ataque — estruturado, no projeto.
modelo = ModeloDeAmeacas(sistema="fluxo de pagamento")

# 1) O que proteger? (ativos)
modelo.ativo("dados do cartão", sensibilidade="crítica")
modelo.ativo("segredo de integração", sensibilidade="crítica")

# 2) Por onde entram? (superfícies de ataque)
modelo.entrada("API pública de cobrança")
modelo.entrada("webhook do provedor")

# 3) O que dá errado em cada ponto? (ameaças)
modelo.ameaca("API", "falsificar identidade de outro cliente")
modelo.ameaca("webhook", "forjar uma notificação de pagamento")

# 4) Como mitigar? (defesa projetada AGORA, no papel)
modelo.mitigar("falsificar identidade", com="autorização por requisição §7.1")
modelo.mitigar("forjar webhook", com="verificar assinatura do provedor")
O modelo percorre as quatro perguntas do atacante de forma estruturada — ativos, entradas, ameaças, mitigações — encontrando as fraquezas antes que ele as encontre.
ameaca_vira_requisito.pyPython
# Cada ameaça não mitigada vira um REQUISITO de segurança — rastreável.
def requisitos_de_seguranca(modelo: ModeloDeAmeacas) -> list[Requisito]:
    return [
        Requisito(ameaca=a, mitigacao=a.mitigacao, status="a implementar")
        for a in modelo.ameacas()
        if not a.ja_mitigada()   # a ameaça aberta é trabalho a fazer
    ]
    # A defesa é projetada ANTES de construir — barato, no papel.
    # Não há surpresa: a ameaça foi pensada, não descoberta num ataque.
Cada ameaça vira um requisito de segurança rastreável, projetado antes da construção. A defesa nasce do modelo, não da dor de um incidente.

A Verificação

modelagem_test.pyPython
class TestModelagemDeAmeacas(TestCase):
    def test_toda_entrada_tem_ameacas_analisadas(self):
        # Nenhuma superfície de ataque fica sem análise (sem ponto cego).
        for entrada in self.modelo.entradas():
            self.assertNotEmpty(self.modelo.ameacas_de(entrada))

    def test_toda_ameaca_tem_mitigacao(self):
        # Nenhuma ameaça identificada fica sem defesa projetada.
        for ameaca in self.modelo.ameacas():
            self.assertIsNotNone(ameaca.mitigacao,
                f"Ameaça sem mitigação: {ameaca}")  # falha aberta
A própria modelagem é verificada: nenhuma entrada fica sem análise de ameaças, e nenhuma ameaça fica sem mitigação projetada — não há ponto cego.
Tabela de decisão Postura reativa vs modelagem de ameaças
AspectoSó reagir ao ataqueModelar a ameaça antes
Quem acha a falha 1ºO atacanteO defensor
Quando se pensa na ameaçaDepois do incidenteNo projeto, antes
Custo da defesaAlto (incidente)Baixo (no papel)
VisãoFalha isoladaConjunto sistemático
PosiçãoUm passo atrásUm passo à frente

Estudo de Caso

Caso aplicado

Achar a falha antes do atacante

Contexto. A segurança da plataforma, embora sólida nas fundações (Cap VII), era pensada sobretudo em reação: uma fraqueza era analisada e corrigida depois que se manifestava. Isso deixava ao atacante a vantagem de encontrar as falhas primeiro, e o defensor sempre correndo atrás.

Só reagir
Atacante acha a falha 1º
Modelar antes
Defensor acha a falha 1º
Ganho
Inverter a assimetria do tempo

Decisão. Adotar a modelagem de ameaças como prática proativa: antes de construir um fluxo sensível, examiná-lo sistematicamente — o que proteger, por onde se entra, o que pode dar errado, como mitigar —, transformando cada ameaça identificada num requisito de segurança projetado de antemão.

Resultado. As fraquezas passaram a ser encontradas pelo defensor, no projeto, antes de qualquer atacante — e as defesas, projetadas enquanto ainda eram baratas, no papel, em vez de no calor de um incidente. A modelagem não eliminou a necessidade de responder ao que escapasse (§7.5), mas inverteu a assimetria: a plataforma deixou de defender sempre o ataque de ontem e passou a antecipar o de amanhã. Pensar como o atacante, antes dele, tornou-se a forma de chegar primeiro à falha.

Incidente Real

Severidade · Média

A entrada que ninguém tinha pensado como alvo

  • Antes Num ponto de entrada do sistema, uma forma de abuso era possível porque aquela superfície nunca havia sido analisada do ponto de vista de um atacante — a segurança ali era presumida, não pensada.
  • Sintoma A fraqueza só foi notada quando começou a ser explorada; até então, ninguém havia perguntado "o que um atacante tentaria fazer aqui?", então a falha existia à espera de quem a procurasse.
  • Diagnóstico A ausência de modelagem de ameaças deixou uma superfície de ataque sem análise — um ponto cego que o atacante encontrou antes do defensor.
  • Correção Modelagem de ameaças daquele fluxo e dos demais: cada entrada passou a ter suas ameaças analisadas e mitigadas, fechando os pontos cegos antes que fossem explorados.
  • Kaizen Vira regra: todo fluxo sensível é submetido à modelagem de ameaças no projeto; nenhuma superfície de ataque fica sem análise.

Decisão Arquitetural

ADR-0061

Modelagem de ameaças proativa, antes de construir

Aceito
Contexto

Uma postura puramente reativa (responder ao incidente, §7.5) cede ao atacante a vantagem do tempo: ele encontra a falha primeiro, e o defensor corre atrás de portas já arrombadas, tratando cada fraqueza isoladamente, sem visão do conjunto.

Decisão

Adota-se a modelagem de ameaças: sistematicamente, antes de construir, pergunta-se o que proteger (ativos), por onde se entra (superfícies), o que pode dar errado (ameaças) e como mitigar (defesas). Cada ameaça vira um requisito de segurança projetado de antemão. Não substitui a resposta a incidentes — inverte a assimetria, achando a falha antes do atacante.

Alternativas consideradas

Só responder a incidentes — atacante chega primeiro, defensor um passo atrás. Segurança ad hoc por intuição — pontos cegos não analisados. Auditoria pontual anual — tarde e desconectada do projeto.

Consequências
PositivasFraquezas encontradas pelo defensor, antes do atacante. Defesas projetadas baratas, no papel. Visão sistemática, sem pontos cegos. Inverte a assimetria do tempo.
NegativasExige disciplina de modelar ameaças no projeto e manter os modelos atualizados.
RiscosNenhuma modelagem prevê tudo; por isso a resposta a incidentes (§7.5) e as camadas (12.2) continuam essenciais para o que escapar.

Antipadrões

Pensar em segurança só depois do ataque

Analisar uma fraqueza apenas quando ela é explorada. O atacante achou primeiro. Correção: modelar a ameaça no projeto, antes de construir.

Modelar o sistema e nunca atualizar o modelo

Um modelo de ameaças feito uma vez e esquecido, enquanto o sistema evolui. Correção: o modelo acompanha o sistema; novas superfícies, novas ameaças analisadas.

Exercício

Modele as ameaças de um fluxo seu

  1. Escolha um fluxo sensível do seu sistema (pagamento, autenticação, dados pessoais). Liste os ativos: o que, ali, precisa ser protegido?
  2. Mapeie as superfícies: por onde um atacante poderia entrar nesse fluxo? Cada entrada, cada integração, cada fronteira é um ponto a considerar.
  3. Para cada entrada, pense como o atacante: o que ele tentaria fazer ali? Roubar dados, falsificar identidade, escalar privilégio, forjar uma requisição?
  4. Para cada ameaça, projete a mitigação: que defesa a neutralizaria? Depois, verifique — essa defesa já existe no seu sistema, ou é um requisito em aberto?
Meta: praticar a modelagem de ameaças num fluxo real — encontrando as fraquezas como o defensor, antes do atacante, e projetando as defesas enquanto ainda é barato.
✓ Checklist do tópico
  • Fluxos sensíveis passam por modelagem de ameaças antes de serem construídos
  • A análise cobre ativos, superfícies de ataque, ameaças e mitigações
  • Cada superfície de ataque tem suas ameaças analisadas (sem pontos cegos)
  • Cada ameaça identificada tem uma mitigação projetada e rastreável
  • O modelo de ameaças é atualizado conforme o sistema evolui
Meta Dados · Engenharia de Plataformas · Capítulo XII · §12.1Próximo → 12.2 Defesa em Profundidade
Capítulo XII · Security Engineering
12.2

Defesa em Profundidade

Confiar numa única linha de defesa é construir uma casca dura em torno de um miolo mole: no instante em que o perímetro é furado, tudo lá dentro fica exposto. A segurança séria empilha camadas, de modo que furar uma não compromete o todo — nenhuma camada sozinha precisa ser perfeita.

Pré-requisitos · 12.1 · Cap VII §7.1 §7.3 · Cap IV §4.2 · Cap IX §9.2Conecta com · 12.3 Cadeia · 12.4 DetecçãoTempo de leitura · ~14 min

O Problema

A forma mais intuitiva — e mais perigosa — de proteger um sistema é erguer uma única e forte linha de defesa em volta dele: um perímetro robusto, uma muralha alta, e confiar que, enquanto a muralha aguentar, tudo lá dentro está seguro. É o modelo da casca dura com miolo mole: investe-se toda a segurança na fronteira externa, e o interior — os dados, os serviços, os segredos — fica relativamente indefeso, sob a premissa de que ninguém de fora conseguirá passar pela muralha.

Essa premissa é falsa, e a falsidade dela é catastrófica. Perímetros cedem — sempre. Uma vulnerabilidade não prevista, uma credencial vazada, um phishing bem-sucedido, uma configuração errada, uma das mil formas pelas quais uma muralha pode ser contornada: basta uma falha na única linha de defesa para que o atacante esteja dentro. E uma vez dentro, num sistema de casca dura e miolo mole, ele encontra um interior indefeso: pode mover-se livremente, acessar os dados em texto puro, alcançar os serviços sem novas barreiras, porque toda a segurança estava na muralha que ele já transpôs. O comprometimento de um único ponto — o perímetro — significa o comprometimento de tudo. Esse é o problema fundamental de depender de uma única camada: ela precisa ser perfeita, porque não há nada atrás dela. E exigir perfeição de qualquer componente é uma garantia de fracasso, porque nenhum componente é perfeito. A casca dura concentra todo o risco num único ponto de falha; o dia em que esse ponto falha — e ele falhará — é o dia em que o sistema inteiro cai junto. Uma segurança que aposta tudo numa só barreira é uma segurança que aposta a sobrevivência do sistema na perfeição impossível de um único elemento.

A casca dura com miolo mole

Confiar numa única linha de defesa concentra todo o risco num ponto: quando o perímetro cede — e perímetros cedem —, o interior indefeso fica todo exposto. A camada única precisa ser perfeita, e nada é perfeito. Correção: empilhar camadas, de modo que furar uma encontre a próxima.

A Decisão

A segurança é empilhada em camadas independentes — perímetro, autorização (§7.1), criptografia (§7.3), isolamento (§4.2) —, de modo que comprometer uma não compromete o todo. Nenhuma camada precisa ser perfeita, porque a falha de cada uma é coberta pela seguinte.

A plataforma adota a defesa em profundidade: em vez de uma única muralha, múltiplas camadas independentes de defesa, empilhadas, de modo que um atacante que transponha uma encontre outra atrás dela — e outra, e outra. Cada camada é uma barreira que vale por si, e várias delas já apareceram ao longo do livro, agora compreendidas como um sistema de profundidade. O perímetro é a primeira camada, mas não a única. Atrás dele, a autorização (§7.1 Cap VII): mesmo um atacante que passe o perímetro encontra, em cada operação, a barreira do "negar por padrão" — ele só consegue o que a permissão explícita concede. Atrás disso, a criptografia (§7.3): mesmo que ele alcance os dados, encontra-os cifrados, inúteis sem a chave que vive em outra camada (o cofre, §7.2). E o isolamento (§4.2 Cap IV): mesmo dentro, ele está confinado a um escopo, sem alcançar o resto. (Esse princípio já apareceu, por exemplo, no RAG governado do §9.2, onde a recuperação filtrava por permissão e re-checava cada trecho — duas camadas, não uma.) A consequência transforma a postura de segurança: como há profundidade, nenhuma camada sozinha precisa ser perfeita. A falha de uma é coberta pela seguinte. O perímetro foi furado? A autorização ainda barra. A autorização foi contornada num ponto? Os dados ainda estão cifrados. A segurança deixa de depender de um único elemento acertar sempre — uma exigência impossível — e passa a depender de que nem todas as camadas falhem ao mesmo tempo no mesmo ponto, o que é incomparavelmente mais provável de se sustentar. O comprometimento de um ponto deixa de significar o comprometimento de tudo, porque há defesas atrás daquele ponto. É a aplicação à segurança de um princípio de robustez que percorre o livro — não confiar a sobrevivência do todo a um único componente —, transformando a fragilidade da casca dura na resiliência das camadas.

Arquitetura lógica Camadas independentes de defesa
CASCA DURA, MIOLO MOLEuma só barreira (o perímetro)furou? tudo exposto CAMADAS perímetro · autorização · cifra · isolamento furou uma? a próxima segura perímetro autorização §7.1 cifra §7.3 isolamento §4.2 nenhuma camada sozinha precisa ser perfeita
Figura 12.2-a. A casca dura concentra o risco numa só barreira — furou, tudo exposto. A defesa em profundidade empilha camadas (perímetro, autorização §7.1, cifra §7.3, isolamento §4.2): furar uma encontra a próxima, e nenhuma precisa ser perfeita.

Camadas que não dependem uma da outra

defesa_em_profundidade.phpPHP
// DEFESA EM PROFUNDIDADE: várias camadas, cada uma vale por si.
final class AcessoAoDado
{
    public function ler(Usuario $u, RecursoId $id): Dado
    {
        // CAMADA 1 — autorização (§7.1): negar por padrão.
        if (! $this->autz->pode($u, 'ler', $id)) {
            throw new AcessoNegado();
        }

        // CAMADA 2 — isolamento (§4.2): o RLS confina ao tenant do usuário.
        $registro = $this->repo->buscarNoEscopoDe($u, $id);

        // CAMADA 3 — criptografia (§7.3): o dado vem cifrado; decifra só
        // quem tem a chave, que vive no cofre (§7.2) — outra camada.
        return $this->cripto->decifrar($registro, $this->cofre->chaveDe($u));
        // Furar UMA destas não basta: as outras ainda protegem.
    }
}
Um único acesso atravessa três camadas independentes — autorização, isolamento, criptografia. Comprometer uma não basta; as outras continuam de pé.
camadas_independentes.phpPHP
// As camadas são INDEPENDENTES: a falha de uma não derruba as outras.
// Cenário: o atacante furou o PERÍMETRO (camada externa).

// → Ainda assim, a autorização (§7.1) barra cada operação:
$autz->pode($atacante, 'ler', $recurso);   // false → negado

// → E se contornasse a autorização num ponto, o dado está CIFRADO:
$dadoBruto = $repo->ler($recurso);          // bytes ilegíveis
// sem a chave (cofre §7.2), o dado cifrado é inútil.

// Nenhuma camada precisa ser perfeita — só não podem TODAS falhar
// no mesmo ponto ao mesmo tempo. Muito mais robusto.
O cenário mostra a profundidade em ação: furar o perímetro não basta, porque a autorização barra e, se contornada, a cifra ainda protege. Camadas independentes.

A Verificação

profundidade_test.phpPHP
final class DefesaEmProfundidadeTest extends TestCase
{
    public function test_furar_uma_camada_nao_expoe_o_dado(): void
    {
        // Simula o perímetro furado: o atacante "está dentro".
        $atacante = $this->atacanteComPerimetroFurado();

        // A camada de autorização (§7.1) ainda barra.
        $this->expectException(AcessoNegado::class);
        $this->acesso->ler($atacante, $this->recurso());
    }

    public function test_dado_alcancado_esta_cifrado(): void
    {
        // Mesmo lendo o byte cru do banco, sem a chave é ilegível (§7.3).
        $bruto = $this->repo->lerBruto($this->recurso());
        $this->assertNotEquals($this->textoEsperado(), $bruto);  // cifrado
    }
}
Os testes provam a profundidade: com o perímetro furado, a autorização ainda barra; e o dado, se alcançado, está cifrado. Furar uma camada não expõe nada.
Tabela de decisão Linha única vs defesa em profundidade
AspectoCasca dura, miolo moleDefesa em profundidade
CamadasUma (o perímetro)Várias, independentes
Perímetro furadoTudo expostoPróxima camada segura
Cada camada precisa serPerfeitaBoa o bastante
Ponto único de falhaSim (o perímetro)Não
Comprometer um pontoCompromete tudoContido

Estudo de Caso

Caso aplicado

Quando furar uma camada deixou de bastar

Contexto. A segurança da plataforma, em alguns pontos, dependia fortemente do perímetro — uma vez dentro, o acesso aos dados e serviços tinha poucas barreiras adicionais. Isso significava que comprometer a fronteira externa poderia expor muito do interior.

Linha única
Furou o perímetro, tudo cai
Profundidade
Furou uma, a próxima segura
Ganho
Sem ponto único de falha

Decisão. Aplicar defesa em profundidade, empilhando as proteções que o livro já estabelecera como camadas independentes: o perímetro, a autorização por operação (§7.1), o isolamento por tenant (§4.2) e a criptografia dos dados sensíveis (§7.3) com as chaves no cofre (§7.2). Cada acesso passa por várias delas.

Resultado. Comprometer um único ponto deixou de comprometer tudo. Um atacante que transpusesse o perímetro encontraria a autorização barrando cada operação; se a contornasse num ponto, encontraria os dados cifrados, inúteis sem a chave de outra camada. Nenhuma camada precisa mais ser perfeita, porque a falha de uma é coberta pela seguinte. A fragilidade da casca dura deu lugar à resiliência das camadas — a segurança deixou de apostar tudo num único elemento impossível de ser perfeito.

Incidente Real

Severidade · Alta

O acesso interno que encontrou tudo aberto

  • Antes Num ponto do sistema, a segurança concentrava-se na fronteira externa; uma vez passada, o acesso interno aos dados tinha poucas barreiras adicionais.
  • Risco Caso a fronteira fosse comprometida, o interior relativamente indefeso ficaria amplamente exposto — o comprometimento de um único ponto poderia significar o comprometimento de muito mais.
  • Diagnóstico A dependência de uma linha de defesa quase única (casca dura, miolo mole) concentrava o risco num ponto e exigia que esse ponto fosse perfeito.
  • Correção Introdução de camadas independentes atrás do perímetro — autorização, isolamento e criptografia — de modo que furar a fronteira não exponha mais o interior.
  • Kaizen Vira regra: a segurança é em profundidade; nenhuma proteção crítica depende de uma única camada, e furar uma encontra a próxima.

Decisão Arquitetural

ADR-0062

Defesa em profundidade: camadas independentes, nenhuma perfeita

Aceito
Contexto

Confiar numa única linha de defesa (casca dura, miolo mole) concentra todo o risco num ponto: quando o perímetro cede — e perímetros cedem —, o interior indefeso fica exposto. A camada única precisa ser perfeita, e nenhum componente é perfeito.

Decisão

A segurança é empilhada em camadas independentes — perímetro, autorização (§7.1), isolamento por tenant (§4.2), criptografia (§7.3) com chaves no cofre (§7.2) —, de modo que comprometer uma não compromete o todo: furar uma camada encontra a próxima. Nenhuma camada precisa ser perfeita, pois a falha de cada uma é coberta pela seguinte.

Alternativas consideradas

Linha única (perímetro) — ponto único de falha; furou, tudo cai. Camadas que dependem uma da outra — falham juntas, não é profundidade real. Confiar em perfeição de uma camada — impossível.

Consequências
PositivasComprometer um ponto não compromete tudo. Nenhum ponto único de falha. Cada camada pode ser imperfeita. Resiliência no lugar da fragilidade.
NegativasExige projetar e manter várias camadas independentes, com algum custo de complexidade e desempenho.
RiscosCamadas mal projetadas podem dar falsa sensação de profundidade se dependerem da mesma premissa; mitigado por torná-las genuinamente independentes.

Antipadrões

Toda a segurança no perímetro

Casca dura, miolo mole: forte por fora, indefeso por dentro. Furou a muralha, tudo cai. Correção: camadas independentes atrás do perímetro.

Camadas que dependem da mesma premissa

"Várias" defesas que falham todas juntas se uma premissa cair. Correção: camadas genuinamente independentes — a falha de uma não implica a das outras.

Exercício

Conte as camadas do seu sistema

  1. Escolha um dado sensível do seu sistema. Quantas barreiras independentes um atacante precisaria furar para alcançá-lo legível? Uma só, ou várias?
  2. Faça o teste do perímetro furado: se um atacante entrasse na sua rede interna agora, o que ele encontraria? Dados em texto puro e acesso livre, ou novas barreiras (autorização, cifra, isolamento)?
  3. Identifique o ponto único de falha: existe uma única proteção que, se falhar, expõe tudo? Esse é o seu miolo mole.
  4. Para esse ponto, projete uma camada adicional independente: que segunda barreira cobriria a falha da primeira, sem depender da mesma premissa?
Meta: avaliar quantas camadas independentes protegem os seus dados — e identificar onde uma única linha de defesa concentra o risco, para empilhar profundidade onde hoje há só uma casca.
✓ Checklist do tópico
  • A segurança é empilhada em múltiplas camadas independentes, não numa só
  • Atrás do perímetro há autorização (§7.1), isolamento (§4.2) e criptografia (§7.3)
  • Furar uma camada encontra a próxima — não o interior exposto
  • Nenhuma proteção crítica depende de uma única camada ser perfeita
  • As camadas são genuinamente independentes (não falham todas pela mesma premissa)
Meta Dados · Engenharia de Plataformas · Capítulo XII · §12.2Próximo → 12.3 A Cadeia de Suprimentos
Capítulo XII · Security Engineering
12.3

A Cadeia de Suprimentos

A maior parte do que roda no seu sistema, você não escreveu: bibliotecas, imagens-base, dependências de dependências. Cada uma é código de terceiros executando com os seus privilégios. Uma dependência comprometida é um sistema comprometido — e a superfície de ataque inclui tudo o que você importou.

Pré-requisitos · 12.1 · 12.2 · Cap X §10.2 Imagens · Cap VII §7.2Conecta com · 12.4 Detecção · 12.5 PosturaTempo de leitura · ~14 min

O Problema

Uma verdade desconfortável do software moderno: a maior parte do código que roda no seu sistema, você não escreveu. Para cada linha que sua equipe produziu, há dezenas — às vezes centenas — vindas de bibliotecas de terceiros, frameworks, a imagem-base do contêiner (§10.2), e as dependências dessas dependências, as transitivas, que entraram sem que ninguém as escolhesse diretamente. Esse vasto corpo de código alheio não é um detalhe de implementação: ele executa dentro do seu sistema, com os seus privilégios, acessando os seus dados. É parte do seu sistema tanto quanto o código que você escreveu — e, do ponto de vista de um atacante, parte da sua superfície de ataque.

E essa superfície é traiçoeira porque é, em grande medida, invisível e fora do seu controle. Uma dependência comprometida — uma biblioteca em que um atacante inseriu código malicioso, ou que tem uma vulnerabilidade conhecida e explorável — é uma porta aberta no coração do seu sistema, e você pode nem saber que ela existe. O perigo se manifesta de várias formas: uma biblioteca popular que é sequestrada e passa a distribuir uma versão maliciosa; uma vulnerabilidade descoberta numa dependência que você usa há anos, subitamente explorável; uma dependência transitiva — que você nem escolheu, que veio junto de outra — comprometida três níveis abaixo, num código que ninguém na sua equipe jamais olhou. Em todos esses casos, a defesa que você construiu no seu código é irrelevante: o atacante não precisa furar suas camadas (§12.2) se já está rodando dentro delas, na forma de uma dependência que você importou e à qual deu seus privilégios. Confiar cegamente em tudo o que se importa — instalar a biblioteca, usar a imagem-base, puxar as transitivas, sem verificar nada — é estender uma confiança ilimitada a um código que você não escreveu, não revisou e muitas vezes não conhece. É deixar a porta dos fundos do seu sistema nas mãos de milhares de autores anônimos, e rezar para que nenhum deles seja malicioso, nenhuma de suas bibliotecas seja sequestrada, e nenhuma vulnerabilidade seja descoberta. Em escala, essa reza não é uma estratégia de segurança.

A porta aberta na dependência que você não escreveu

O código de terceiros roda com os seus privilégios; uma dependência comprometida — mesmo uma transitiva que você nem sabia que tinha — é uma porta no coração do sistema. Confiar cegamente no que se importa é ceder a porta dos fundos a autores anônimos. Correção: travar, verificar, varrer e minimizar as dependências.

A Decisão

A cadeia de suprimentos é tratada como superfície de ataque: as versões das dependências são travadas (§10.2), sua proveniência é verificada, suas vulnerabilidades conhecidas são varridas continuamente, e a pegada de dependências é minimizada — quanto menos código alheio, menos risco.

A plataforma trata a cadeia de suprimentos de software — tudo o que importa e não escreveu — com o mesmo rigor que aplica ao próprio código, reconhecendo-a como parte da superfície de ataque. Quatro práticas a governam. Primeira, travar as versões: as dependências são fixadas em versões exatas (a mesma disciplina das imagens imutáveis, §10.2), de modo que ninguém — nem um atacante que sequestre uma biblioteca — possa injetar uma versão diferente sem que a mudança seja explícita, revisada e deliberada. Uma dependência não muda silenciosamente por baixo dos panos. Segunda, verificar a proveniência: confirma-se que a dependência veio de fato de sua origem legítima e não foi adulterada no caminho — que a biblioteca que se instalou é a biblioteca que seus autores publicaram, não uma falsificação. Terceira, varrer vulnerabilidades conhecidas: as dependências são continuamente comparadas com bases de vulnerabilidades conhecidas, de modo que, quando uma falha é descoberta numa biblioteca que se usa, isso é detectado — e tratado — em vez de permanecer uma porta aberta ignorada. (Essa varredura é parte da postura verificável do §12.5 e dos portões de entrega do §10.4.) Quarta, e talvez a mais profunda, minimizar a pegada: cada dependência é um risco, então a plataforma resiste a importar o que não precisa — questiona se uma biblioteca pesada é mesmo necessária, prefere o que tem menos dependências transitivas, remove o que deixou de ser usado. Menos código alheio significa menos superfície de ataque, menos a verificar, menos a temer. É a aplicação à cadeia de suprimentos do princípio da subtração que abre o Manifesto e do menor privilégio que rege o Capítulo VII: assim como se concede só o acesso necessário, importa-se só o código necessário, e cada peça importada é tratada não como confiável por padrão, mas como mais uma superfície a governar. A confiança cega em tudo o que se importa dá lugar a uma confiança verificada — travada, conferida, varrida — e deliberadamente minimizada. A porta dos fundos deixa de estar nas mãos anônimas de autores que você nunca verificou, e passa a ser tão governada quanto a porta da frente.

Arquitetura lógica A cadeia de suprimentos como superfície de ataque
SEU CÓDIGOo que você escreveu biblioteca Avocê não escreveu imagem basevocê não escreveu dependência Bvocê não escreveu transitiva Cnem sabia que tinha dependência comprometida = sistema comprometidoo código que você não escreveu também é sua superfície
Figura 12.3-a. Bibliotecas, imagens-base e dependências transitivas — o código que você não escreveu — rodam com os seus privilégios. Uma dependência comprometida (mesmo transitiva) é uma porta no coração do sistema. Travar, verificar, varrer e minimizar.

Travar, verificar e varrer as dependências

dependencias.travatexto
# VERSÕES TRAVADAS — nada muda silenciosamente (§10.2).
biblioteca-a == 2.4.1   # versão EXATA, com hash de integridade
  └─ hash: sha256:9f86d0…  # proveniência verificada

dependencia-b == 1.0.7
  └─ hash: sha256:2c26b4…
  └─ transitiva-c == 0.3.2   # a que veio junto: TAMBÉM travada
       └─ hash: sha256:486ea4…

# Instalar exige que o hash bata: a dependência é a publicada,
# não uma versão sequestrada ou adulterada no caminho.
As versões são travadas com hashes de integridade — inclusive as transitivas. Instalar exige que o hash bata: a dependência é a legítima, não uma adulterada.
varredura_cadeia.shBash
# VARRER vulnerabilidades conhecidas — continuamente (§12.5, §10.4).
varrer-dependencias dependencias.trava

# Uma falha conhecida numa dependência que usamos? É DETECTADA.
if ha-vulnerabilidade-conhecida; then
  bloquear-entrega "dependência vulnerável: corrigir antes de subir"
fi

# MINIMIZAR: a dependência ainda é usada? Se não, remover.
listar-dependencias-nao-usadas   # menos código alheio = menos risco

# Cada dependência importada é uma superfície — só o necessário entra.
A varredura compara as dependências com vulnerabilidades conhecidas e bloqueia a entrega se houver falha; e o que não é usado é removido — minimizando a superfície.

A Verificação

cadeia_test.pyPython
class TestCadeiaDeSuprimentos(TestCase):
    def test_versoes_travadas_com_integridade(self):
        # Toda dependência (inclusive transitiva) tem versão e hash fixos.
        for dep in self.todas_as_dependencias():
            self.assertTrue(dep.versao_exata())
            self.assertTrue(dep.tem_hash_de_integridade())

    def test_falha_a_entrega_se_houver_vulneravel(self):
        # Uma dependência com vulnerabilidade conhecida barra a entrega.
        self.injetar_dependencia_vulneravel()
        self.assertFalse(self.varredura_da_cadeia().passou())  // §10.4
Os testes garantem o cerne: toda dependência tem versão e hash fixos, e uma dependência vulnerável conhecida barra a entrega — a cadeia é governada, não confiada cegamente.
Tabela de decisão Confiar cego vs governar a cadeia
AspectoImportar e confiarCadeia governada
Versão da dependênciaPode mudar sozinhaTravada, hash (§10.2)
ProveniênciaPresumidaVerificada
Vulnerabilidade conhecidaPorta aberta ignoradaVarrida, detectada
TransitivasInvisíveisTravadas e varridas
PegadaInflada "por garantia"Minimizada

Estudo de Caso

Caso aplicado

Governar o código que não foi escrito aqui

Contexto. Como todo sistema moderno, a plataforma depende de um grande corpo de código de terceiros — bibliotecas, imagens-base, dependências transitivas — que executa com os seus privilégios. Importá-las sem verificação significaria estender confiança ilimitada a código não escrito, não revisado e muitas vezes desconhecido.

Confiar cego
Porta nas mãos de anônimos
Cadeia governada
Travada, verificada, varrida
Ganho
Superfície de terceiros sob controle

Decisão. Tratar a cadeia de suprimentos como superfície de ataque: travar todas as versões com hashes de integridade (§10.2), inclusive as transitivas; verificar a proveniência; varrer continuamente vulnerabilidades conhecidas como portão de entrega (§10.4, §12.5); e minimizar a pegada, removendo o que não é necessário.

Resultado. Uma dependência deixou de poder mudar silenciosamente — qualquer alteração é explícita e revisada, fechando a porta ao sequestro de bibliotecas. Vulnerabilidades conhecidas em dependências usadas passaram a ser detectadas e tratadas antes de subir, em vez de permanecerem portas abertas ignoradas. E a superfície encolheu, porque só o código necessário é importado. A confiança cega em tudo o que se importa deu lugar a uma confiança verificada e minimizada — a porta dos fundos tornou-se tão governada quanto a da frente.

Incidente Real

Severidade · Alta

A vulnerabilidade na dependência usada há tempos

  • Antes Uma dependência de terceiros, em uso na plataforma havia tempos sem varredura contínua, teve uma vulnerabilidade conhecida descoberta publicamente.
  • Risco Sem uma varredura que comparasse as dependências com vulnerabilidades conhecidas, a falha poderia permanecer presente e explorável no sistema sem que ninguém percebesse — uma porta aberta ignorada.
  • Diagnóstico A ausência de varredura contínua da cadeia de suprimentos deixava vulnerabilidades em dependências passarem despercebidas, mesmo sendo de conhecimento público.
  • Correção Instituição da varredura contínua de vulnerabilidades como portão de entrega, com travamento de versões e verificação de proveniência; a dependência afetada foi atualizada.
  • Kaizen Vira regra: a cadeia de suprimentos é varrida continuamente; uma dependência com vulnerabilidade conhecida barra a entrega até ser corrigida.

Decisão Arquitetural

ADR-0063

A cadeia de suprimentos é superfície de ataque: travar, verificar, varrer, minimizar

Aceito
Contexto

A maior parte do código que roda no sistema é de terceiros e executa com os seus privilégios. Uma dependência comprometida — maliciosa, vulnerável, ou transitiva desconhecida — é uma porta no coração do sistema. Confiar cegamente no que se importa é ceder a porta dos fundos a autores anônimos.

Decisão

A cadeia de suprimentos é governada como superfície de ataque: versões travadas com hashes de integridade (§10.2), inclusive transitivas; proveniência verificada; vulnerabilidades conhecidas varridas continuamente como portão de entrega (§10.4, §12.5); e pegada minimizada (importar só o necessário — a subtração do Manifesto). A confiança cega dá lugar à confiança verificada.

Alternativas consideradas

Importar e confiar — porta aberta a sequestro e vulnerabilidades. Travar sem varrer — fixa a versão, mas ignora falhas descobertas. Varrer sem travar — a versão pode mudar por baixo dos panos.

Consequências
PositivasDependências não mudam silenciosamente. Vulnerabilidades conhecidas detectadas antes de subir. Transitivas governadas. Superfície de ataque minimizada.
NegativasExige ferramental de travamento, verificação e varredura, e a disciplina de minimizar dependências.
RiscosA varredura depende de bases de vulnerabilidades conhecidas (não pega o desconhecido); por isso as camadas (12.2) e a detecção (12.4) seguem essenciais.

Antipadrões

Confiar cegamente em tudo o que se importa

Instalar bibliotecas e imagens sem travar, verificar ou varrer. Confiança ilimitada em código alheio. Correção: travar versões, verificar proveniência, varrer vulnerabilidades.

Inflar a pegada de dependências "por garantia"

Importar bibliotecas pesadas que não são realmente necessárias. Cada uma é superfície de ataque. Correção: minimizar — só o necessário entra (a subtração do Manifesto).

Exercício

Inventarie a sua cadeia de suprimentos

  1. Pegue uma aplicação sua. Quantas dependências de terceiros ela tem, contando as transitivas? Você sabe o que cada uma faz e por que está ali?
  2. Faça o teste do travamento: as versões das suas dependências estão fixadas, ou podem mudar sozinhas na próxima instalação? Uma mudança silenciosa seria notada?
  3. Faça o teste da vulnerabilidade: existe uma varredura que compara as suas dependências com vulnerabilidades conhecidas? Se uma falha fosse descoberta numa delas, você saberia?
  4. Faça o teste da minimização: há dependências que entraram "por garantia" e não são realmente usadas? Cada uma é superfície de ataque que poderia ser removida.
Meta: tratar a sua cadeia de suprimentos como superfície de ataque — travando versões, varrendo vulnerabilidades e minimizando a pegada, para que o código que você não escreveu deixe de ser uma porta dos fundos não governada.
✓ Checklist do tópico
  • As versões das dependências são travadas com integridade, inclusive as transitivas
  • A proveniência das dependências é verificada (são as publicadas, não adulteradas)
  • As dependências são varridas continuamente contra vulnerabilidades conhecidas
  • Uma dependência com vulnerabilidade conhecida barra a entrega (§10.4)
  • A pegada de dependências é minimizada — só o necessário é importado
Meta Dados · Engenharia de Plataformas · Capítulo XII · §12.3Próximo → 12.4 Detecção e Resposta
Capítulo XII · Security Engineering
12.4

Detecção e Resposta

Toda prevenção falha eventualmente — modelar ameaças e empilhar camadas reduz o risco, não o elimina. Quem presume que preveniu tudo fica cego para o intruso que já entrou. A segurança madura presume a brecha: vigia o comportamento anômalo para caçar o atacante de dentro.

Pré-requisitos · 12.1–12.3 · Cap VII §7.5 · Cap VIII ObservabilidadeConecta com · 12.5 PosturaTempo de leitura · ~14 min

O Problema

Os tópicos anteriores deste capítulo fortaleceram a prevenção: modelar ameaças para achar falhas antes do atacante (§12.1), empilhar camadas para que furar uma não baste (§12.2), governar a cadeia de suprimentos para fechar a porta dos fundos (§12.3). Tudo isso reduz drasticamente a probabilidade de um ataque ter sucesso. Mas reduzir não é eliminar. Toda prevenção falha eventualmente — uma vulnerabilidade de dia zero que ninguém poderia prever, uma credencial roubada que abre a porta legitimamente, uma combinação improvável de fatores que contorna as camadas. Por mais forte que seja a prevenção, há sempre uma probabilidade de que um atacante, em algum momento, esteja dentro.

E é aqui que uma postura que confia apenas na prevenção tem seu ponto cego mais perigoso. Quem opera sob a premissa "preveni tudo, logo estou seguro" não constrói nenhuma capacidade de detectar um intruso que já entrou. O atacante que transpôs as defesas encontra um interior onde ninguém está olhando: ele se move pelo sistema, acessa dados, escala privilégios, estabelece persistência — tudo sem ser notado, porque toda a atenção estava voltada para impedir a entrada, e nenhuma para perceber a presença. O defensor está cego dentro da própria casa. As consequências dessa cegueira são severas. O comprometimento se prolonga: um atacante não detectado permanece no sistema por tempo indefinido — semanas, meses —, e cada dia dentro é mais dano, mais dados exfiltrados, mais acesso consolidado. E quando o comprometimento finalmente se revela — por um efeito visível, por um terceiro que avisa —, descobre-se que o atacante esteve lá o tempo todo, e que tudo o que ele tocou nesse período é suspeito. A premissa de que a prevenção é suficiente não apenas é falsa; ela é perigosa, porque cria uma falsa sensação de segurança que impede a construção das capacidades de detecção que pegariam o que a prevenção deixou passar. Confiar que se preveniu tudo é a melhor forma de não ver o ataque que a prevenção não conteve.

A cegueira de quem só previne

Toda prevenção falha eventualmente. Quem presume que preveniu tudo não constrói detecção — e fica cego para o intruso que já entrou, que se move livremente porque ninguém olha de dentro. Correção: presumir a brecha e vigiar o comportamento anômalo para caçar o atacante interno.

A Decisão

Presume-se a brecha: parte-se do princípio de que um atacante pode estar dentro, e vigia-se continuamente o comportamento do sistema em busca de anomalias que traiam sua presença — alimentando o ciclo de resposta (§7.5) com a detecção que ele pressupõe.

A plataforma adota a postura de presumir a brecha (assume breach): em vez de operar sob a premissa confortável de que a prevenção é perfeita, opera sob a premissa realista de que um atacante pode já estar dentro — e, por isso, constrói a capacidade de detectá-lo. A chave da detecção é o comportamento anômalo: um atacante que comprometeu o sistema, por mais cuidadoso que seja, tende a fazer coisas que destoam do normal — acessar recursos que aquele usuário nunca acessa, consumir recursos de forma incompatível com a carga esperada, agir em horários atípicos, mover-se por caminhos incomuns. A plataforma vigia esses sinais, e essa vigilância é precisamente a observabilidade do Capítulo VIII voltada para a segurança: as mesmas métricas, traços e registros que tornam o sistema compreensível para a operação tornam-no vigiável contra o intruso. O caso real do criptominerador (§7.5) é o exemplo perfeito desse princípio: o que denunciou o comprometimento foi um consumo de recursos anômalo — uma métrica fora do esperado, captada pela observabilidade, que não fazia sentido para a carga normal daquele servidor. A presença do atacante traiu-se pelo comportamento, e a vigilância o captou. Detectada a anomalia, ela alimenta o ciclo de resposta que o Capítulo VII (§7.5) estabeleceu: detectar — agora proativamente, não por acaso —, conter, recuperar e aprender. A diferença que "presumir a brecha" introduz é tornar a detecção uma capacidade deliberada e contínua, em vez de uma descoberta acidental. O atacante deixa de poder contar com a cegueira do defensor: ele agora opera dentro de um sistema que está olhando, onde cada desvio do normal é um sinal que pode denunciá-lo. A segurança completa-se assim em duas metades complementares: a prevenção (§12.1–§12.3) reduz a chance de o atacante entrar; a detecção e resposta garantem que, se ele entrar mesmo assim, não permanecerá invisível. Presumir a brecha não é pessimismo — é a única postura honesta diante da verdade de que nenhuma prevenção é perfeita, e a única que transforma o intruso de um fantasma invisível num alvo que se pode caçar.

Fluxograma operacional Presumir a brecha e caçar a anomalia
"PREVENI, logo estou seguro"se o intruso entrou, ninguém vêcego dentro de casa PRESUMIR a brechavigiar o comportamento anômalocaçar o intruso já dentro detectar anomalia conter recuperar aprender §7.5 o consumo anômalo do minerador foi o sinal — a observabilidade (Cap VIII) detectou
Figura 12.4-a. Confiar só na prevenção deixa cego para o intruso interno. Presumir a brecha vigia o comportamento anômalo — como o consumo do minerador (§7.5), captado pela observabilidade (Cap VIII) — e alimenta o ciclo de resposta.

Vigiar a anomalia que trai o intruso

deteccao.pyPython
# PRESUMIR A BRECHA: vigiar comportamento ANÔMALO (Cap VIII p/ segurança).
def vigiar_anomalias(observabilidade: Metricas) -> None:
    # O atacante interno se trai pelo que destoa do NORMAL.
    for sinal in observabilidade.fluxo():
        if sinal.destoa_do_normal():       # fora do padrão esperado
            # Ex.: consumo de recursos incompatível com a carga
            # (foi assim que o minerador §7.5 se denunciou).
            alertar_seguranca(sinal, severidade=sinal.gravidade())
            # A anomalia detectada alimenta o ciclo de resposta (§7.5).
A detecção vigia sinais que destoam do normal — como o consumo anômalo que denunciou o minerador. A observabilidade do Cap VIII vira o sentido contra o intruso.
deteccao_para_resposta.pyPython
# A DETECÇÃO alimenta a RESPOSTA (§7.5) — agora proativa, não acidental.
def ao_detectar_anomalia(anomalia: Anomalia) -> None:
    # 1) DETECTAR — já feito: a vigilância captou o sinal.
    incidente = abrir_incidente(anomalia)

    # 2) CONTER — isolar o ativo suspeito antes que o intruso avance.
    isolar(anomalia.ativo_afetado())

    # 3) RECUPERAR — restaurar a estado seguro (ex.: rebuild, §7.5).
    # 4) APRENDER — post-mortem; a anomalia vira regra de detecção nova.
    registrar_para_aprendizado(incidente)
    # Presumir a brecha torna a detecção DELIBERADA, não um acaso.
A anomalia detectada entra no ciclo de resposta do §7.5 — conter, recuperar, aprender. Presumir a brecha torna a detecção uma capacidade, não um acaso.

A Verificação

deteccao_test.pyPython
class TestDeteccao(TestCase):
    def test_comportamento_anomalo_dispara_alerta(self):
        # Um consumo de recursos muito acima do normal (ex.: minerador)...
        self.simular_consumo_anomalo()

        # ...é detectado e gera um alerta de segurança.
        self.assertTrue(self.houve_alerta_de_seguranca())

    def test_acesso_atipico_e_sinalizado(self):
        # Acesso a um recurso que aquele usuário nunca toca é anômalo.
        self.simular_acesso_atipico()
        self.assertTrue(self.foi_sinalizado_como_anomalia())
Os testes provam a detecção: um consumo anômalo (como o de um minerador) e um acesso atípico disparam alerta — o intruso que destoa do normal é captado.
Tabela de decisão Só prevenir vs presumir a brecha
AspectoConfiar só na prevençãoPresumir a brecha
Intruso já dentroInvisívelDetectado por anomalia
Tempo de comprometimentoIndefinidoCurto (detecção)
Como se descobrePor acaso, tardeVigilância contínua
PosturaFalsa segurançaRealista, vigilante
Observabilidade (Cap VIII)Só para operarTambém para vigiar

Estudo de Caso

Caso aplicado

Detectar o intruso pela anomalia que ele cria

Contexto. A plataforma investira fortemente em prevenção (Cap VII, §12.1–§12.3), mas operar sob a premissa de que a prevenção bastaria deixaria um ponto cego: nenhuma capacidade de perceber um atacante que tivesse, apesar de tudo, conseguido entrar.

Só prevenir
Intruso invisível, cego dentro
Presumir a brecha
Anomalia denuncia o intruso
Ganho
Detecção deliberada, não acaso

Decisão. Adotar a postura de presumir a brecha: usar a observabilidade do Capítulo VIII também para segurança, vigiando continuamente o comportamento do sistema em busca de anomalias — consumo de recursos fora do esperado, acessos atípicos, padrões incomuns — que traíssem a presença de um intruso, alimentando o ciclo de resposta do §7.5.

Resultado. O comprometimento por um minerador de criptomoedas — narrado no §7.5 — é a prova viva desse princípio: o que o denunciou foi exatamente um consumo de recursos anômalo, uma métrica fora do esperado captada pela observabilidade, incompatível com a carga normal do servidor. A presença do atacante traiu-se pelo comportamento, e a vigilância a captou, disparando a contenção, a reconstrução e o aprendizado. Presumir a brecha transformou a detecção de uma descoberta acidental numa capacidade deliberada: o intruso deixou de poder contar com a cegueira do defensor.

Incidente Real

Severidade · Crítica

O minerador detectado pelo consumo anômalo

  • Detecção Um servidor passou a apresentar consumo de recursos muito acima do normal para a sua carga — um sinal anômalo captado pela observabilidade (Cap VIII), incompatível com a operação esperada.
  • Diagnóstico O consumo anômalo era a assinatura de um minerador de criptomoedas em execução: o atacante havia comprometido o servidor, e a presença dele traiu-se pelo comportamento fora do padrão.
  • Contenção Detectada a anomalia, o servidor comprometido foi isolado para impedir que o atacante avançasse — o ciclo de resposta (§7.5) acionado pela detecção.
  • Recuperação O servidor foi reconstruído a partir de uma base limpa e segura, restaurando-o a um estado confiável.
  • Kaizen Vira regra: presumir a brecha; a observabilidade vigia comportamento anômalo, e a detecção de um intruso é deliberada, alimentando a resposta — não uma descoberta por acaso.

Decisão Arquitetural

ADR-0064

Presumir a brecha: detecção por comportamento anômalo, alimentando a resposta

Aceito
Contexto

Toda prevenção falha eventualmente (§12.1–§12.3 reduzem o risco, não o eliminam). Quem confia apenas na prevenção fica cego para o intruso que já entrou — que se move livremente e permanece por tempo indefinido, porque ninguém vigia de dentro. A falsa segurança impede a detecção que pegaria o que escapou.

Decisão

Presume-se a brecha: parte-se de que um atacante pode estar dentro e vigia-se continuamente o comportamento anômalo (consumo, acessos e padrões fora do normal) usando a observabilidade do Cap VIII voltada para segurança. A anomalia detectada alimenta o ciclo de resposta (§7.5): conter, recuperar, aprender. A detecção vira capacidade deliberada, não acaso (como o minerador, denunciado pelo consumo anômalo).

Alternativas consideradas

Confiar só na prevenção — cego para o intruso interno. Detecção só por efeito visível — tarde, por acaso. Vigiar sem responder — detecta mas não contém.

Consequências
PositivasIntruso interno detectado pela anomalia. Comprometimento curto, não indefinido. Detecção deliberada e contínua. Observabilidade vira sentido de segurança.
NegativasExige instrumentar a vigilância de anomalias e definir o que é "normal" para detectar o desvio.
RiscosDetecção por anomalia gera falsos positivos e não pega tudo; mitigada por calibragem e por combinar com a prevenção (12.1–12.3) e a postura verificável (12.5).

Antipadrões

Confiar que a prevenção é suficiente

Operar sem detecção, presumindo que nada entrou. O intruso interno fica invisível. Correção: presumir a brecha; vigiar o comportamento anômalo.

Detectar só pelo efeito visível

Perceber o comprometimento apenas quando ele causa um dano evidente — tarde. Correção: vigiar a anomalia que precede o efeito, captada pela observabilidade (Cap VIII).

Exercício

Construa os olhos de dentro de casa

  1. Pergunte-se honestamente: se um atacante estivesse no seu sistema agora, você saberia? Que sinal o denunciaria? Ou ele se moveria invisível?
  2. Pense no comportamento normal: o que é "normal" no seu sistema (consumo de recursos, padrões de acesso, horários)? Sem saber o normal, não se reconhece o anômalo.
  3. Reconsidere a sua observabilidade (Cap VIII): as métricas e registros que você tem para operar também serviriam para vigiar — para captar um consumo anômalo, um acesso atípico?
  4. Esboce a ponte com a resposta: quando uma anomalia for detectada, o que acontece? Ela dispara o ciclo de conter-recuperar-aprender (§7.5), ou só gera um alerta que ninguém trata?
Meta: construir a capacidade de detectar um intruso já dentro — presumindo a brecha e vigiando o comportamento anômalo —, para que a segurança não dependa da premissa falsa de que a prevenção conteve tudo.
✓ Checklist do tópico
  • A postura presume que um atacante pode já estar dentro (assume breach)
  • O comportamento anômalo (consumo, acessos, padrões) é vigiado continuamente
  • A observabilidade do Cap VIII é usada também para detecção de segurança
  • Uma anomalia detectada alimenta o ciclo de resposta (§7.5): conter, recuperar, aprender
  • A detecção é uma capacidade deliberada, não uma descoberta acidental
Meta Dados · Engenharia de Plataformas · Capítulo XII · §12.4Próximo → 12.5 Postura Verificável
Capítulo XII · Security Engineering
12.5

Postura Verificável

Uma defesa que ninguém testa é uma esperança, não uma garantia. Controles silenciam sem avisar — a cifra é desligada num ajuste, uma porta se abre, a cobertura de monitoramento cai —, e presume-se uma segurança que derivou. A postura precisa ser provada continuamente, não presumida.

Pré-requisitos · 12.1–12.4 · Cap VII §7.5 · Cap VIII · §9.4 · §10.4Conecta com · encerra o Cap XIITempo de leitura · ~13 min

O Problema

Este volume construiu uma postura de segurança robusta: modelagem de ameaças (§12.1), camadas (§12.2), cadeia de suprimentos governada (§12.3), detecção do intruso (§12.4) — somadas às fundações do Capítulo VII. Mas há uma pergunta que decide se toda essa segurança é real ou ilusória: como você sabe que ela continua funcionando? Um controle de segurança não é uma estátua imutável; ele vive num sistema que muda constantemente, e pode, silenciosamente, parar de funcionar sem que ninguém perceba. A premissa perigosa é presumir que, porque um controle foi implementado um dia, ele continua ativo e eficaz hoje.

Essa premissa quebra de mil formas, todas silenciosas. A criptografia de um dado sensível (§7.3) é acidentalmente desligada num ajuste de configuração feito por outro motivo. Uma porta que deveria estar fechada é aberta numa mudança de infraestrutura e esquecida aberta. Uma permissão é afrouxada "temporariamente" para resolver um problema e nunca restaurada, abrindo um buraco no "negar por padrão" (§7.1). Um segredo vaza para um lugar onde não deveria estar (§7.2). Uma dependência ganha uma vulnerabilidade que a varredura não está mais rodando para pegar (§12.3). Em cada caso, o controle derivou — saiu silenciosamente do estado seguro — e ninguém foi avisado, porque a segurança estava sendo presumida, não verificada. E há um caso especialmente insidioso, que é a forma mais perigosa de todas: a própria capacidade de perceber problemas degrada. A cobertura de monitoramento cai — silenciosamente, por uma falha de configuração, por um agente que parou — e cria uma falsa sensação de saúde: tudo parece bem, os alertas estão silenciosos, mas apenas porque nada está olhando. O silêncio é interpretado como segurança, quando na verdade é cegueira. Uma defesa que ninguém testa é, no fundo, apenas uma esperança de que ainda funcione — e construir a segurança de um sistema sobre esperanças não verificadas é construir sobre uma fundação que pode ter desmoronado sem fazer barulho. O atacante não precisa derrotar o seu controle de segurança se o seu controle de segurança já se desligou sozinho e ninguém percebeu.

A segurança presumida que derivou em silêncio

Controles silenciam sem avisar — a cifra é desligada, uma porta abre, a cobertura de monitoramento cai criando falsa saúde. Uma defesa que ninguém testa é só uma esperança. Correção: provar a postura continuamente — os controles são testados, e a deriva é detectada.

A Decisão

A postura de segurança é verificável: os controles são testados continuamente — o "negar por padrão" barra, a cifra está ligada, nenhum segredo vazou, as dependências estão limpas, a cobertura de monitoramento está intacta. A segurança vira uma garantia provada, não uma esperança presumida.

A plataforma trata a segurança como tudo o mais no livro: algo que se prova, não se presume. Assim como uma decisão não comprovada é só uma esperança (a tese da Verificação que percorre cada tópico), um controle de segurança não testado é só uma esperança de que ainda funcione. Por isso, a postura de segurança é continuamente verificada por testes automáticos que confirmam que cada controle está, de fato, ativo e eficaz agora — não que foi implementado um dia. Esses testes são variados e concretos. Verifica-se que o "negar por padrão" (§7.1) ainda barra: tenta-se ativamente um acesso que deveria ser negado, e confirma-se que é. Verifica-se que a criptografia (§7.3) está ligada: confirma-se que os dados sensíveis estão de fato cifrados em repouso, não em texto puro por um ajuste acidental. Verifica-se que nenhum segredo vazou (§7.2): varre-se o código e a configuração à procura de segredos que não deveriam estar lá. Verifica-se que as dependências estão limpas (§12.3): a varredura de vulnerabilidades roda como portão (§10.4). E — crucialmente, dado o caso da saúde falsa — verifica-se que a própria cobertura de monitoramento está intacta: confirma-se ativamente que o monitoramento está funcionando e cobrindo o que deveria, de modo que o silêncio só possa ser interpretado como segurança quando se provou que os olhos estão abertos. Essas verificações não são uma auditoria anual, desconectada e tardia; são contínuas, integradas aos portões de entrega (§10.4) e à operação, no mesmo espírito das avaliações que verificam a IA (§9.4) e das checagens de hardening (§7.5). A consequência é que a deriva é detectada: no instante em que um controle silenciosamente sai do estado seguro — a cifra desligou, a porta abriu, a cobertura caiu —, a verificação contínua falha, e o problema é revelado imediatamente, em vez de permanecer uma vulnerabilidade silenciosa à espera de um atacante. A segurança deixa de ser uma esperança frágil de que tudo continue como foi um dia, e torna-se uma garantia testada, renovada a cada verificação. É a postura honesta que fecha o volume e ecoa o livro inteiro: não se acredita que o sistema está seguro — prova-se que está, continuamente, e o dia em que deixa de estar é o dia em que se descobre, não anos depois.

Arquitetura lógica Provar a postura, detectar a deriva
PRESUMIR a segurançao controle silenciou sem avisarfalsa sensação de saúde PROVAR a segurançatestes contínuos dos controlesa deriva é detectada negar por padrãoainda funciona? cifra ligada?§7.3 segredo vazado?§7.2 deps limpas?§12.3 a segurança é uma garantia testada, não uma esperança cobertura de monitoramento que cai sem avisar = saúde falsa
Figura 12.5-a. Presumir a segurança deixa a deriva silenciosa passar (o controle desliga, a cobertura cai → saúde falsa). A postura verificável testa os controles continuamente — negar por padrão, cifra, segredos, dependências, cobertura — e detecta a deriva na hora.

Testar continuamente que os controles funcionam

postura_verificavel.pyPython
# PROVAR a postura: testar que cada controle funciona AGORA (não um dia).
def verificar_postura() -> Relatorio:
    checagens = [
        # O "negar por padrão" (§7.1) AINDA barra? Tenta e confirma.
        afirmar(acesso_proibido_e_negado(),     "negar por padrão falhou"),
        # A cifra (§7.3) está LIGADA? Dado sensível está cifrado em repouso?
        afirmar(dados_sensiveis_cifrados(),     "cifra desligada!"),
        # Nenhum SEGREDO (§7.2) vazou para o código/config?
        afirmar(nenhum_segredo_no_codigo(),     "segredo vazado!"),
        # As DEPENDÊNCIAS (§12.3) estão limpas?
        afirmar(dependencias_sem_vulneravel(),  "dependência vulnerável!"),
        # A COBERTURA de monitoramento está intacta? (contra a saúde falsa)
        afirmar(monitoramento_cobrindo_tudo(),  "cobertura caiu — saúde falsa!"),
    ]
    return Relatorio(checagens)   # qualquer falha = deriva detectada
Cada controle é testado ativamente agora: negar por padrão, cifra, segredos, dependências e — contra a saúde falsa — a própria cobertura de monitoramento.
deriva_detectada.shBash
# A verificação é CONTÍNUA (portão §10.4 + operação), não auditoria anual.
verificar-postura || bloquear-entrega "postura de segurança falhou"

# No instante em que um controle DERIVA, a verificação falha e avisa:
#   cifra desligada num ajuste?      → detectado AGORA
#   porta aberta e esquecida?        → detectado AGORA
#   cobertura de monitoramento caiu? → detectado AGORA (sem saúde falsa)

# A segurança é uma garantia TESTADA, renovada a cada verificação —
# não uma esperança de que tudo continue como foi um dia.
A verificação contínua roda como portão e na operação. A deriva — cifra desligada, porta aberta, cobertura caída — é detectada na hora, não anos depois.

A Verificação

postura_test.pyPython
class TestPosturaVerificavel(TestCase):
    def test_deriva_de_controle_e_detectada(self):
        # Simula a cifra sendo desligada silenciosamente (deriva).
        self.desligar_cifra_acidentalmente()

        # A verificação de postura FALHA — a deriva foi detectada.
        self.assertFalse(verificar_postura().passou())

    def test_queda_de_cobertura_e_detectada(self):
        # O caso da saúde falsa: a cobertura de monitoramento cai.
        self.derrubar_cobertura_de_monitoramento()
        # A verificação detecta — o silêncio NÃO é confundido com saúde.
        self.assertFalse(verificar_postura().passou())
Os testes provam o essencial: a deriva de um controle (cifra desligada) é detectada, e — crucial — a queda da cobertura de monitoramento também, evitando a saúde falsa.
Tabela de decisão Segurança presumida vs verificável
AspectoPresumir a segurançaPostura verificável
Controle desligadoPassa despercebidoVerificação falha
Cobertura de monitoramento caiFalsa sensação de saúdeDetectada na hora
Quando se sabe da derivaTarde, ou nuncaImediatamente
FrequênciaAuditoria anualContínua (§10.4)
A segurança éUma esperançaUma garantia testada

Estudo de Caso

Caso aplicado

A cobertura que caiu e criou uma saúde falsa

Contexto. A plataforma dependia de um sistema de monitoramento interno para perceber problemas — inclusive de segurança. Presumir que esse monitoramento estava sempre funcionando, sem verificá-lo, criava um risco sutil: se a cobertura caísse, o silêncio resultante seria confundido com saúde.

Segurança presumida
Cobertura cai, saúde falsa
Postura verificável
Deriva detectada na hora
Ganho
Garantia testada, não esperança

Decisão. Tornar a postura verificável: testar continuamente não só os controles de segurança (negar por padrão §7.1, cifra §7.3, segredos §7.2, dependências §12.3), mas também a própria cobertura de monitoramento — confirmar ativamente que os olhos estão abertos —, integrando essas verificações aos portões (§10.4) e à operação.

Resultado. O caso real em que a cobertura de monitoramento caiu significativamente — criando uma falsa sensação de saúde operacional, em que tudo parecia bem porque nada estava olhando — é exatamente o que a postura verificável evita. Com a verificação contínua, uma queda de cobertura passa a ser detectada, em vez de interpretada como silêncio tranquilo. E qualquer outra deriva — uma cifra desligada, uma porta aberta — falha a verificação na hora. A segurança deixou de ser uma esperança de que tudo continuasse como foi um dia e tornou-se uma garantia testada, renovada a cada verificação. O dia em que um controle deriva passou a ser o dia em que se descobre.

Incidente Real

Severidade · Alta

A queda de cobertura que fingiu que estava tudo bem

  • Antes A cobertura de um sistema de monitoramento interno caiu significativamente, por uma falha que não foi notada — porque a própria cobertura não era verificada.
  • Sintoma Criou-se uma falsa sensação de saúde operacional: como os alertas estavam silenciosos, presumiu-se que tudo estava bem, quando na verdade o silêncio se devia a que o monitoramento havia deixado de cobrir o que deveria — ninguém estava olhando.
  • Diagnóstico A cobertura de monitoramento era presumida, não verificada; sua queda passou despercebida, e o silêncio foi confundido com segurança.
  • Correção A postura passou a ser verificada continuamente, incluindo a própria cobertura de monitoramento — uma queda de cobertura passou a ser detectada, em vez de interpretada como saúde.
  • Kaizen Vira regra: a postura de segurança e a cobertura de monitoramento são provadas continuamente; o silêncio só é segurança quando se verificou que os olhos estão abertos.

Decisão Arquitetural

ADR-0065

Postura de segurança verificável continuamente, contra a deriva silenciosa

Aceito
Contexto

Um controle de segurança pode silenciosamente parar de funcionar — a cifra é desligada, uma porta abre, uma permissão é afrouxada, a cobertura de monitoramento cai criando falsa saúde. Uma defesa que ninguém testa é só uma esperança, e presumir a segurança deixa a deriva passar despercebida até um atacante a explorar.

Decisão

A postura de segurança é provada, não presumida: testes automáticos contínuos confirmam que cada controle está ativo e eficaz agora — o negar por padrão (§7.1) barra, a cifra (§7.3) está ligada, nenhum segredo (§7.2) vazou, as dependências (§12.3) estão limpas e, crucialmente, a cobertura de monitoramento está intacta (contra a saúde falsa). As verificações são contínuas, integradas aos portões (§10.4), e detectam a deriva na hora.

Alternativas consideradas

Presumir a segurança — deriva silenciosa passa. Auditoria anual — tarde e desconectada. Verificar os controles mas não a cobertura — a saúde falsa permanece possível.

Consequências
PositivasDeriva detectada imediatamente. Saúde falsa evitada (a cobertura é verificada). Segurança vira garantia testada, renovada a cada verificação. Honestidade no lugar da esperança.
NegativasExige construir e manter as verificações contínuas de cada controle e da cobertura.
RiscosAs verificações precisam acompanhar os controles existentes (uma verificação esquecida deixa um ponto cego); mitigado por tratá-las como parte de cada controle.

Antipadrões

Presumir que os controles continuam ativos

Acreditar que um controle implementado um dia segue funcionando, sem testá-lo. A deriva silenciosa passa. Correção: provar a postura continuamente; a deriva falha a verificação.

Confundir silêncio com segurança

Interpretar a ausência de alertas como saúde, sem verificar se o monitoramento está cobrindo. Correção: verificar a própria cobertura — o silêncio só é segurança com os olhos provadamente abertos.

Exercício

Prove a postura do seu sistema

  1. Pegue um controle de segurança seu (a criptografia de um dado, uma regra de acesso). Como você sabe que ele está funcionando agora? Você testa, ou presume?
  2. Faça o teste da deriva: se esse controle fosse acidentalmente desligado num ajuste de configuração, quanto tempo levaria para alguém perceber? Algo falharia, ou passaria despercebido?
  3. Faça o teste da saúde falsa: se a sua cobertura de monitoramento caísse, você saberia? Ou o silêncio resultante seria confundido com "está tudo bem"?
  4. Esboce uma verificação contínua: que teste automático confirmaria, a cada entrega (§10.4), que os seus controles — e a sua cobertura — continuam ativos e eficazes?
Meta: transformar a segurança do seu sistema de uma esperança presumida numa garantia verificável — testando continuamente que os controles funcionam e que a cobertura está intacta, para que a deriva seja detectada na hora, não anos depois.
✓ Checklist do tópico
  • Os controles de segurança são testados continuamente, não presumidos
  • Verifica-se ativamente que o negar por padrão (§7.1) ainda barra um acesso proibido
  • Verifica-se que a cifra (§7.3) está ligada e nenhum segredo (§7.2) vazou
  • Verifica-se que a própria cobertura de monitoramento está intacta (contra a saúde falsa)
  • As verificações são contínuas (§10.4) e detectam a deriva de um controle na hora
Meta Dados · Engenharia de Plataformas · Capítulo XII · §12.5Fim do Capítulo XII · voltar ao início
Meta Dados · Engenharia de Plataformas
Capítulo XIII

SRE e
Operações
Globais

Confiabilidade como disciplina de engenharia, não como heroísmo: medir o que os usuários sentem, gastar a falha que se pode pagar, eliminar o trabalho repetido, operar entre fusos sem ninguém de prontidão eterna e sobreviver à queda de uma região inteira.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo XIII
Capítulo XIII · SRE e Operações Globais
13.1

SLO e Orçamento de Erro

Perseguir 100% de confiabilidade é uma armadilha: é impossível, custa o infinito e paralisa a evolução. Mas não ter meta nenhuma é voar às cegas. A confiabilidade vira disciplina quando se mede o que o usuário sente e se decide, conscientemente, quanta falha se pode pagar.

Pré-requisitos · Cap VIII Observabilidade · §8.2 SLOs · Cap X §10.4Conecta com · 13.2 Toil · 13.3 PlantãoTempo de leitura · ~14 min

O Problema

"Quão confiável o sistema precisa ser?" parece uma pergunta com resposta óbvia: o máximo possível, 100%, sempre no ar. Essa resposta intuitiva é uma armadilha. Perseguir 100% de confiabilidade é perseguir um alvo impossível — nenhum sistema, por melhor que seja, nunca falha — e, pior, o custo de cada "nove" adicional de confiabilidade (de 99% para 99,9% para 99,99%) cresce exponencialmente, tendendo ao infinito conforme se aproxima do inatingível 100%. Uma organização que persegue a perfeição gasta recursos desproporcionais para ganhos marginais e, sobretudo, paralisa: como toda mudança traz risco, a busca por confiabilidade absoluta leva ao medo de mudar, e o sistema deixa de evoluir.

Mas o extremo oposto é igualmente desastroso. Operar sem nenhuma meta de confiabilidade é voar às cegas: não há como dizer se o sistema está "confiável o suficiente", não há critério para decidir quando priorizar estabilidade sobre novas funcionalidades, não há linguagem comum entre quem constrói (que quer entregar) e quem opera (que quer estabilidade). As discussões viram opiniões — "está instável demais" contra "está bom o suficiente" — sem nada objetivo para resolvê-las. E há um problema mais sutil em ambos os extremos: a confiabilidade costuma ser medida pela coisa errada. Mede-se o uso de CPU, a saúde dos servidores, métricas internas — quando o que importa é o que o usuário sente. Um sistema pode ter todos os servidores "saudáveis" e ainda assim estar falhando para os usuários (lento, com erros), e pode ter um servidor sobrecarregado sem que nenhum usuário perceba. Medir a saúde interna em vez da experiência do usuário é medir a coisa errada — e otimizar a coisa errada não torna o sistema melhor para quem o usa. O problema de fundo é a ausência de uma disciplina: a confiabilidade é tratada como uma aspiração vaga (ser o mais confiável possível) ou um sentimento (parece estável), em vez de uma grandeza medida no que importa e gerida com decisões conscientes.

Os dois extremos errados

Perseguir 100% é impossível e paralisa a evolução; não ter meta é voar às cegas. E medir a saúde interna em vez do que o usuário sente é otimizar a coisa errada. Correção: uma meta de confiabilidade (SLO) sobre a experiência do usuário, com um orçamento de erro que governa a tensão entre evoluir e estabilizar.

A Decisão

Define-se um SLO — uma meta de confiabilidade sobre o que o usuário sente, alta mas não absoluta (99,9%, não 100%). O complemento é o orçamento de erro: a falha que se pode pagar. Enquanto há orçamento, evolui-se; quando esgota, freia-se a mudança e foca-se a estabilidade.

A plataforma trata a confiabilidade como uma disciplina de engenharia, fundada em dois conceitos que se complementam. O primeiro é o SLO (objetivo de nível de serviço): uma meta explícita e numérica de confiabilidade, definida sobre o que o usuário sente — não a saúde interna dos servidores, mas a experiência real: o sistema responde às requisições com sucesso? Responde rápido o bastante? Um SLO típico afirma algo como "99,9% das requisições terão sucesso dentro de um limite de latência, medido ao longo de um período". Crucialmente, o alvo é alto mas não absoluto: 99,9%, não 100% — porque os 0,1% restantes reconhecem, honestamente, que a falha é inevitável e que perseguir a perfeição seria a armadilha. O segundo conceito é o que transforma o SLO de uma simples meta numa ferramenta de decisão: o orçamento de erro. Se o SLO é 99,9%, então 0,1% de falha é permitido — e esse 0,1% é um orçamento, uma quantidade de falha que se pode "gastar". O orçamento de erro resolve a tensão central entre evoluir (que traz risco) e estabilizar (que trava). Enquanto há orçamento de erro disponível — o sistema está dentro do SLO —, pode-se gastar esse orçamento em mudança: lançar funcionalidades, correr riscos calculados, evoluir, porque há margem de falha para absorver os eventuais tropeços. Mas quando o orçamento esgota — o sistema está consumindo sua cota de falha, ameaçando furar o SLO —, a prioridade automaticamente muda: freia-se a mudança e foca-se a estabilidade, até que o sistema volte a estar confortavelmente dentro do SLO e o orçamento se recomponha. Isso dá uma resposta objetiva à pergunta "devemos lançar esta funcionalidade ou estabilizar o sistema?": olha-se o orçamento de erro. Há orçamento? Lança. Esgotou? Estabiliza. A discussão deixa de ser opinião ("acho que está instável") e passa a ser dado ("o orçamento de erro está em 8%"). E a meta deixa de ser a perfeição impossível ou a ausência paralisante, e passa a ser um número honesto sobre a experiência do usuário, que governa conscientemente o equilíbrio entre o impulso de construir e a necessidade de durar — exatamente a tensão que o Manifesto reconhece. A confiabilidade vira engenharia: medida no que importa, e gerida com decisões fundamentadas, não com heroísmo nem com sentimento.

Diagrama conceitual SLO sobre a experiência, orçamento de erro como decisão
PERSEGUIR 100%impossível e paralisanteou: nenhuma meta = às cegas SLO: a meta certamedir o que o usuário SENTE99,9% — não 100% ORÇAMENTO DE ERRO = 100% − SLOa falha que se PODE pagar — gasta-se com consciência orçamento esgotado? freia a mudança, foca a estabilidade
Figura 13.1-a. Perseguir 100% paralisa; nenhuma meta é cegueira. O SLO mede o que o usuário sente (99,9%, não 100%), e o orçamento de erro (100%−SLO) governa a tensão: há orçamento, evolui-se; esgotou, estabiliza-se.

O SLO sobre o que o usuário sente

slo.pyPython
# O SLO mede o que o USUÁRIO sente — não a saúde interna do servidor.
SLO = ObjetivoDeNivelDeServico(
    indicador="requisições com sucesso e rápidas",  # experiência
    alvo=0.999,        # 99,9% — ALTO, mas não 100% (a perfeição é armadilha)
    janela="28 dias",   # medido ao longo do tempo
)

# ERRADO: medir CPU/saúde do servidor (não é o que o usuário sente).
# CERTO: medir o sucesso e a latência das requisições reais.
def conformidade(reqs) -> float:
    boas = [r for r in reqs if r.sucesso and r.latencia < LIMITE]
    return len(boas) / len(reqs)   # o que o usuário de fato experimenta
O SLO é definido sobre a experiência real — sucesso e latência das requisições —, não sobre métricas internas. O alvo é alto (99,9%) mas honestamente não absoluto.
orcamento_de_erro.pyPython
# ORÇAMENTO DE ERRO = a falha que se PODE pagar (100% − SLO).
def orcamento_restante(conformidade_atual: float) -> float:
    permitido = 1 - SLO.alvo          # 0,1% de falha é o orçamento
    gasto = 1 - conformidade_atual   # quanto já se gastou
    return (permitido - gasto) / permitido   # fração restante

# A DECISÃO objetiva: evoluir ou estabilizar?
def pode_lancar_mudanca(conformidade_atual: float) -> bool:
    if orcamento_restante(conformidade_atual) > 0:
        return True    # há orçamento → gasta-se em evolução
    # Orçamento esgotado → freia a mudança, foca a estabilidade.
    return False      # não é opinião — é o orçamento que decide
O orçamento de erro transforma o SLO em decisão: enquanto há orçamento, evolui-se; quando esgota, estabiliza-se. A escolha vira dado, não opinião.

A Verificação

slo_test.pyPython
class TestSloEOrcamento(TestCase):
    def test_orcamento_disponivel_permite_evoluir(self):
        # Sistema confortavelmente dentro do SLO → há orçamento → evolui.
        self.assertTrue(pode_lancar_mudanca(conformidade_atual=0.9995))

    def test_orcamento_esgotado_freia_a_mudanca(self):
        # Sistema furando o SLO → orçamento esgotado → estabiliza.
        self.assertFalse(pode_lancar_mudanca(conformidade_atual=0.998))

    def test_slo_mede_experiencia_nao_cpu(self):
        # Servidor com CPU alta mas requisições boas → SLO cumprido.
        self.cpu_alta_mas_requisicoes_boas()
        self.assertTrue(self.slo_cumprido())  // mede o usuário, não a CPU
Os testes fixam a disciplina: há orçamento, evolui-se; esgotou, estabiliza-se; e o SLO reflete a experiência do usuário, não a saúde interna do servidor.
Tabela de decisão Sem disciplina vs SLO e orçamento de erro
AspectoSem disciplinaSLO + orçamento de erro
Meta de confiabilidade100% (ou nenhuma)Alta, não absoluta
O que se medeSaúde interna (CPU)O que o usuário sente
Evoluir ou estabilizar?Opinião, brigaO orçamento decide
FalhaInadmissível (paralisa)Orçada, gasta com consciência
Confiabilidade éAspiração / sentimentoDisciplina medida

Estudo de Caso

Caso aplicado

Quando "está estável o suficiente?" ganhou uma resposta objetiva

Contexto. Na plataforma, decidir entre lançar novas funcionalidades e estabilizar o sistema era uma fonte recorrente de tensão sem critério objetivo — quem construía queria entregar, quem operava queria estabilidade, e a discussão girava em opiniões. Além disso, a confiabilidade era avaliada por métricas internas que nem sempre refletiam a experiência real dos usuários.

Sem critério
Opinião, métrica errada
SLO + orçamento
Decisão objetiva, mede o usuário
Ganho
Confiabilidade como disciplina

Decisão. Adotar a disciplina de SRE: definir SLOs sobre o que o usuário sente (sucesso e latência das requisições), com alvos altos mas não absolutos (99,9%), e derivar deles um orçamento de erro que governa a tensão — enquanto há orçamento, evolui-se; quando esgota, freia-se a mudança e foca-se a estabilidade.

Resultado. A pergunta "devemos lançar ou estabilizar?" ganhou uma resposta objetiva: olha-se o orçamento de erro. A discussão deixou de ser opinião e passou a ser dado, alinhando quem constrói e quem opera em torno de um número comum. E porque o SLO mede a experiência do usuário, não a saúde interna, a plataforma passou a otimizar o que de fato importa para quem a usa. A confiabilidade deixou de ser uma aspiração vaga ou um sentimento e tornou-se uma disciplina medida e gerida — nem a perfeição paralisante, nem a cegueira.

Incidente Real

Severidade · Média

O servidor "saudável" que falhava para o usuário

  • Antes A confiabilidade era avaliada por métricas internas de saúde dos servidores; presumia-se que servidores saudáveis significavam usuários satisfeitos.
  • Sintoma Houve momentos em que os servidores apareciam "saudáveis" pelas métricas internas, mas os usuários experimentavam lentidão ou erros — a métrica interna não capturava a experiência real, criando um descompasso entre o que se media e o que importava.
  • Diagnóstico Medir a saúde interna em vez do que o usuário sente fazia a confiabilidade ser avaliada pela coisa errada, mascarando problemas reais de experiência.
  • Correção Adoção de SLOs definidos sobre a experiência do usuário (sucesso e latência das requisições), tornando a medida da confiabilidade fiel ao que os usuários de fato experimentam.
  • Kaizen Vira regra: a confiabilidade é medida pelo que o usuário sente, via SLOs, e gerida por orçamento de erro — não por métricas internas que não refletem a experiência.

Decisão Arquitetural

ADR-0066

Confiabilidade gerida por SLO (na experiência do usuário) e orçamento de erro

Aceito
Contexto

Perseguir 100% de confiabilidade é impossível, custa o infinito e paralisa a evolução; não ter meta é voar às cegas, sem critério para equilibrar evoluir e estabilizar. E medir a saúde interna (CPU, servidores) em vez do que o usuário sente é otimizar a coisa errada.

Decisão

A confiabilidade é gerida por SLOs — metas explícitas e numéricas sobre a experiência do usuário (sucesso, latência), altas mas não absolutas (99,9%, não 100%) — e por orçamento de erro (100% − SLO): a falha que se pode pagar. Enquanto há orçamento, evolui-se; quando esgota, freia-se a mudança e foca-se a estabilidade. A decisão vira dado, não opinião.

Alternativas consideradas

Perseguir 100% — impossível, paralisa. Nenhuma meta — cegueira, briga de opinião. Medir saúde interna — otimiza a coisa errada, mascara a experiência.

Consequências
PositivasMeta honesta sobre o que o usuário sente. Decisão objetiva entre evoluir e estabilizar. Alinhamento entre construir e operar. Falha orçada, gasta com consciência.
NegativasExige instrumentar a experiência do usuário e a disciplina de respeitar o orçamento de erro.
RiscosSLOs mal escolhidos medem o irrelevante; mitigado por defini-los a partir do que o usuário realmente sente e revisá-los.

Antipadrões

Perseguir 100% de confiabilidade

Tratar qualquer falha como inadmissível. Impossível, custa o infinito e paralisa a evolução. Correção: um SLO alto mas não absoluto, com orçamento de erro para a falha inevitável.

Medir a saúde interna em vez da experiência

Avaliar a confiabilidade por CPU e servidores "saudáveis". Pode mascarar falhas reais para o usuário. Correção: o SLO mede o que o usuário sente.

Exercício

Defina o SLO do seu sistema

  1. Pergunte: como você sabe hoje se o seu sistema está "confiável o suficiente"? Há uma meta numérica, ou é um sentimento? E ela mede a saúde interna ou a experiência do usuário?
  2. Escolha um indicador do que o usuário sente: a fração de requisições que têm sucesso e respondem dentro de um limite de latência. Esse é o candidato a SLO.
  3. Defina um alvo honesto: alto, mas não 100% (ex.: 99,9%). Os "noves" que faltam são o reconhecimento de que a falha é inevitável — e o seu orçamento de erro.
  4. Use o orçamento para decidir: da próxima vez que houver tensão entre lançar algo e estabilizar, em vez de discutir opiniões, pergunte — há orçamento de erro disponível?
Meta: definir um SLO sobre a experiência do usuário e derivar dele um orçamento de erro — para que a confiabilidade do seu sistema deixe de ser sentimento e a decisão entre evoluir e estabilizar deixe de ser briga.
✓ Checklist do tópico
  • Há um SLO numérico de confiabilidade, definido sobre o que o usuário sente
  • O alvo é alto mas não absoluto (ex.: 99,9%), reconhecendo a falha inevitável
  • O orçamento de erro (100% − SLO) é medido e acompanhado
  • A decisão entre evoluir e estabilizar é guiada pelo orçamento, não por opinião
  • A confiabilidade não é avaliada apenas por métricas internas (CPU, servidores)
Meta Dados · Engenharia de Plataformas · Capítulo XIII · §13.1Próximo → 13.2 A Guerra Contra o Toil
Capítulo XIII · SRE e Operações Globais
13.2

A Guerra Contra o Toil

Há um trabalho operacional que é manual, repetitivo e cresce junto com o sistema, sem deixar nada de durável: o toil. Se não for combatido, ele afoga a equipe — quanto maior o sistema, mais toil, até não sobrar tempo para melhorar o sistema. A saída é automatizar a classe, não a instância.

Pré-requisitos · 13.1 · Cap X §10.1 IaC · §10.3 AutocuraConecta com · 13.3 Plantão · 13.5 RegiãoTempo de leitura · ~14 min

O Problema

Há uma categoria insidiosa de trabalho que consome as equipes de operação: o toil. Toil é o trabalho operacional que é manual (feito à mão, repetidamente), repetitivo (a mesma tarefa, de novo e de novo), automatizável (uma máquina poderia fazê-lo), e — crucialmente — que não produz valor durável: depois de feito, não deixa nada de permanente, e terá que ser feito de novo da próxima vez. Reiniciar um serviço que trava sempre da mesma forma, aplicar manualmente a mesma correção recorrente, executar o mesmo procedimento repetido a cada incidente: tudo isso é toil. Cada instância parece pequena — "é só reiniciar", "é rápido" —, e é justamente essa aparência inofensiva que torna o toil perigoso.

O perigo do toil é que ele cresce com o sistema, numa espiral que afoga. Quanto maior e mais complexo o sistema, mais coisas há para manter manualmente, mais incidentes recorrentes para tratar à mão, mais procedimentos repetidos para executar. O toil escala junto com o que se opera — e, se nada for feito, ele consome uma fração cada vez maior do tempo da equipe. Aí está a armadilha: o tempo gasto em toil é tempo não gasto em melhorar o sistema. E como o toil cresce com o sistema, quanto mais o sistema cresce, menos tempo sobra para melhorá-lo — incluindo para automatizar o próprio toil. A equipe entra numa espiral descendente: afogada em trabalho repetitivo, sem tempo para a engenharia que reduziria esse trabalho, vendo o toil crescer mais rápido do que consegue dar conta. O resultado é uma operação puramente reativa, onde as pessoas passam os dias apagando os mesmos incêndios, executando os mesmos procedimentos, sem nunca chegar a resolver as causas que geram esse trabalho. Pior, o toil é desmoralizante: trabalho repetitivo e sem valor durável esgota as pessoas, que se sentem hamsters numa roda, correndo sem sair do lugar. Uma equipe afogada em toil não é apenas improdutiva — é uma equipe a caminho do esgotamento, presa a um trabalho que cresce sem fim e que nenhuma pessoa deveria passar a vida fazendo.

A espiral do toil

Toil é trabalho manual, repetitivo e sem valor durável que cresce com o sistema. Sem combatê-lo, ele consome o tempo da equipe — e quanto mais o sistema cresce, menos tempo sobra para melhorá-lo. Correção: automatizar a classe do problema, não a instância, eliminando o trabalho repetido de uma vez.

A Decisão

O toil é combatido sistematicamente: em vez de resolver a mesma tarefa manual repetidamente (a instância), automatiza-se a classe do problema — uma vez —, eliminando o trabalho repetido para sempre e liberando as pessoas para a engenharia que só elas fazem.

A plataforma trava uma guerra deliberada contra o toil, com um princípio orientador: quando uma tarefa manual se repete, não se resolve a instância mais uma vez — automatiza-se a classe do problema, de modo que ele nunca mais precise de trabalho manual. A diferença é profunda. Resolver a instância é reiniciar o serviço que travou — de novo. Resolver a classe é construir algo que detecta e reinicia automaticamente qualquer serviço que trave daquela forma — uma vez, e nunca mais um humano precisa fazê-lo. (A orquestração com autocura do §10.3, que recria sozinha o que cai, é precisamente isso: a automação de uma classe inteira de toil — o "reiniciar o que caiu" — eliminada de vez.) A automação, aqui, é tratada não como um luxo opcional, mas como a atividade central da engenharia de confiabilidade: cada pedaço de toil identificado é um candidato a ser eliminado por código. O princípio se manifesta em formas concretas que percorrem o livro: a infraestrutura como código (§10.1) elimina o toil de configurar servidores à mão; a autocura (§10.3) elimina o toil de reerguer o que cai; um script que detecta e corrige automaticamente uma condição recorrente elimina o toil de aplicar a mesma correção repetidamente. O resultado quebra a espiral descendente. Como o toil é eliminado em vez de apenas executado, ele não cresce sem fim com o sistema — cada classe automatizada é uma fonte de trabalho repetido que desaparece permanentemente. E o tempo que deixa de ser gasto em toil é reinvestido em mais engenharia — incluindo em automatizar mais toil —, criando uma espiral ascendente: quanto mais toil se elimina, mais tempo sobra para eliminar ainda mais e para melhorar o sistema de verdade. As pessoas são liberadas do trabalho repetitivo e desmoralizante para o trabalho que só elas podem fazer — projetar, resolver problemas novos, melhorar o sistema —, que é também o trabalho que as realiza. É a aplicação à operação do princípio que move a engenharia essencial: não aceitar o trabalho repetido como inevitável, mas eliminá-lo pela raiz, para que a inteligência humana se ocupe do que importa, e não do que uma máquina deveria fazer. A guerra contra o toil é, no fundo, a recusa de deixar pessoas viverem como hamsters numa roda quando poderiam estar construindo.

Diagrama conceitual Automatizar a classe elimina o toil de vez
TOIL: trabalho repetidomanual, cresce com o sistemaconsome o tempo, sem valor durável AUTOMATIZAR o toila máquina faz o repetidoo engenheiro faz engenharia toil cresce com o sistema → afoga, não sobra tempo p/ melhorar resolver a CLASSE do problema, não a instânciaautomatizar uma vez livra de repetir para sempre
Figura 13.2-a. O toil — trabalho manual e repetitivo — cresce com o sistema e afoga. Automatizar a classe do problema (não a instância) o elimina de vez: a máquina faz o repetido, e o engenheiro é liberado para a engenharia.

Eliminar a classe, não tratar a instância

toil_vs_automacao.pyPython
# INSTÂNCIA (toil): reiniciar À MÃO o serviço que travou — de novo.
#   $ reiniciar_servico   ← repetido a cada ocorrência, p/ sempre

# CLASSE (engenharia): automatizar o tratamento — UMA vez.
def guardiao_do_servico() -> None:
    while True:
        if servico.travou():
            # A máquina faz o que o humano fazia à mão — sem acordar ninguém.
            servico.reiniciar()
            registrar("reinício automático")   // observável (Cap VIII)
        dormir(intervalo)
    # Construído UMA vez → a classe inteira de toil desaparece.
    # (a autocura do §10.3 é exatamente esta ideia, no orquestrador)
Em vez de reiniciar o serviço à mão a cada vez (a instância), automatiza-se o tratamento da classe inteira. Construído uma vez, o toil desaparece para sempre.
medir_e_atacar_toil.pyPython
# O toil é MEDIDO e atacado — não aceito como inevitável.
def priorizar_automacao(tarefas: list[TarefaOperacional]) -> list:
    # Toil = manual + repetitivo + automatizável + sem valor durável.
    toil = [t for t in tarefas if t.eh_toil()]

    # Atacar primeiro o que mais consome tempo (maior retorno).
    return sorted(toil, key=lambda t: t.tempo_gasto_por_mes(), reverse=True)
    # Cada classe automatizada = tempo reinvestido em mais engenharia.
    # Espiral ASCENDENTE: menos toil → mais tempo → menos toil ainda.
O toil é medido e priorizado: ataca-se primeiro o que mais consome tempo. Cada classe eliminada libera tempo para eliminar mais — a espiral ascendente.

A Verificação

toil_test.pyPython
class TestGuerraContraToil(TestCase):
    def test_classe_automatizada_dispensa_humano(self):
        # O serviço trava repetidamente...
        self.fazer_servico_travar_varias_vezes()

        # ...e o guardião o reergue sozinho, sem intervenção humana.
        self.rodar_guardiao()
        self.assertTrue(self.servico_no_ar())
        self.assertEqual(self.intervencoes_humanas(), 0)  // toil eliminado

    def test_acao_automatica_e_observavel(self):
        # A automação não é silenciosa: registra o que fez (Cap VIII).
        self.rodar_guardiao()
        self.assertTrue(self.ha_registro_de_acao_automatica())
Os testes provam a eliminação do toil: a classe automatizada reergue o serviço sem humano algum, e a ação automática é observável — não uma caixa-preta.
Tabela de decisão Tratar a instância vs eliminar a classe
AspectoResolver a instância (toil)Automatizar a classe
Quem fazHumano, à mãoA máquina
FrequênciaToda vez, p/ sempreConstruído uma vez
Com o sistema crescendoToil cresce, afogaToil eliminado, não cresce
Tempo da equipeConsumido no repetidoLiberado p/ engenharia
MoralHamster na rodaConstrói o que importa

Estudo de Caso

Caso aplicado

Do reinício manual ao guardião que cuida sozinho

Contexto. Uma condição recorrente em um componente da plataforma — conexões órfãs que se acumulavam num banco legado e o degradavam — exigia intervenção manual repetida: alguém precisava, vez após vez, executar a correção para restaurar a saúde. Era toil clássico: manual, repetitivo, sem valor durável, e crescendo com o uso do sistema.

Reinício manual
Toil que cresce e afoga
Guardião automático
Classe eliminada de vez
Ganho
Tempo livre para engenharia

Decisão. Em vez de continuar tratando cada ocorrência à mão (a instância), automatizar a classe do problema: construir um guardião que detecta a condição recorrente e aplica a correção automaticamente — com lógica de proteção e registro observável (Cap VIII) —, eliminando a necessidade de intervenção humana.

Resultado. O trabalho manual repetido desapareceu: o guardião passou a cuidar da condição sozinho, sem acordar ninguém, registrando cada ação para que a operação permanecesse visível (não uma caixa-preta). O tempo que era consumido aplicando a mesma correção repetidamente foi liberado para engenharia de verdade. E, mais importante, a classe inteira de toil foi eliminada de uma vez, em vez de continuar crescendo com o sistema. A operação deu um passo da reatividade exaustiva para a automação que liberta — o engenheiro voltou a fazer engenharia.

Incidente Real

Severidade · Média

A correção manual que se repetia sem fim

  • Antes Uma condição recorrente em um banco legado — acúmulo de conexões órfãs deixadas por processos externos — degradava o sistema e exigia que alguém aplicasse manualmente a mesma correção, repetidamente.
  • Sintoma O trabalho manual se repetia indefinidamente e crescia com o uso; o tempo da equipe era consumido tratando a mesma instância do problema vez após vez, sem nunca eliminá-lo, e sem sobrar tempo para melhorias.
  • Diagnóstico O problema estava sendo resolvido na instância (cada ocorrência à mão), não na classe; era toil puro, manual e sem valor durável, escalando com o sistema.
  • Correção Construção de um guardião automatizado que detecta a condição e aplica a correção sozinho, com proteções e registro observável — eliminando a classe inteira de toil.
  • Kaizen Vira regra: trabalho operacional recorrente é automatizado na classe, não tratado na instância; o toil é medido e combatido, não aceito como inevitável.

Decisão Arquitetural

ADR-0067

Guerra contra o toil: automatizar a classe do problema, não a instância

Aceito
Contexto

Toil — trabalho operacional manual, repetitivo, automatizável e sem valor durável — cresce com o sistema. Tratá-lo na instância (à mão, toda vez) consome uma fração crescente do tempo da equipe, deixando cada vez menos tempo para melhorar o sistema, numa espiral descendente que afoga e desmoraliza.

Decisão

O toil é combatido sistematicamente: identificado e medido, é eliminado automatizando a classe do problema (uma vez) em vez de resolver a instância (toda vez). A automação é tratada como atividade central — infraestrutura como código (§10.1), autocura (§10.3), guardiões que tratam condições recorrentes —, com as ações automáticas observáveis (Cap VIII). O tempo liberado é reinvestido em mais engenharia.

Alternativas consideradas

Resolver a instância à mão — toil cresce, espiral descendente. Aceitar o toil como inevitável — equipe afogada e esgotada. Automatizar sem observabilidade — troca toil por uma caixa-preta.

Consequências
PositivasToil eliminado, não apenas executado. Espiral ascendente: menos toil, mais tempo, menos toil ainda. Equipe liberada para engenharia. Operação proativa, não reativa.
NegativasExige investir tempo em automação e a disciplina de medir e atacar o toil.
RiscosAutomação mal feita vira fonte nova de toil; mitigada por torná-la robusta, observável e testada como qualquer código.

Antipadrões

Tratar o toil na instância para sempre

Resolver a mesma tarefa manual repetidamente, sem eliminar a causa. O toil cresce e afoga. Correção: automatizar a classe do problema, de uma vez.

Aceitar o toil como parte inevitável do trabalho

Resignar-se a passar os dias em trabalho repetitivo. Correção: medir o toil e tratá-lo como inimigo a eliminar — a automação é a atividade central, não um luxo.

Exercício

Declare guerra ao seu toil

  1. Liste o trabalho operacional repetitivo da sua semana: o que você faz à mão, de novo e de novo, que uma máquina poderia fazer e que não deixa nada de durável? Esse é o seu toil.
  2. Meça: quanto do seu tempo (ou da sua equipe) é consumido por esse trabalho repetido? E ele cresce conforme o sistema cresce?
  3. Para o maior item, distinga instância de classe: você está resolvendo cada ocorrência à mão, ou poderia automatizar o tratamento da classe inteira de uma vez?
  4. Esboce a automação: o que detectaria a condição e a trataria sozinho? Garanta que a ação seja observável (Cap VIII), para não trocar o toil por uma caixa-preta.
Meta: identificar e medir o toil que consome o seu tempo — e planejar automatizar a classe do problema, não a instância, para escapar da espiral descendente e liberar-se para a engenharia que importa.
✓ Checklist do tópico
  • O trabalho operacional repetitivo (toil) é identificado e medido
  • O toil é eliminado automatizando a classe do problema, não tratando a instância
  • A automação é tratada como atividade central, não como um luxo opcional
  • As ações automáticas são observáveis (Cap VIII), não uma caixa-preta
  • O tempo liberado do toil é reinvestido em melhorar o sistema
Meta Dados · Engenharia de Plataformas · Capítulo XIII · §13.2Próximo → 13.3 Plantão Sustentável
Capítulo XIII · SRE e Operações Globais
13.3

Plantão Sustentável

Alguém precisa estar de prontidão quando o sistema falha — mas um plantão mal feito destrói a pessoa que o carrega. Alarmes demais, ruído que não exige ação, páginas às três da manhã que uma máquina deveria ter resolvido: o caminho do esgotamento. O plantão precisa ser sustentável, ou ninguém o sustenta.

Pré-requisitos · 13.1 · 13.2 · Cap VIII §8.2 Alertas · O ManifestoConecta com · 13.4 Fusos · 13.5 RegiãoTempo de leitura · ~14 min

O Problema

Algum humano precisa estar de prontidão (de plantão) para responder quando o sistema falha de uma forma que exige intervenção. Essa é uma necessidade real — nem tudo pode ser automatizado (§13.2), e quando o automatizado não dá conta, alguém precisa agir. Mas o plantão, se mal projetado, deixa de ser uma rede de segurança e torna-se uma máquina de esgotar pessoas. E a forma mais comum de mal projetá-lo é deixá-lo gerar ruído: alarmes demais, e a maioria deles sobre coisas que não exigem ação humana imediata.

O ruído de alarmes é corrosivo de várias maneiras simultâneas. Primeiro, ele esgota diretamente a pessoa de plantão: ser acordado às três da manhã é desgastante mesmo quando necessário; ser acordado às três da manhã por um alarme que não exigia ação — algo que se resolveu sozinho, ou que poderia esperar até de manhã, ou que uma automação (§13.2) deveria ter tratado — é desgaste gratuito, e ele se acumula noite após noite até quebrar a pessoa. Segundo, o ruído destrói a confiança nos alarmes: quando a maioria dos alarmes não exige ação, a pessoa de plantão aprende a ignorá-los, e o alarme importante — o que de fato exige ação urgente — se perde no meio do ruído, com consequências graves. Terceiro, há os problemas estruturais que agravam tudo: uma carga de plantão excessiva (poucas pessoas cobrindo prontidão demais, ou incidentes frequentes demais) que não dá descanso; e uma cultura de caça a culpados, em que quem está de plantão durante uma falha é responsabilizado por ela, transformando o plantão num campo minado de medo. O resultado de tudo isso é o esgotamento: as pessoas que carregam o plantão se exaurem, adoecem, perdem a motivação, e acabam saindo — levando consigo o conhecimento operacional. E há uma ironia cruel: um plantão que esgota as pessoas torna o sistema menos confiável, porque pessoas exaustas cometem erros, ignoram alarmes e não conseguem responder bem. O plantão mal projetado destrói exatamente o que deveria proteger: tanto as pessoas quanto a confiabilidade. E isso é uma falha não apenas operacional, mas humana — é tratar as pessoas como recursos descartáveis a serem esgotados, em vez de seres cujo bem-estar importa.

O plantão que destrói quem o carrega

Alarmes demais, ruído que não exige ação, páginas às três da manhã por coisas que uma máquina resolveria, carga excessiva, caça a culpados: o caminho do esgotamento. E pessoas exaustas tornam o sistema menos confiável. Correção: um plantão sustentável — só o acionável acorda, carga justa, sem culpados.

A Decisão

O plantão é projetado para ser sustentável: um alarme só acorda um humano se exige ação humana imediata (o ruído é eliminado), a carga é mantida em níveis justos, a cultura é sem culpados, e — quando possível — segue-se o sol (§13.4) para evitar as noites. Cuida-se de quem opera.

A plataforma projeta o plantão com a mesma empatia que o Manifesto dedica ao humano solitário às três da manhã — porque é exatamente essa pessoa que o plantão envolve. Quatro princípios o tornam sustentável. O primeiro, e mais importante, é que só o acionável acorda alguém: um alarme só desperta um humano se exige ação humana imediata. Tudo o mais — o que se resolve sozinho, o que pode esperar até de manhã, o que uma automação deveria tratar (§13.2), o que é apenas informativo — não gera uma página no meio da noite; vai para um registro a ser revisado depois, ou dispara a automação correspondente, ou simplesmente não existe como alarme. Isso ataca o ruído pela raiz: cada alarme que de fato chega à pessoa de plantão merece sua atenção, o que preserva tanto o descanso dela quanto a confiança no alarme (quando o telefone toca, é importante de verdade). É a aplicação ao plantão da disciplina de alertas do Capítulo VIII (§8.2): alertar sobre sintomas que importam, com sinais acionáveis, não sobre ruído. O segundo princípio é a carga justa: o plantão é distribuído entre pessoas suficientes para que ninguém carregue prontidão demais, e a frequência de incidentes é mantida baixa o bastante (pela guerra ao toil, §13.2, e pela confiabilidade gerida, §13.1) para que o plantão seja, na maior parte do tempo, tranquilo. O terceiro é a cultura sem culpados (blameless): quando uma falha acontece durante um plantão, o foco é entender e corrigir o sistema, não responsabilizar a pessoa — exatamente o "a falha ensina sem caçar culpados" do Manifesto. Isso remove o medo do plantão e torna possível o aprendizado honesto (§7.5). O quarto, quando a operação é global, é seguir o sol (§13.4, o próximo tópico): distribuir o plantão por fusos horários de modo que cada região cubra suas horas de vigília, eliminando a necessidade de qualquer pessoa fazer plantão noturno regular. O resultado é um plantão que as pessoas conseguem sustentar: tranquilo na maior parte do tempo, acionado apenas quando realmente precisam, sem o medo de serem culpadas e sem o desgaste das noites desnecessárias. E um plantão sustentável é, não por acaso, um plantão eficaz: pessoas descansadas e confiantes respondem bem quando o alarme importante toca. A escolha de cuidar de quem opera não é apenas humana — embora seja, antes de tudo, isso —, é também o que mantém a confiabilidade, porque a confiabilidade de um sistema repousa, no fim, sobre o bem-estar das pessoas que o sustentam. O plantão deixa de ser uma máquina de esgotar e passa a ser uma rede de segurança que as pessoas podem carregar sem se destruir.

Diagrama conceitual Um plantão que as pessoas conseguem sustentar
PLANTÃO QUE ESGOTAalarmes demais, ruído, 3h da manhãa pessoa quebra PLANTÃO SUSTENTÁVELsó alarme acionável, carga justaa pessoa sustenta só acionável carga limitada sem culpados seguir o sol §13.4 se cada alarme acorda alguém à toa, o ruído mata a confiança e a pessoa o humano às 3h da manhã do Manifesto — cuidar de quem opera
Figura 13.3-a. O plantão que esgota (ruído, noites, culpa) destrói a pessoa. O plantão sustentável só deixa o acionável acordar alguém, mantém a carga justa, é sem culpados e segue o sol — cuidando do humano às três da manhã do Manifesto.

Só o acionável desperta um humano

alarme_acionavel.pyPython
# SÓ O ACIONÁVEL acorda alguém. O ruído NÃO gera página (§8.2).
def rotear_alarme(evento: Evento) -> None:
    if evento.exige_acao_humana_imediata():
        # Merece acordar a pessoa de plantão — é importante de verdade.
        paginar_plantao(evento)
    elif evento.tem_automacao():
        # Uma máquina trata (§13.2) — ninguém é acordado.
        disparar_automacao(evento)
    else:
        # Informativo / pode esperar → registro a revisar de dia.
        registrar_para_revisao(evento)   // não é página noturna
    # Cada página que chega à pessoa MERECE sua atenção → confiança.
O roteamento garante que só o acionável acorde um humano; o resto vira automação ou registro. Cada página noturna merece atenção, preservando descanso e confiança.
plantao_sustentavel.pyPython
# PLANTÃO SUSTENTÁVEL: carga justa, sem culpados, segue o sol.
def escala_de_plantao(pessoas: list, regioes: list) -> Escala:
    # CARGA JUSTA: distribuir entre pessoas suficientes — ninguém demais.
    afirmar(len(pessoas) >= MINIMO_PARA_NAO_ESGOTAR)

    # SEGUIR O SOL (§13.4): cada região cobre suas HORAS DE VIGÍLIA.
    # Ninguém faz plantão noturno regular — outra região está acordada.
    return Escala.por_horas_de_vigilia(pessoas, regioes)

# E quando uma falha ocorre no plantão: SEM culpados (§7.5).
# Foco em corrigir o sistema, não responsabilizar a pessoa.
# (a falha ensina sem caçar culpados — o Manifesto)
A escala mantém carga justa e segue o sol para evitar noites; e a cultura é sem culpados. O plantão é projetado em torno do bem-estar de quem o carrega.

A Verificação

plantao_test.pyPython
class TestPlantaoSustentavel(TestCase):
    def test_ruido_nao_acorda_ninguem(self):
        # Um evento que NÃO exige ação humana não gera página noturna.
        evento = self.evento_que_se_resolve_sozinho()
        rotear_alarme(evento)
        self.assertFalse(self.alguem_foi_paginado())  // virou registro/automação

    def test_so_o_acionavel_pagina(self):
        # Um evento que EXIGE ação imediata acorda a pessoa de plantão.
        evento = self.evento_que_exige_acao_humana()
        rotear_alarme(evento)
        self.assertTrue(self.alguem_foi_paginado())

    def test_carga_nao_excede_o_sustentavel(self):
        # A escala não sobrecarrega ninguém além do sustentável.
        self.assertLessEqual(self.carga_por_pessoa(), self.limite_sustentavel)
Os testes garantem o cerne: o ruído não acorda ninguém, só o acionável pagina, e a carga por pessoa fica dentro do sustentável. O plantão protege quem o carrega.
Tabela de decisão Plantão que esgota vs sustentável
AspectoPlantão que esgotaPlantão sustentável
O que acorda a pessoaTudo, inclusive ruídoSó o acionável
Confiança no alarmeErodida (ignora-se)Cada página importa
CargaExcessivaJusta, distribuída
Cultura na falhaCaça a culpadosSem culpados (§7.5)
NoitesPlantão noturno regularSeguir o sol (§13.4)

Estudo de Caso

Caso aplicado

Silenciar o ruído para preservar a pessoa

Contexto. Em um período, um componente da plataforma gerava um volume elevado de alarmes — muitos deles sobre uma condição instável (oscilação) que não exigia ação humana imediata. Esse ruído inundava quem acompanhava a operação, desgastando a atenção e ameaçando esconder, no meio do barulho, os alarmes que de fato importavam.

Plantão ruidoso
Esgota a pessoa, esconde o importante
Plantão sustentável
Só o acionável, carga justa
Ganho
Pessoas e confiabilidade preservadas

Decisão. Aplicar os princípios do plantão sustentável: fazer com que apenas alarmes acionáveis — os que exigem ação humana imediata — despertem uma pessoa, encaminhando o ruído para automação (§13.2) ou para registro a ser revisado, e tratando a causa da instabilidade. Somar a isso carga justa, cultura sem culpados e, na operação global, seguir o sol (§13.4).

Resultado. O ruído deixou de acordar pessoas à toa: cada alarme que chega à prontidão passou a merecer atenção, preservando tanto o descanso de quem opera quanto a confiança nos alarmes — quando o telefone toca, é importante de verdade. O plantão deixou de ser uma fonte de desgaste gratuito e tornou-se uma rede de segurança que as pessoas conseguem sustentar. E, como efeito, a operação ficou mais confiável: pessoas descansadas e confiantes respondem melhor ao alarme que importa. Cuidar de quem opera — o humano às três da manhã do Manifesto — revelou-se também o caminho da confiabilidade.

Incidente Real

Severidade · Média

O alarme oscilante que inundava a operação

  • Antes Um componente em estado instável (oscilando entre falha e recuperação) gerava um fluxo intenso de alarmes, a maioria sobre uma condição que não exigia ação humana imediata.
  • Sintoma O ruído inundava quem acompanhava a operação, desgastando a atenção e ameaçando afogar, no meio do barulho, os alarmes realmente importantes — o ruído corroía tanto as pessoas quanto a confiança no sistema de alarmes.
  • Diagnóstico Alarmes eram disparados por condições que não exigiam ação humana imediata; a ausência do princípio "só o acionável acorda" transformava a operação numa fonte de ruído desgastante.
  • Correção A causa da instabilidade foi tratada e os alarmes foram reorganizados para que apenas os acionáveis despertassem uma pessoa, com o restante indo para automação ou registro.
  • Kaizen Vira regra: só alarmes acionáveis acordam um humano; o ruído é eliminado na raiz, preservando o descanso de quem opera e a confiança nos alarmes.

Decisão Arquitetural

ADR-0068

Plantão sustentável: só o acionável acorda, carga justa, sem culpados

Aceito
Contexto

O plantão é necessário, mas, mal projetado, esgota as pessoas: alarmes demais e ruído que não exige ação desgastam quem está de prontidão e destroem a confiança no alarme importante; carga excessiva e caça a culpados agravam. Pessoas exaustas tornam o sistema menos confiável — o plantão destrói o que deveria proteger.

Decisão

O plantão é projetado para ser sustentável, com a empatia do Manifesto pelo humano às três da manhã: só alarmes acionáveis (que exigem ação humana imediata) acordam alguém — o ruído vira automação (§13.2) ou registro (disciplina de alertas, §8.2); a carga é mantida justa e distribuída; a cultura é sem culpados (§7.5); e, na operação global, segue-se o sol (§13.4) para evitar plantões noturnos regulares.

Alternativas consideradas

Alertar sobre tudo — ruído esgota e esconde o importante. Carga concentrada em poucos — sem descanso. Culpar quem está de plantão — medo, sem aprendizado. Plantão noturno regular — desgaste evitável.

Consequências
PositivasPlantão que as pessoas conseguem sustentar. Confiança no alarme preservada. Aprendizado honesto sem medo. Confiabilidade mantida — pessoas descansadas respondem melhor.
NegativasExige disciplina para silenciar o ruído, distribuir a carga e construir cultura sem culpados.
RiscosSilenciar alarmes demais pode esconder um sinal; mitigado por distinguir cuidadosamente o acionável do ruído, revendo o que vira registro.

Antipadrões

Alarmar sobre tudo, acionável ou não

Acordar a pessoa de plantão por ruído que não exige ação. Esgota e faz o alarme importante se perder. Correção: só o acionável acorda; o resto vira automação ou registro.

Culpar quem está de plantão na falha

Responsabilizar a pessoa pela falha que ocorreu no seu turno. Cria medo e impede o aprendizado. Correção: cultura sem culpados (§7.5) — corrige-se o sistema, não a pessoa.

Exercício

Torne o seu plantão sustentável

  1. Se você (ou sua equipe) está de plantão, conte os alarmes de uma semana. Quantos exigiam ação humana imediata, e quantos eram ruído (resolveram-se sozinhos, podiam esperar, uma máquina deveria tratar)?
  2. Faça o teste do desgaste: quantas vezes alguém foi acordado à noite por algo que não precisava de ação humana? Cada uma é desgaste gratuito que se acumula.
  3. Para o ruído mais frequente, decida o destino: ele deveria virar automação (§13.2), ir para um registro a revisar de dia, ou deixar de existir como alarme?
  4. Olhe a carga e a cultura: a prontidão está distribuída de forma justa, ou concentrada? E quando uma falha ocorre no plantão, o foco é corrigir o sistema ou achar um culpado?
Meta: tornar o seu plantão sustentável — fazendo só o acionável acordar pessoas, distribuindo a carga e cultivando uma cultura sem culpados —, para proteger tanto quem opera quanto a confiabilidade que repousa sobre o bem-estar dessas pessoas.
✓ Checklist do tópico
  • Só alarmes acionáveis (que exigem ação humana imediata) acordam uma pessoa
  • O ruído vira automação (§13.2) ou registro a revisar, não página noturna
  • A carga de plantão é mantida justa e distribuída entre pessoas suficientes
  • A cultura é sem culpados: corrige-se o sistema, não se responsabiliza a pessoa
  • Na operação global, segue-se o sol (§13.4) para evitar plantões noturnos regulares
Meta Dados · Engenharia de Plataformas · Capítulo XIII · §13.3Próximo → 13.4 Operação Entre Fusos
Capítulo XIII · SRE e Operações Globais
13.4

Operação Entre Fusos

Operar um sistema de um continente enquanto seus usuários estão em outro impõe uma escolha: ou uma pessoa carrega a operação em todos os horários, esgotando-se, ou o conhecimento é libertado para que qualquer região possa operar. A operação global madura faz o sol nunca se pôr — sem que ninguém vire a noite.

Pré-requisitos · 13.3 Plantão · Cap VIII Runbooks · §10.5Conecta com · 13.5 RegiãoTempo de leitura · ~14 min

O Problema

Uma realidade cada vez mais comum: o sistema é operado a partir de um lugar, mas seus usuários e suas operações estão em outro fuso horário — às vezes em outro continente, com várias horas de diferença. Quando a operação está num fuso e os usuários em outro, surge um descompasso fundamental: o horário de pico dos usuários — quando o sistema é mais usado e quando os problemas mais doem — pode cair fora do horário de trabalho de quem opera, e vice-versa. As três da manhã de quem opera podem ser o meio-dia movimentado dos usuários.

Esse descompasso, mal resolvido, leva a um de dois caminhos ruins. O primeiro é uma pessoa carregar a operação em todos os horários: para cobrir o pico dos usuários num fuso distante, alguém precisa estar disponível fora do seu próprio horário natural — virando noites, sacrificando o descanso, ficando de prontidão em horários que destroem a vida e a saúde. É o esgotamento do plantão (§13.3) elevado à potência da distância geográfica: não um alarme noturno ocasional, mas a necessidade estrutural de estar acordado quando se deveria dormir, dia após dia. Uma operação que depende de uma pessoa virar a noite cronicamente é uma operação construída sobre o sacrifício insustentável dessa pessoa. O segundo caminho ruim é o conhecimento preso: quando apenas uma pessoa — ou apenas as pessoas de um fuso — sabe como operar o sistema, essa pessoa torna-se simultaneamente o gargalo (nada acontece sem ela) e o ponto único de falha (se ela está indisponível, dormindo, doente, de férias, ninguém mais pode agir). Quando um problema acontece no fuso dos usuários e a única pessoa que sabe resolvê-lo está dormindo do outro lado do mundo, o problema espera — ou a pessoa é acordada, voltando ao primeiro caminho ruim. O conhecimento operacional, trancado numa cabeça ou num fuso, impede que a operação seja verdadeiramente global. O problema de fundo é que operar entre fusos com uma operação concentrada — numa pessoa, num lugar, num horário — não escala humanamente: ou se esgota a pessoa fazendo-a cobrir o mundo inteiro, ou se trava a operação limitando-a a quando essa pessoa está disponível. A distância geográfica, que poderia ser uma vantagem (sempre há algum lugar com o sol alto), vira um fardo quando a operação não está preparada para ela.

A operação concentrada que não cobre o globo

Operar de um fuso com usuários em outro força uma escolha ruim: uma pessoa vira noites cronicamente para cobrir o pico distante, ou o conhecimento fica preso e só um fuso pode agir. Concentrar a operação não escala humanamente. Correção: seguir o sol e libertar o conhecimento em runbooks.

A Decisão

A operação é distribuída por fusos (seguir o sol): cada região cobre suas horas de vigília, e o conhecimento operacional vive em runbooks escritos, não na cabeça de uma pessoa — para que qualquer região, a qualquer hora, possa operar, sem que ninguém precise virar a noite.

A plataforma resolve a operação global com dois princípios que transformam a distância geográfica de fardo em vantagem. O primeiro é seguir o sol (follow-the-sun): a operação é distribuída por fusos horários de modo que cada região cobre suas próprias horas de vigília. Quando é dia num lugar, é a equipe (ou a pessoa) daquele lugar que opera; quando ali anoitece, a responsabilidade passa para outra região onde o sol está nascendo. Assim, há sempre alguém operando em seu horário natural de vigília, e ninguém precisa virar a noite — o sol nunca se põe na operação porque ela acompanha o sol pelo globo. (Quando a operação ainda é pequena demais para ter equipes em vários fusos, o princípio se aplica em escala menor, mas a direção é a mesma: estruturar a operação para que o pico dos usuários seja coberto por quem está acordado, não por quem deveria estar dormindo.) O segundo princípio, que torna o primeiro possível, é libertar o conhecimento: o conhecimento de como operar o sistema não pode viver preso na cabeça de uma pessoa ou de um fuso — ele precisa estar escrito, em runbooks (os procedimentos operacionais que o Capítulo VIII estabeleceu), acessíveis a qualquer região, a qualquer hora. Um runbook captura como diagnosticar e resolver cada situação operacional conhecida, de modo que qualquer pessoa qualificada, em qualquer fuso, possa executá-lo — não apenas a pessoa que originalmente sabia. Isso dissolve tanto o gargalo (a operação não depende mais de uma pessoa específica) quanto o ponto único de falha (se uma região está indisponível, outra pode operar a partir dos mesmos runbooks). A esses dois princípios soma-se uma prática que os sustenta: a comunicação assíncrona em primeiro lugar. Quando as equipes estão em fusos diferentes, nem sempre há sobreposição de horário para conversas ao vivo; por isso, o estado da operação, as decisões, o contexto e as passagens de turno (handoffs) entre regiões são registrados de forma escrita e assíncrona, de modo que a região que assume encontre tudo o que precisa documentado, sem depender de pegar a região anterior acordada. A passagem de turno entre fusos torna-se uma transferência limpa de contexto escrito, não uma conversa que exige horários coincidentes. O resultado é uma operação verdadeiramente global e sustentável: a distância geográfica, antes um fardo que esgotava uma pessoa ou travava a operação, torna-se a vantagem de ter sempre algum lugar com o sol alto para operar — e o conhecimento, libertado nos runbooks, permite que esse lugar, qualquer que seja, opere bem. Ninguém vira a noite; ninguém é gargalo; ninguém é ponto único de falha. É a aplicação à dimensão geográfica dos princípios que regem todo o livro — não concentrar o essencial num único ponto (aqui, uma pessoa ou um fuso), e tornar o conhecimento explícito e compartilhável em vez de tácito e preso — somada à empatia do plantão sustentável (§13.3): cuidar de quem opera significa, numa operação global, não fazer ninguém viver contra o próprio relógio.

Diagrama conceitual Seguir o sol, com o conhecimento nos runbooks
UMA PESSOA, 24 HORASconhecimento preso nelasó um fuso pode agir SEGUIR O SOLcada fuso cobre sua vigíliaqualquer região pode operar runbook escrito assíncrono 1º passagem clara sem noites operar de um continente com clientes em outro — o sol nunca se põe na operação o conhecimento vive no runbook, não na cabeça de uma pessoa
Figura 13.4-a. Concentrar a operação numa pessoa força virar noites ou prende o conhecimento. Seguir o sol distribui por fusos — cada região cobre sua vigília — e o runbook escrito (Cap VIII) liberta o conhecimento, para que qualquer região opere sem que ninguém vire a noite.

O runbook que liberta o conhecimento

runbook.mdMarkdown
# RUNBOOK: o conhecimento operacional ESCRITO (Cap VIII) — não numa cabeça.
## Sintoma: latência alta nas requisições de cobrança

### Diagnóstico (qualquer região, qualquer hora, pode seguir)
1. Verificar o painel de latência por serviço (§Cap VIII).
2. Conferir se há acúmulo de conexões no banco (§guardião, §13.2).

### Resolução
3. Se houver acúmulo: o guardião já trata; confirmar a recuperação.
4. Se persistir: escalar conforme a passagem de turno abaixo.

# Qualquer pessoa qualificada executa — não só quem "já sabia".
# Dissolve o gargalo E o ponto único de falha humano.
O runbook escreve o conhecimento que estaria preso numa cabeça: qualquer região qualificada pode diagnosticar e resolver. O gargalo e o ponto único humano se dissolvem.
passagem_de_turno.pyPython
# SEGUIR O SOL: a responsabilidade passa de fuso em fuso, sem virar noite.
def passar_o_turno(de_regiao: Regiao, para_regiao: Regiao) -> None:
    # Comunicação ASSÍNCRONA: a região que assume encontra tudo escrito.
    contexto = RegistroDeTurno(
        estado_atual="o que está acontecendo agora",
        em_andamento="incidentes/ações em curso",
        atencao="o que vigiar nas próximas horas",
    )
    para_regiao.receber(contexto)   # não exige horário coincidente

    # Cada região cobre suas HORAS DE VIGÍLIA — ninguém opera dormindo.
    # O sol nunca se põe na operação; nenhuma pessoa vira a noite.
A passagem de turno é uma transferência de contexto escrito e assíncrono — a região que assume encontra tudo documentado, sem precisar da anterior acordada.

A Verificação

fusos_test.pyPython
class TestOperacaoEntreFusos(TestCase):
    def test_qualquer_regiao_resolve_pelo_runbook(self):
        # Uma região que NÃO criou o conhecimento resolve pelo runbook.
        regiao = self.regiao_sem_o_conhecimento_original()
        resultado = regiao.executar_runbook(self.sintoma())
        self.assertTrue(resultado.resolvido)  // conhecimento libertado

    def test_passagem_de_turno_e_completa(self):
        # A região que assume recebe TODO o contexto necessário, por escrito.
        contexto = self.passar_turno()
        self.assertTrue(contexto.completo())
        self.assertFalse(self.precisou_acordar_regiao_anterior())  // assíncrono
Os testes provam a operação global: uma região sem o conhecimento original resolve pelo runbook, e a passagem de turno é completa sem precisar acordar a região anterior.
Tabela de decisão Operação concentrada vs seguir o sol
AspectoConcentrada numa pessoa/fusoSeguir o sol + runbooks
Cobrir o pico distanteVirar a noiteOutro fuso, em vigília
ConhecimentoPreso numa cabeçaEscrito em runbooks
Gargalo / ponto únicoA pessoa é ambosQualquer região opera
ComunicaçãoExige horário coincidenteAssíncrona, escrita
Distância geográficaFardo que esgotaVantagem (sempre há sol)

Estudo de Caso

Caso aplicado

Operar de um continente, com os usuários em outro

Contexto. A plataforma é operada a partir de um continente enquanto seus clientes e operações estão em outro, com várias horas de diferença de fuso. Isso impunha o risco de uma pessoa ter que carregar a operação fora do seu horário natural — virando noites para cobrir o pico dos usuários distantes — e de o conhecimento operacional ficar preso, tornando essa pessoa gargalo e ponto único de falha.

Concentrada
Virar noites, conhecimento preso
Seguir o sol + runbooks
Qualquer região opera, sem noites
Ganho
Operação global sustentável

Decisão. Estruturar a operação para acompanhar o sol e libertar o conhecimento: escrever runbooks (Cap VIII) que permitam a qualquer pessoa qualificada, em qualquer fuso, diagnosticar e resolver as situações operacionais conhecidas; adotar comunicação assíncrona em primeiro lugar, com passagens de turno registradas por escrito; e organizar a cobertura de modo que o pico dos usuários seja atendido por quem está em vigília, não por quem deveria dormir.

Resultado. A distância geográfica deixou de ser um fardo que esgotaria uma pessoa e passou a ser administrável: o conhecimento, escrito nos runbooks, deixou de viver preso numa cabeça, dissolvendo o gargalo e o ponto único de falha humano. A operação passou a poder ser conduzida de onde houvesse alguém em vigília, com o contexto transferido de forma assíncrona, sem exigir que ninguém virasse a noite cronicamente. A operação global tornou-se sustentável — fiel ao cuidado com quem opera que o plantão sustentável (§13.3) e o Manifesto defendem.

Incidente Real

Severidade · Média

O conhecimento que vivia só numa pessoa

  • Antes Procedimentos operacionais críticos eram conhecidos sobretudo por uma única pessoa, sem estarem plenamente escritos — o conhecimento vivia numa cabeça, não num runbook acessível.
  • Sintoma Quando essa pessoa estava indisponível (em outro fuso, descansando, ausente), certas situações operacionais não podiam ser resolvidas por mais ninguém — a pessoa era simultaneamente o gargalo e o ponto único de falha da operação.
  • Diagnóstico O conhecimento operacional preso numa única pessoa impedia que a operação fosse distribuída por fusos ou conduzida por outros, concentrando risco e fardo.
  • Correção O conhecimento foi escrito em runbooks acessíveis a qualquer região qualificada, com passagens de turno assíncronas, permitindo que a operação fosse conduzida de onde houvesse alguém em vigília.
  • Kaizen Vira regra: o conhecimento operacional vive em runbooks escritos, não em cabeças; a operação segue o sol, e nenhuma pessoa é gargalo, ponto único de falha ou obrigada a virar noites.

Decisão Arquitetural

ADR-0069

Operação global: seguir o sol, com o conhecimento libertado em runbooks

Aceito
Contexto

Operar de um fuso com usuários em outro força uma escolha ruim quando a operação é concentrada: uma pessoa vira noites cronicamente para cobrir o pico distante (esgotamento estrutural), ou o conhecimento fica preso numa cabeça/num fuso, tornando essa pessoa gargalo e ponto único de falha.

Decisão

A operação segue o sol: distribuída por fusos, cada região cobre suas horas de vigília, e a responsabilidade passa de região em região — ninguém vira a noite. O conhecimento operacional vive em runbooks escritos (Cap VIII), acessíveis a qualquer região, dissolvendo o gargalo e o ponto único humano. A comunicação é assíncrona em primeiro lugar, com passagens de turno escritas. É a empatia do §13.3 na dimensão geográfica.

Alternativas consideradas

Uma pessoa cobre todos os horários — esgotamento estrutural. Conhecimento preso numa cabeça — gargalo e ponto único. Comunicação só síncrona — exige horários coincidentes que fusos distantes não têm.

Consequências
PositivasOperação global sustentável. Ninguém vira a noite. Qualquer região opera pelos runbooks. Sem gargalo nem ponto único humano. Distância vira vantagem.
NegativasExige escrever e manter runbooks e a disciplina da comunicação assíncrona e das passagens de turno.
RiscosRunbooks desatualizados enganam quem os segue; mitigado por mantê-los vivos e testá-los na prática (qualquer região consegue executar?).

Antipadrões

Uma pessoa carregando a operação em todos os fusos

Alguém virando noites cronicamente para cobrir usuários distantes. Esgotamento estrutural. Correção: seguir o sol — cada região cobre sua vigília, ninguém opera dormindo.

Conhecimento operacional preso numa cabeça

Só uma pessoa sabe operar; ela vira gargalo e ponto único de falha. Correção: o conhecimento vive em runbooks escritos, acessíveis a qualquer região.

Exercício

Prepare a sua operação para o globo

  1. Se você opera um sistema para usuários em outro fuso, pergunte: o pico dos usuários cai no seu horário de vigília, ou você precisa estar disponível quando deveria dormir?
  2. Faça o teste do ônibus: se a pessoa que mais sabe operar o seu sistema ficasse indisponível por uma semana, outra conseguiria operar? Ou o conhecimento está preso numa cabeça?
  3. Pegue uma situação operacional recorrente e escreva o runbook: como diagnosticá-la e resolvê-la, de forma que qualquer pessoa qualificada — em qualquer fuso — consiga executar?
  4. Pense na passagem de turno: se a responsabilidade passasse para outra pessoa ou região, ela encontraria o contexto necessário escrito, ou precisaria te acordar para perguntar?
Meta: preparar a sua operação para ser global e sustentável — libertando o conhecimento em runbooks e estruturando a cobertura para seguir o sol —, para que a distância geográfica deixe de esgotar uma pessoa ou prender a operação.
✓ Checklist do tópico
  • A operação é estruturada para seguir o sol: cada região cobre suas horas de vigília
  • Ninguém precisa virar a noite cronicamente para cobrir usuários distantes
  • O conhecimento operacional vive em runbooks escritos (Cap VIII), não numa cabeça
  • Qualquer região qualificada pode operar a partir dos runbooks
  • A comunicação é assíncrona em primeiro lugar, com passagens de turno escritas
Meta Dados · Engenharia de Plataformas · Capítulo XIII · §13.4Próximo → 13.5 Sobreviver à Queda de uma Região
Capítulo XIII · SRE e Operações Globais
13.5

Sobreviver à Queda de uma Região

Os capítulos anteriores eliminaram pontos únicos de falha no código, na operação, nas pessoas. Resta o maior de todos: o geográfico. Uma região inteira — um data center, uma zona — pode cair. Se tudo estiver nela, tudo cai junto. Sobreviver exige que a queda de uma região seja apenas isso: a queda de uma região.

Pré-requisitos · 13.1–13.4 · Cap X §10.3 · Cap XII §12.2 · Cap VIII WANConecta com · encerra o Cap XIIITempo de leitura · ~14 min

O Problema

Este livro travou uma guerra longa contra os pontos únicos de falha. A defesa em profundidade (§12.2) recusou a casca dura cujo perímetro era um ponto único. A autocura (§10.3) eliminou a dependência de um único processo que, ao cair, não voltava. A operação entre fusos (§13.4) dissolveu a pessoa única que era gargalo e ponto de falha. Cada um desses tópicos removeu um ponto cuja falha derrubaria o todo. Mas resta o maior ponto único de falha de todos, e o mais fácil de ignorar: o geográfico. Todo o sistema — os servidores, os dados, a operação — vive, fisicamente, em algum lugar. E se vive todo num único lugar — uma única região, um único data center, uma única zona geográfica —, então esse lugar é um ponto único de falha.

E regiões inteiras caem. Não é uma hipótese remota: um data center inteiro pode perder energia, ser desconectado por um rompimento de fibra, ser atingido por um desastre natural, sofrer uma falha de infraestrutura que derruba toda uma zona de uma vez. Quando isso acontece — e, em escala de tempo suficiente, acontece —, um sistema que vive inteiramente naquela região cai inteiro junto com ela. Não importa quão resiliente seja o sistema dentro da região: a autocura não ajuda se a região onde ela rodaria está fora; as réplicas não ajudam se todas estão na mesma região que caiu; os backups não ajudam imediatamente se estão no mesmo lugar que se perdeu. Toda a robustez construída dentro de um único lugar é anulada quando esse lugar inteiro desaparece. O sistema fica fora do ar não por minutos, mas potencialmente por horas ou dias — o tempo que levar para a região se recuperar, ou para reconstruir tudo em outro lugar a partir do zero, sob pânico. Para os usuários, é uma indisponibilidade total e prolongada; para o negócio, pode ser catastrófico. E o mais perigoso é que esse ponto único de falha é invisível na operação normal: enquanto a região está de pé, tudo funciona perfeitamente, e a concentração geográfica não causa nenhum sintoma — até o dia em que a região cai e revela que tudo dependia dela. Confiar que uma região nunca cairá é a última e mais perigosa forma de depender de um ponto único de falha, porque é a que mais fácil se ignora e a que tem as consequências mais devastadoras.

O ponto único de falha geográfico

Se todo o sistema vive numa única região, essa região é um ponto único de falha — e regiões inteiras caem. Toda a robustez interna é anulada quando o lugar inteiro desaparece, deixando o sistema fora do ar por horas ou dias. Correção: o sistema vive em mais de uma região, e a queda de uma é apenas a queda de uma.

A Decisão

O sistema vive em mais de uma região geográfica, de modo que a queda de uma região inteira seja apenas isso — não a queda do sistema. Quando uma região falha, outra assume (failover), e essa capacidade de sobreviver é testada provocando a falha, não presumida.

A plataforma elimina o último ponto único de falha — o geográfico — distribuindo o sistema por mais de uma região, de modo que nenhuma região isolada seja indispensável. O princípio é o mesmo que rege toda a luta contra pontos únicos no livro, agora aplicado à dimensão física: não concentrar o essencial num único ponto — e uma região inteira é um ponto. Concretamente, isso significa que os componentes críticos do sistema (a capacidade de servir as requisições, os dados) existem redundantemente em regiões geograficamente distintas, de forma que a perda de qualquer uma delas não leve à perda do sistema. Quando uma região cai, ocorre o failover: a responsabilidade de servir os usuários transfere-se para uma região que continua de pé, idealmente de forma automática e rápida, de modo que a queda de uma região inteira se traduza, para os usuários, numa interrupção mínima ou inexistente, em vez de uma indisponibilidade total e prolongada. A queda de uma região passa a ser apenas isso — a queda de uma região —, e não a queda do sistema. Essa redundância geográfica apoia-se em capacidades que o livro já construiu: os dados precisam estar replicados entre regiões (uma extensão da reprodutibilidade e da separação de responsabilidades dos Volumes IV e XI); o sistema precisa ser implantável em qualquer região de forma idêntica (a infraestrutura como código e as imagens imutáveis do Capítulo X tornam isso possível — uma região nova é a mesma definição aplicada noutro lugar); e o failover precisa ser orquestrado (a operação declarativa do §10.3, elevada à escala geográfica). E há um princípio final, herdado diretamente da postura verificável (§12.5), que é o que separa a redundância real da redundância imaginária: a capacidade de sobreviver à queda de uma região não se presume — testa-se. Como toda defesa não testada é apenas uma esperança, a única forma de saber que o failover funciona é provocá-lo: derrubar deliberadamente uma região (de forma controlada) e confirmar que outra assume, que os usuários continuam servidos, que os dados estão lá. Uma redundância geográfica que nunca foi testada é uma redundância em que não se pode confiar — pode haver uma suposição não verificada que só se revelaria falsa no dia real da queda, quando é tarde demais. Testá-la transforma a sobrevivência à queda de uma região de uma esperança numa capacidade comprovada. Com isso, o sistema completa sua resiliência: não há mais nenhum ponto único de falha — nem no código, nem na operação, nem nas pessoas, nem na geografia —, e a queda de qualquer parte isolada, até de uma região inteira, é absorvida em vez de fatal. É a culminação do princípio que percorre o livro inteiro: a robustez de um sistema é a ausência de qualquer ponto cuja falha o derrube, e o último desses pontos a enfrentar é o chão sob os servidores.

Arquitetura lógica Várias regiões, com failover testado
TUDO NUMA REGIÃOa região cai → tudo cai juntoponto único GEOGRÁFICO VÁRIAS REGIÕESuma cai → outra assumefailover, sem queda total Região Aativa Região Bpronta p/ assumir failover nenhum ponto único de falha — nem mesmo o geográfico
Figura 13.5-a. Tudo numa região é um ponto único geográfico — a região cai, tudo cai. Distribuir por várias regiões, com failover quando uma cai, faz a queda de uma região ser apenas isso. E a capacidade de sobreviver é testada (§12.5), não presumida.

Failover entre regiões, testado

multirregiao.yamlYAML
# O sistema vive em MAIS DE UMA região — sem ponto único geográfico.
sistema:
  regioes:
    - nome: regiao-a
      papel: ativa
    - nome: regiao-b
      papel: pronta-para-assumir   # redundância geográfica

  dados:
    replicacao: entre-regioes   # os dados existem nas duas (Cap IV/XI)

  failover:
    automatico: true            # região cai → outra assume (§10.3)
    # uma região nova = a MESMA definição (§10.1, §10.2) aplicada lá
O sistema declara mais de uma região, com dados replicados e failover automático. Uma região nova é a mesma definição (Cap X) aplicada noutro lugar — sem ponto único.
testar_failover.shBash
# TESTAR a sobrevivência: não se PRESUME (§12.5) — provoca-se a falha.

# Derruba DELIBERADAMENTE a região ativa (de forma controlada).
derrubar-regiao regiao-a --controlado

# CONFIRMA que a outra assumiu e os usuários seguem servidos.
afirmar "$(regiao-servindo)" = "regiao-b" || falhar "FAILOVER NÃO FUNCIONOU"
afirmar "$(usuarios-sendo-atendidos)" = "sim" || falhar "indisponibilidade!"
afirmar "$(dados-presentes regiao-b)" = "sim" || falhar "dados perdidos!"

# Redundância TESTADA = capacidade comprovada, não esperança.
# A queda de uma região passa a ser APENAS a queda de uma região.
A sobrevivência é testada provocando a queda de uma região e confirmando que outra assume, os usuários seguem servidos e os dados estão lá — capacidade comprovada, não esperança.

A Verificação

regiao_test.pyPython
class TestSobreviverAQuedaDeRegiao(TestCase):
    def test_queda_de_uma_regiao_nao_derruba_o_sistema(self):
        # Derruba uma região INTEIRA...
        self.derrubar_regiao("regiao-a")

        # ...e o sistema continua servindo os usuários (failover).
        self.assertTrue(self.sistema_no_ar())
        self.assertEqual(self.regiao_ativa(), "regiao-b")

    def test_dados_sobrevivem_a_perda_da_regiao(self):
        # Os dados estão replicados — a perda da região não os perde.
        self.derrubar_regiao("regiao-a")
        self.assertTrue(self.dados_presentes("regiao-b"))  // nada perdido
Os testes provam a resiliência geográfica: derrubada uma região inteira, o sistema continua servindo de outra e os dados sobrevivem. O ponto único geográfico foi eliminado.
Tabela de decisão Uma região vs várias regiões
AspectoTudo numa regiãoVárias regiões + failover
A região caiTudo cai juntoOutra assume
IndisponibilidadeHoras ou diasMínima ou nenhuma
Ponto único geográficoSimEliminado
Dados na quedaIndisponíveisReplicados, presentes
Sobrevivência—Testada (§12.5), não presumida

Estudo de Caso

Caso aplicado

Quando a queda de uma região virou apenas a queda de uma região

Contexto. Componentes da plataforma corriam o risco de depender de uma única localização geográfica — uma única região ou data center. Por mais resiliente que fosse o sistema dentro daquela localização, a queda da região inteira (por falha de energia, conectividade ou desastre) derrubaria tudo junto, deixando o sistema indisponível por um tempo prolongado.

Uma região
Ponto único geográfico
Várias regiões
Failover testado
Ganho
Nenhum ponto único, nem o geográfico

Decisão. Eliminar o ponto único de falha geográfico distribuindo o sistema por mais de uma região, com dados replicados entre elas e failover para que, se uma região caísse, outra assumisse. Apoiar-se na infraestrutura como código e nas imagens imutáveis (Cap X) para que uma região seja a mesma definição aplicada noutro lugar — e, crucialmente, testar a sobrevivência provocando a queda controlada de uma região, em vez de presumi-la (§12.5).

Resultado. A queda de uma região deixou de ser a queda do sistema e passou a ser apenas o que é — a queda de uma região —, absorvida pelo failover para outra que permanece de pé. A indisponibilidade total e prolongada que uma concentração geográfica causaria deu lugar a uma interrupção mínima. E porque a capacidade de sobreviver foi testada, não suposta, ela é confiável: a redundância é real, comprovada provocando a falha, e não uma esperança que só se revelaria falsa no dia real. Com isso, eliminou-se o último ponto único de falha — o geográfico —, completando a resiliência que o livro inteiro perseguiu.

Incidente Real

Severidade · Alta

A dependência de conectividade que precisava de alternativa

  • Antes Em um ambiente crítico, a operação dependia de um único caminho de conectividade; uma falha nesse caminho poderia isolar o ambiente, sem uma rota alternativa que assumisse automaticamente.
  • Risco Um único ponto na conectividade significava que sua falha derrubaria o acesso ao ambiente — um ponto único de falha que, na operação normal, permanecia invisível, até o momento em que a conexão caísse.
  • Diagnóstico A ausência de redundância (uma alternativa que assumisse na falha do caminho principal) concentrava o risco num único ponto, à espera de uma falha que o revelaria.
  • Correção Introdução de redundância com failover — uma rota alternativa que assume automaticamente quando a principal falha —, com monitoramento (Cap VIII) confirmando a alternância, eliminando o ponto único.
  • Kaizen Vira regra: nenhum ponto único de falha — inclusive na conectividade e na localização; há sempre uma alternativa com failover, e a sobrevivência é testada, não presumida.

Decisão Arquitetural

ADR-0070

Sobreviver à queda de uma região: redundância geográfica com failover testado

Aceito
Contexto

O último e maior ponto único de falha é o geográfico: se todo o sistema vive numa única região, a queda dessa região (energia, conectividade, desastre) derruba tudo junto, e toda a robustez interna é anulada. Esse ponto é invisível na operação normal e tem as consequências mais devastadoras — indisponibilidade total por horas ou dias.

Decisão

O sistema vive em mais de uma região geográfica, sem que nenhuma seja indispensável: componentes críticos e dados existem redundantemente em regiões distintas, e quando uma cai, outra assume (failover). Apoia-se na IaC e nas imagens imutáveis (Cap X) para implantar regiões idênticas, e na replicação de dados (Cap IV/XI). Crucialmente, a sobrevivência é testada provocando a queda controlada de uma região (§12.5), não presumida.

Alternativas consideradas

Tudo numa região — ponto único geográfico, queda total. Réplicas só na mesma região — caem junto com ela. Redundância não testada — esperança que falha no dia real.

Consequências
PositivasA queda de uma região é apenas isso, não do sistema. Indisponibilidade mínima no lugar de prolongada. Dados sobrevivem. Último ponto único de falha eliminado. Sobrevivência comprovada.
NegativasExige operar em múltiplas regiões (custo e complexidade) e testar o failover regularmente.
RiscosMultirregião adiciona complexidade e custo, e a replicação tem implicações de consistência; mitigado por dimensioná-la à criticidade e por testar o failover de fato.

Antipadrões

Todo o sistema numa única região

Concentrar servidores, dados e operação num único lugar geográfico. A região cai, tudo cai. Correção: distribuir por mais de uma região, com failover.

Presumir que o failover funciona sem testá-lo

Ter redundância geográfica no papel, nunca exercitada. Pode falhar no dia real. Correção: testar provocando a queda controlada de uma região (§12.5).

Exercício

Encontre o seu ponto único geográfico

  1. Pergunte: se a região (ou o data center) onde o seu sistema vive caísse inteira agora, o que aconteceria? O sistema continuaria no ar de outro lugar, ou cairia junto?
  2. Faça o inventário geográfico: os seus servidores, os seus dados e os seus backups estão todos no mesmo lugar físico? Se sim, esse lugar é o seu ponto único de falha.
  3. Pense no failover: se uma região caísse, haveria outra pronta para assumir, com os dados disponíveis? Quanto tempo levaria — minutos (failover) ou dias (reconstruir do zero)?
  4. Faça o teste da postura verificável (§12.5): a sua capacidade de sobreviver à queda de uma região já foi testada provocando a falha, ou é uma suposição que só se confirmaria no dia real?
Meta: identificar o ponto único de falha geográfico do seu sistema e planejar a redundância entre regiões com failover testado — eliminando o último ponto cuja falha derrubaria tudo.
✓ Checklist do tópico
  • O sistema vive em mais de uma região geográfica; nenhuma é indispensável
  • Os dados estão replicados entre regiões e sobrevivem à perda de uma
  • Quando uma região cai, outra assume (failover), idealmente automático
  • Uma região nova é a mesma definição (Cap X) aplicada noutro lugar
  • A sobrevivência à queda de uma região é testada provocando a falha, não presumida (§12.5)
Meta Dados · Engenharia de Plataformas · Capítulo XIII · §13.5Fim do Capítulo XIII · voltar ao início
Meta Dados · Engenharia de Plataformas
Capítulo XIV

Arquitetura
Empresarial

A vista de cima do sistema inteiro: traçar fronteiras entre domínios para que o todo não vire um nó, registrar o porquê das decisões como ativo, governar a técnica sem virar burocracia, manter muitos produtos coerentes sem enrijecê-los e fazer o sistema evoluir sem reescrevê-lo do zero.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo XIV
Capítulo XIV · Arquitetura Empresarial
14.1

Fronteiras de Contexto

Visto de muito perto, todo sistema parece um amontoado de partes. Visto de cima, ou tem fronteiras claras entre domínios — e é navegável — ou não tem nenhuma, e é um nó em que tudo depende de tudo e uma mudança em qualquer ponto pode quebrar qualquer outro. A arquitetura começa por traçar essas fronteiras.

Pré-requisitos · Cap VI §6.1 ACL · Cap IV Contrato · Cap IIConecta com · 14.3 Governança · 14.4 CoerênciaTempo de leitura · ~15 min

O Problema

Este capítulo muda a escala de observação. Os capítulos anteriores olharam para dentro do sistema — como construir uma camada, um pipeline, uma defesa. Este olha para o sistema inteiro, de cima, como quem vê uma cidade do alto em vez de uma rua. E dessa altura, a primeira pergunta — a que decide se tudo o mais será possível — é sobre fronteiras: onde um domínio termina e outro começa. Um domínio é uma área de responsabilidade com seu próprio assunto: o faturamento, a logística, a identidade, o catálogo. A questão é se há limites claros entre esses domínios, ou se eles se misturam num emaranhado sem divisões.

Quando não há fronteiras, o sistema torna-se um nó — o que a literatura chama, sem delicadeza, de uma "grande bola de lama". Tudo depende de tudo: o código do faturamento conhece os detalhes internos da logística, que conhece os do catálogo, que conhece os da identidade, numa teia de dependências cruzadas em que nada pode ser entendido isoladamente. As consequências são paralisantes. Uma mudança em qualquer ponto pode quebrar qualquer outro: alterar como a logística representa uma entrega pode, inexplicavelmente, quebrar o faturamento, porque o faturamento dependia daquele detalhe interno. Ninguém consegue entender uma parte sem entender o todo, e o todo é grande demais para caber na cabeça de alguém. Há ainda um problema mais sutil e insidioso: a confusão de vocabulário. A palavra "cliente" significa coisas diferentes em domínios diferentes — no faturamento, é uma entidade fiscal com CNPJ e condições de pagamento; na logística, é um endereço de entrega e uma janela de horário; no suporte, é um histórico de tickets. Sem fronteiras que separem esses sentidos, força-se um único conceito de "cliente" a servir a todos os domínios, e ele incha numa abstração monstruosa que tenta ser tudo para todos e acaba sendo confusa para cada um — cheia de campos que só importam a um domínio, com regras contraditórias entre domínios, impossível de evoluir sem afetar áreas que não deveriam ter relação. O sistema sem fronteiras não escala nem em complexidade (vira um nó indecifrável) nem em organização (várias pessoas não conseguem trabalhar em paralelo sem colidir, porque tudo está acoplado a tudo). Visto de cima, é uma mancha sem contornos, onde não se sabe onde uma responsabilidade termina e outra começa — e o que não tem contorno não se pode governar, dividir, nem entender.

O nó onde tudo depende de tudo

Sem fronteiras entre domínios, o sistema vira um nó: uma mudança em qualquer ponto pode quebrar qualquer outro, ninguém entende uma parte sem o todo, e um mesmo termo ("cliente") incha tentando servir a todos os domínios. Correção: traçar fronteiras de contexto — onde cada domínio termina, com vocabulário próprio e contratos explícitos entre eles.

A Decisão

Traçam-se fronteiras de contexto: cada domínio tem um limite claro, dentro do qual seu vocabulário é próprio e coerente, e entre os quais a comunicação passa por contratos explícitos. Mudar dentro de um domínio não atravessa a fronteira; um termo pode significar coisas distintas em contextos distintos.

A plataforma organiza o sistema inteiro em fronteiras de contexto (bounded contexts): cada domínio é delimitado por uma fronteira clara, que define onde ele termina e outro começa. Dentro de cada fronteira, três coisas valem. Primeira, um vocabulário próprio e coerente: dentro do contexto de faturamento, "cliente" tem um sentido preciso (a entidade fiscal), e todo o código, os dados e as conversas daquele domínio usam o termo com esse sentido — sem a ambiguidade de tentar significar também o que "cliente" significa na logística. Cada contexto é livre para dar a cada termo o sentido que faz sentido ali, e a fronteira é precisamente o que torna isso possível sem confusão: "cliente" no faturamento e "cliente" na logística são conceitos diferentes, cada um coerente em seu contexto, e a fronteira impede que se misturem numa abstração monstruosa. Segunda, autonomia interna: o que acontece dentro de uma fronteira é assunto daquele domínio. Como o faturamento representa internamente seus dados, como organiza sua lógica, como evolui — nada disso atravessa a fronteira nem afeta os outros domínios. Mudar a representação interna da logística não quebra o faturamento, porque o faturamento nunca dependeu dos detalhes internos da logística — dependeu apenas do contrato na fronteira. Terceira, e decisiva, a comunicação entre contextos passa por contratos explícitos: quando o faturamento precisa de algo da logística, não alcança para dentro dela e mexe em suas entranhas; pede através de uma interface bem definida — um contrato (Cap IV) protegido, se necessário, por uma camada anticorrupção (§6.1 Cap VI) que traduz entre os vocabulários dos dois contextos. A fronteira é, assim, ao mesmo tempo uma separação (o que está dentro é autônomo) e um ponto de contato controlado (o que atravessa passa por um contrato). As consequências dissolvem o nó. O sistema deixa de ser uma mancha sem contornos e passa a ser um mapa de domínios delimitados, em que se sabe onde cada responsabilidade mora. Uma mudança dentro de um domínio fica contida nele, sem ripple imprevisível pelo resto. Cada domínio pode ser entendido isoladamente, porque tem uma fronteira que o torna um todo compreensível em si. E várias pessoas (ou equipes) podem trabalhar em domínios diferentes em paralelo, sem colidir, porque as fronteiras desacoplam o trabalho. É a aplicação, na maior escala — a do sistema inteiro —, do princípio da separação de responsabilidades que percorre o livro desde a Clean Architecture do Capítulo II: assim como dentro de um componente separamos as responsabilidades em camadas, entre os componentes do sistema separamos os domínios em contextos delimitados. A arquitetura empresarial começa aqui, com o ato fundamental de traçar contornos — porque só o que tem contorno pode ser entendido, governado e evoluído; e um sistema sem fronteiras é um sistema condenado a virar um nó que ninguém ousa tocar.

Arquitetura lógica Domínios delimitados, com contratos nas fronteiras
SEM FRONTEIRAStudo depende de tudomudar aqui quebra lá — o nó domínio Avocabulário próprio domínio Bvocabulário próprio contrato a fronteira diz onde um domínio termina e outro começa "cliente" no faturamento ≠ "cliente" na logística — cada contexto, seu sentido mudar dentro de um domínio não atravessa a fronteira
Figura 14.1-a. Sem fronteiras, tudo depende de tudo — o nó. As fronteiras de contexto delimitam cada domínio (vocabulário próprio, autonomia interna) e a comunicação entre eles passa por contratos. "Cliente" pode significar coisas distintas em contextos distintos.

Cada contexto, seu vocabulário

contextos.txttexto
  O MESMO termo, sentidos DIFERENTES — cada um coerente em seu contexto.

  ┌─ Contexto: FATURAMENTO ────────┐   ┌─ Contexto: LOGÍSTICA ──────────┐
  │  "Cliente" =                   │   │  "Cliente" =                   │
  │    • entidade fiscal (CNPJ)    │   │    • endereço de entrega       │
  │    • condições de pagamento    │   │    • janela de horário         │
  │    • limite de crédito         │   │    • restrições de acesso      │
  └───────────────┬────────────────┘   └────────────────┬───────────────┘
                  │         contrato explícito           │
                  └──────────────────────────────────────┘
        (a fronteira traduz entre os dois — camada anticorrupção §6.1)

  SEM a fronteira: um "Cliente" único, inchado, tentando ser os dois → confuso.
A fronteira deixa "cliente" significar coisas diferentes em cada contexto, cada uma coerente. Sem ela, um conceito único incha tentando servir a todos os domínios.
fronteira.phpPHP
// A comunicação entre contextos passa por um CONTRATO — não por dentro.
namespace Faturamento;

final class ServicoDeCobranca
{
    // O faturamento PEDE à logística pelo contrato, sem mexer nas entranhas.
    public function cobrarEntrega(EntregaId $id): Fatura
    {
        // Interface bem definida na fronteira (não o objeto interno da logística).
        $dados = $this->logistica->consultarEntrega($id);  // contrato

        // Traduz para o vocabulário do faturamento (ACL §6.1).
        $cliente = $this->traduzir->paraClienteFiscal($dados);
        return $this->emitir($cliente);
    }
    // Mudar o INTERNO da logística não quebra isto — só o contrato importa.
}
O faturamento pede à logística por um contrato na fronteira e traduz para o próprio vocabulário (ACL). Mudar o interno da logística não atravessa a fronteira.

A Verificação

fronteira_test.phpPHP
final class FronteiraDeContextoTest extends TestCase
{
    public function test_mudanca_interna_nao_atravessa_a_fronteira(): void
    {
        // A logística muda sua representação INTERNA da entrega...
        $this->logistica->mudarRepresentacaoInterna();

        // ...e o faturamento continua funcionando (dependia do contrato, não do interno).
        $fatura = $this->cobranca->cobrarEntrega($this->entregaId());
        $this->assertNotNull($fatura);
    }

    public function test_contextos_nao_compartilham_o_modelo_interno(): void
    {
        // O faturamento NÃO depende das classes internas da logística.
        $this->assertNaoDependeDe('Faturamento', 'Logistica\Interno');
    }
}
Os testes provam a fronteira: uma mudança interna num domínio não quebra o outro, e os contextos não compartilham modelos internos — só contratos.
Tabela de decisão Sem fronteiras vs fronteiras de contexto
AspectoNó sem fronteirasFronteiras de contexto
DependênciasTudo depende de tudoSó via contrato
Mudança internaRipple imprevisívelContida no domínio
Entender uma parteExige o todoIsoladamente
"Cliente"Um, inchado, confusoPróprio de cada contexto
Trabalho em paraleloColideDomínios desacoplados

Estudo de Caso

Caso aplicado

Do nó ao mapa de domínios

Contexto. À medida que a plataforma crescia, abrangendo faturamento, logística, identidade, catálogo e mais, havia o risco de esses domínios se misturarem num emaranhado — onde um conceito como "cliente" tentaria significar tudo ao mesmo tempo e onde uma mudança num domínio quebraria outros sem aviso.

Nó sem fronteiras
Tudo acoplado, mudança quebra tudo
Fronteiras de contexto
Domínios delimitados e autônomos
Ganho
Sistema navegável e evoluível

Decisão. Organizar o sistema em fronteiras de contexto: delimitar cada domínio, dar-lhe um vocabulário próprio e coerente (um "cliente" do faturamento distinto de um "cliente" da logística), garantir autonomia interna e fazer toda a comunicação entre domínios passar por contratos explícitos, protegidos por camadas anticorrupção (§6.1) quando os vocabulários divergem.

Resultado. O sistema deixou de ser uma mancha sem contornos e tornou-se um mapa de domínios delimitados, em que se sabe onde cada responsabilidade mora. Uma mudança dentro de um domínio passou a ficar contida nele, sem ripple imprevisível pelo resto. Cada domínio pôde ser entendido isoladamente, e equipes diferentes passaram a trabalhar em domínios diferentes em paralelo, sem colidir. O conceito monstruoso de "cliente único" deu lugar a conceitos coerentes em cada contexto. A separação de responsabilidades do Capítulo II, aplicada à escala do sistema inteiro, transformou o nó em arquitetura.

Incidente Real

Severidade · Média

O conceito que tentava servir a domínios demais

  • Antes Um conceito central de negócio era compartilhado entre vários domínios sem fronteiras claras, acumulando campos e regras de todos eles num único modelo que tentava servir a todos.
  • Sintoma O modelo inchado tornou-se difícil de evoluir: uma mudança feita para atender a um domínio afetava ou confundia os outros, porque todos dependiam do mesmo conceito sobrecarregado, com regras que faziam sentido em um contexto e não em outro.
  • Diagnóstico A ausência de fronteiras de contexto forçava um único conceito a servir a domínios distintos, criando acoplamento e ambiguidade entre áreas que deveriam ser independentes.
  • Correção O conceito foi separado em contextos: cada domínio passou a ter sua própria versão coerente, com contratos explícitos e tradução nas fronteiras, desacoplando as evoluções.
  • Kaizen Vira regra: domínios têm fronteiras de contexto; um termo pode significar coisas distintas em contextos distintos, e a comunicação entre eles passa por contratos, não por um modelo compartilhado.

Decisão Arquitetural

ADR-0071

O sistema é organizado em fronteiras de contexto entre domínios

Aceito
Contexto

Sem fronteiras entre domínios, o sistema vira um nó (uma "grande bola de lama"): tudo depende de tudo, uma mudança em qualquer ponto pode quebrar qualquer outro, nenhuma parte se entende sem o todo, e um mesmo termo incha tentando servir a todos os domínios. Não escala em complexidade nem em organização.

Decisão

O sistema é organizado em fronteiras de contexto: cada domínio é delimitado, com vocabulário próprio e coerente (um termo pode significar coisas distintas em contextos distintos), autonomia interna (mudanças internas não atravessam a fronteira) e comunicação entre contextos via contratos explícitos (Cap IV), protegidos por camada anticorrupção (§6.1) quando os vocabulários divergem. É a separação de responsabilidades (Cap II) na escala do sistema.

Alternativas consideradas

Sem fronteiras (bola de lama) — nó acoplado, ripple imprevisível. Modelo único compartilhado — incha e acopla domínios distintos. Fronteiras sem contrato — domínios alcançam as entranhas uns dos outros.

Consequências
PositivasSistema vira mapa navegável de domínios. Mudança contida no domínio. Cada parte entendível isoladamente. Trabalho em paralelo sem colisão. Vocabulário coerente por contexto.
NegativasExige identificar os domínios e disciplinar a comunicação entre eles por contratos.
RiscosFronteiras mal traçadas atrapalham; mitigado por alinhá-las aos domínios reais do negócio e ajustá-las conforme o entendimento evolui.

Antipadrões

Um modelo único para todos os domínios

Forçar um conceito (como "cliente") a servir faturamento, logística e suporte ao mesmo tempo. Incha e acopla. Correção: cada contexto tem sua versão coerente do conceito, com contrato na fronteira.

Domínios que alcançam as entranhas uns dos outros

Um domínio depende dos detalhes internos de outro, sem contrato. Mudar o interno quebra o vizinho. Correção: comunicação só via contrato explícito na fronteira.

Exercício

Trace as fronteiras do seu sistema

  1. Liste os domínios do seu sistema (faturamento, identidade, catálogo, etc.). Eles têm fronteiras claras, ou se misturam num emaranhado onde tudo depende de tudo?
  2. Faça o teste do termo ambíguo: existe uma palavra ("cliente", "pedido", "conta") que significa coisas diferentes em partes diferentes do sistema, mas é forçada num único conceito? Esse é um sintoma de fronteiras ausentes.
  3. Faça o teste do ripple: quando você muda algo em um domínio, com que frequência isso quebra inesperadamente outro? Cada surpresa dessas é uma fronteira que não existe.
  4. Para dois domínios, esboce a fronteira: qual seria o contrato explícito entre eles, e onde uma camada anticorrupção (§6.1) traduziria entre seus vocabulários?
Meta: identificar os domínios do seu sistema e traçar fronteiras de contexto entre eles — para que cada um seja autônomo e coerente, a comunicação passe por contratos, e o sistema deixe de ser um nó para virar um mapa.
✓ Checklist do tópico
  • Os domínios do sistema têm fronteiras de contexto claras
  • Dentro de cada fronteira, o vocabulário é próprio e coerente
  • Um termo pode significar coisas distintas em contextos distintos, sem confusão
  • Mudanças internas a um domínio não atravessam a fronteira nem quebram outros
  • A comunicação entre contextos passa por contratos explícitos (com ACL quando necessário)
Meta Dados · Engenharia de Plataformas · Capítulo XIV · §14.1Próximo → 14.2 A Decisão como Ativo
Capítulo XIV · Arquitetura Empresarial
14.2

A Decisão como Ativo

Toda arquitetura é uma pilha de decisões — mas o que se guarda costuma ser só o resultado, não o porquê. E quando o porquê se perde, restam duas misérias: repetir um erro já cometido, ou temer mexer no que ninguém entende. Registrar a razão da decisão a transforma de evento esquecido em ativo durável.

Pré-requisitos · O Manifesto · Cap I · todos os ADRs do livroConecta com · 14.3 Governança · 14.5 EvoluirTempo de leitura · ~14 min

O Problema

Um sistema é, no fundo, a materialização de milhares de decisões. Por que os dados ficam neste banco e não naquele? Por que a comunicação entre dois serviços é assíncrona? Por que existe esta camada de tradução? Por que não se adotou aquela tecnologia que parecia óbvia? Cada uma dessas escolhas foi uma decisão, tomada por alguém, em algum momento, por razões que faziam sentido então. Mas há uma assimetria cruel no que sobrevive: o resultado da decisão fica gravado no sistema — o banco escolhido está lá, a camada existe —, mas o porquê da decisão, a razão que a justificou, raramente é registrado. Ele vive apenas na cabeça de quem decidiu. E cabeças esquecem, e pessoas saem.

Quando o porquê se perde, o sistema fica cheio de decisões cujo resultado persiste mas cuja razão evaporou — e isso leva a duas misérias opostas, ambas custosas. A primeira é repetir um erro já cometido. Alguém olha para uma escolha do sistema, não entende por que foi feita assim, acha que foi um equívoco ou uma limitação, e a "corrige" — sem saber que aquela escolha foi uma decisão deliberada, tomada justamente para evitar um problema que agora vai retornar. A razão original — "fizemos assim porque a alternativa óbvia causava tal problema" — não estava registrada, então o problema que a decisão evitava é reintroduzido, e a equipe redescobre, com dor, o que já sabia e esqueceu. A segunda miséria é o oposto: temer mexer no que ninguém entende. Diante de uma parte do sistema cujo porquê se perdeu, a reação prudente vira paralisia: ninguém ousa tocar, porque ninguém sabe se aquela estranheza é um erro a corrigir ou uma decisão crucial cuja remoção quebraria algo. A parte vira intocável não por ser boa, mas por ser incompreendida — um pedaço de "código assombrado" que se mantém por medo, mesmo quando deveria mudar. Entre repetir erros e temer mudanças, o sistema fica preso: ou se desfazem decisões boas por ignorância, ou se preservam decisões ruins por medo, e em ambos os casos a causa é a mesma — o porquê não foi registrado. E essa perda se agrava com o tempo e com a rotatividade: a cada pessoa que sai, vai-se um pedaço da memória das decisões, até que o sistema se torna um conjunto de escolhas que ninguém vivo entende completamente — uma caixa-preta construída não pela complexidade, mas pelo esquecimento. Contra essa opacidade, exatamente, o Manifesto se levantou: contra as caixas-pretas, a favor do porquê explícito, dos ADRs que registram a razão.

O porquê que evapora com quem decidiu

Quando só o resultado da decisão fica e a razão se perde, repete-se um erro já resolvido (a "correção" que reintroduz o problema que a decisão evitava) ou teme-se mexer no que ninguém entende (o código assombrado mantido por medo). Correção: registrar a decisão — contexto, alternativas, consequências — como um ativo durável.

A Decisão

Cada decisão de arquitetura significativa é registrada como um ativo: o contexto que a motivou, a decisão tomada, as alternativas consideradas e as consequências aceitas. O porquê sobrevive a quem decidiu — é o método de ADRs que este livro usa em cada tópico.

A plataforma trata cada decisão de arquitetura como um ativo durável, registrando-a num formato que captura não apenas o que se decidiu, mas — sobretudo — por quê. Esse registro é o ADR (registro de decisão de arquitetura), e sua estrutura, que percorre cada tópico deste livro, é deliberada. Um ADR captura o contexto: qual era a situação, o problema, as forças em jogo que tornaram a decisão necessária. Captura a decisão em si: o que se escolheu fazer. Captura as alternativas consideradas: que outros caminhos foram avaliados, e — crucialmente — por que foram rejeitados (é aqui que mora a defesa contra a repetição de erros: o ADR registra explicitamente "a alternativa óbvia X foi rejeitada porque causa o problema Y"). E captura as consequências: o que se ganha com a decisão, mas também o que se aceita perder — os custos, os riscos, os trade-offs assumidos com os olhos abertos. Com isso, a decisão deixa de ser um evento que aconteceu e foi esquecido, e torna-se um ativo que permanece. As consequências dessa permanência desfazem as duas misérias. Contra a repetição de erros: quem, no futuro, considerar "corrigir" uma escolha encontra o ADR que explica por que ela foi feita, descobre que a alternativa que parecia óbvia já foi avaliada e rejeitada por uma boa razão, e evita reintroduzir o problema — o erro não se repete porque a razão de não cometê-lo está registrada. Contra o medo de mexer: quem precisar evoluir uma parte do sistema encontra o ADR que explica o porquê dela, entende o que aquela decisão protege, e pode mudá-la com consciência — sabendo o que está em jogo, em vez de temer o desconhecido. A parte deixa de ser código assombrado e vira uma decisão compreendida, que se pode manter ou revisar com fundamento. E há um ganho mais profundo, ligado à rotatividade e à memória: o ADR faz a sabedoria das decisões sobreviver às pessoas. Quando alguém sai, o porquê das decisões que tomou não vai embora com ela — fica nos ADRs, disponível para quem chega. A equipe acumula uma memória institucional de raciocínio, não apenas de resultado, e cada decisão futura pode se apoiar no entendimento das passadas, em vez de redescobri-las do zero. Isto é a elevação dos ADRs de um artefato pontual (uma seção em cada tópico) a um método deliberado de arquitetura — e não é por acaso que este livro inteiro foi escrito assim, com um ADR ancorando cada decisão de cada tópico: o método é a tese. A arquitetura empresarial não se faz apenas de boas decisões; faz-se de boas decisões cujo porquê permanece, porque um sistema é tão compreensível e evoluível quanto a memória das razões que o moldaram. Registrar a decisão é a forma de não deixar o sistema virar uma caixa-preta de escolhas órfãs — é manter acesa, contra o esquecimento, a luz do porquê.

Diagrama conceitual A decisão registrada como ativo durável
SÓ O "O QUÊ" FICAo porquê foi-se com quem decidiurepete-se o erro ou teme-se mexer O "PORQUÊ" REGISTRADOcontexto, alternativas, consequênciassobrevive a quem decidiu contexto decisão alternativas consequências a decisão registrada é um ATIVO — não um evento esquecido este livro inteiro decide por ADRs — o método é a tese
Figura 14.2-a. Quando só o "o quê" fica, o porquê some com quem decidiu — repete-se o erro ou teme-se mexer. Registrar a decisão (contexto, decisão, alternativas, consequências) a torna um ativo que sobrevive. É o método de ADRs que este livro usa em cada tópico.

A estrutura que captura o porquê

adr.mdMarkdown
# ADR: a DECISÃO como ativo — captura o PORQUÊ, não só o quê.
## Título: Comunicação assíncrona entre o pedido e o faturamento

### Contexto   # a situação e as forças em jogo
O pedido precisa avisar o faturamento, mas o faturamento pode estar
indisponível, e o pedido não pode falhar por causa disso.

### Decisão   # o que se escolheu
A comunicação é assíncrona, via mensagem, com entrega garantida.

### Alternativas   # o que foi REJEITADO e por quê (contra repetir o erro)
- Chamada síncrona direta — REJEITADA: acopla a disponibilidade
  do pedido à do faturamento (o problema que queremos evitar).

### Consequências   # o que se ganha E o que se aceita perder
+ O pedido não falha se o faturamento cai.
− Há latência até o faturamento processar (trade-off aceito).
O ADR captura o porquê: contexto, decisão, alternativas rejeitadas (e por quê) e consequências aceitas. É a estrutura que ancora cada tópico deste livro.
decisao_viva.txttexto
  A DECISÃO REGISTRADA desfaz as duas misérias:

  Futuro engenheiro pensa: "essa chamada assíncrona é estranha,
  vou simplificar para uma chamada direta..."
        │
        ▼  encontra o ADR
  ┌──────────────────────────────────────────────────────┐
  │ "Alternativa: chamada síncrona — REJEITADA porque     │
  │  acopla a disponibilidade dos dois serviços."         │
  └──────────────────────────────────────────────────────┘
        │
        ▼
  Entende o porquê → NÃO reintroduz o problema (não repete o erro)
                  → pode revisar COM consciência (não teme às cegas)

  A sabedoria sobreviveu a quem decidiu.
O ADR encontrado no futuro evita que se reintroduza o problema que a decisão evitava, e permite revisar com consciência. A razão sobrevive a quem decidiu.

A Verificação

adr_test.pyPython
class TestDecisaoComoAtivo(TestCase):
    def test_decisao_significativa_tem_adr(self):
        # Toda decisão de arquitetura significativa está registrada.
        for decisao in self.decisoes_de_arquitetura():
            self.assertTrue(decisao.tem_adr(),
                f"Decisão sem ADR: {decisao}")  // porquê não registrado

    def test_adr_captura_o_porque_completo(self):
        # O ADR captura contexto, alternativas e consequências — não só "o quê".
        for adr in self.todos_os_adrs():
            self.assertTrue(adr.tem_contexto())
            self.assertTrue(adr.tem_alternativas_rejeitadas())  // o porquê do não
            self.assertTrue(adr.tem_consequencias())
Os testes garantem o método: decisões significativas têm ADR, e cada ADR captura o porquê completo — contexto, alternativas rejeitadas e consequências, não só o resultado.
Tabela de decisão Decisão esquecida vs decisão como ativo
AspectoSó o resultado ficaDecisão registrada (ADR)
O porquêNa cabeça de quem decidiuRegistrado, durável
Alguém quer "corrigir"Reintroduz o problemaLê o porquê, evita o erro
Parte incompreendidaCódigo assombrado, intocávelRevisável com consciência
Quando alguém saiA razão vai juntoFica nos ADRs
Memória da equipeSó de resultadosDe raciocínio

Estudo de Caso

Caso aplicado

O conselho que deixou o raciocínio registrado

Contexto. Decisões estratégicas e de arquitetura importantes na plataforma — sobre posicionamento, sobre estrutura técnica — corriam o risco de serem tomadas e esquecidas, com o resultado persistindo mas o raciocínio se perdendo, deixando futuras pessoas sem entender por que as coisas eram como eram.

Decisão esquecida
Porquê some, repete-se ou teme-se
Decisão como ativo
Raciocínio registrado e durável
Ganho
Memória de raciocínio que sobrevive

Decisão. Tratar as decisões significativas como ativos, registrando-as no formato de ADR: o contexto que as motivou, a decisão tomada, as alternativas consideradas (com as razões de rejeição) e as consequências aceitas. Uma análise estruturada — ponderando perspectivas distintas antes de decidir — passou a deixar não só a conclusão, mas o caminho do raciocínio documentado.

Resultado. O porquê das decisões deixou de viver apenas em cabeças e passou a permanecer como ativo. Quem, depois, considerasse reverter uma escolha encontrava o registro do raciocínio — incluindo as alternativas já avaliadas e rejeitadas —, evitando repetir erros e podendo revisar com consciência. A sabedoria das decisões passou a sobreviver à rotatividade das pessoas, e a equipe acumulou uma memória de raciocínio, não apenas de resultados. É exatamente o método que este livro pratica: cada tópico ancora sua decisão num ADR, porque registrar o porquê é o que mantém o sistema compreensível contra o esquecimento.

Incidente Real

Severidade · Média

A "correção" que reintroduziu um problema já resolvido

  • Antes Uma escolha deliberada de arquitetura havia sido feita para evitar um problema específico, mas o porquê dessa escolha não estava registrado de forma acessível.
  • Sintoma Tempos depois, a escolha foi interpretada como desnecessária ou subótima e "corrigida" — reintroduzindo exatamente o problema que a decisão original evitava, que a equipe teve então de rediagnosticar e resolver de novo.
  • Diagnóstico A razão da decisão original não fora registrada como ativo; sem o porquê acessível, a alternativa já rejeitada foi readotada, repetindo um erro evitável.
  • Correção A decisão (e a razão de rejeitar a alternativa) foi registrada como ADR; decisões significativas passaram a ser documentadas com contexto, alternativas e consequências.
  • Kaizen Vira regra: decisões de arquitetura significativas são registradas como ADRs, capturando o porquê e as alternativas rejeitadas — para que erros já resolvidos não se repitam.

Decisão Arquitetural

ADR-0072

Decisões de arquitetura são registradas como ativos (método de ADRs)

Aceito
Contexto

O resultado de uma decisão persiste no sistema, mas o porquê costuma viver só na cabeça de quem decidiu — e cabeças esquecem, pessoas saem. Sem o porquê registrado, repete-se um erro já resolvido (a "correção" que reintroduz o problema evitado) ou teme-se mexer no que ninguém entende (código assombrado). A sabedoria evapora com a rotatividade.

Decisão

Cada decisão de arquitetura significativa é registrada como um ativo durável, no formato de ADR: contexto (as forças em jogo), decisão (o que se escolheu), alternativas (o que foi rejeitado e por quê — contra a repetição de erros) e consequências (o que se ganha e o que se aceita perder). O porquê sobrevive a quem decidiu. É a elevação dos ADRs de artefato pontual a método deliberado — o método que este livro pratica em cada tópico.

Alternativas consideradas

Não registrar o porquê — repete erros ou teme mudanças. Registrar só o resultado — falta a razão e as alternativas. Documentação narrativa dispersa — o porquê se perde no volume; o ADR é focado e durável.

Consequências
PositivasO porquê permanece. Erros já resolvidos não se repetem. Partes incompreendidas viram decisões revisáveis com consciência. Memória de raciocínio sobrevive à rotatividade.
NegativasExige a disciplina de registrar decisões e manter os ADRs acessíveis.
RiscosADRs podem ficar desatualizados; mitigado por marcá-los como substituídos quando uma nova decisão os revoga, preservando o histórico do raciocínio.

Antipadrões

Tomar decisões sem registrar o porquê

Decidir e seguir em frente, deixando a razão só na memória. Ela evapora, e o erro se repete ou o medo paralisa. Correção: registrar a decisão como ativo (ADR) com seu porquê.

Registrar o que, mas não as alternativas rejeitadas

Documentar a decisão sem dizer o que foi avaliado e descartado. Quem vier não saberá por que não fazer "o óbvio". Correção: o ADR registra as alternativas e por que foram rejeitadas.

Exercício

Transforme as suas decisões em ativos

  1. Pegue uma parte do seu sistema que parece estranha ou complexa. Você sabe por que foi feita assim? Se sim, isso está registrado em algum lugar, ou só na sua cabeça?
  2. Faça o teste do recém-chegado: alguém que entrasse na equipe hoje entenderia por que as principais decisões de arquitetura foram tomadas, ou encontraria escolhas órfãs cujo porquê se perdeu?
  3. Escolha uma decisão importante e escreva o ADR: qual era o contexto, o que se decidiu, que alternativas foram consideradas e por que rejeitadas, e quais consequências se aceitaram?
  4. Procure um "código assombrado": uma parte que ninguém mexe por medo. Investigue e registre o porquê dela — transformando o medo do desconhecido numa decisão compreendida e revisável.
Meta: começar a registrar as suas decisões de arquitetura como ativos (ADRs) — capturando o porquê, não só o resultado —, para que a sabedoria sobreviva à rotatividade e o sistema não vire uma caixa-preta de escolhas órfãs.
✓ Checklist do tópico
  • As decisões de arquitetura significativas são registradas (não só seu resultado)
  • Cada registro captura o contexto, a decisão, as alternativas e as consequências
  • As alternativas rejeitadas e o porquê da rejeição ficam documentados
  • O porquê das decisões sobrevive à saída de quem as tomou
  • Os ADRs são acessíveis e mantidos (marcados como substituídos quando revogados)
Meta Dados · Engenharia de Plataformas · Capítulo XIV · §14.2Próximo → 14.3 Governança sem Burocracia
Capítulo XIV · Arquitetura Empresarial
14.3

Governança sem Burocracia

A governança técnica vive entre dois abismos: a anarquia, onde sem padrões cada um diverge e o sistema vira um caos de soluções incompatíveis; e a burocracia, onde tudo precisa de aprovação e nada se move. O caminho entre eles é alinhar por construção — tornar o jeito certo o jeito fácil —, não por portões.

Pré-requisitos · 14.1 · 14.2 · Cap X §10.5 Caminho PavimentadoConecta com · 14.4 Coerência · 14.5 EvoluirTempo de leitura · ~14 min

O Problema

Quando muitas pessoas e equipes constroem partes de um mesmo sistema grande, surge a questão da governança técnica: como garantir que essas partes sejam coerentes, sigam boas práticas e funcionem juntas, em vez de divergirem cada uma para o seu lado? É uma questão real — sem nenhuma coordenação, um sistema construído por muitos vira uma babel. Mas a governança é traiçoeira, porque as duas respostas intuitivas são ambas desastrosas, em direções opostas. Ela vive entre dois abismos, e cair em qualquer um é fatal.

O primeiro abismo é a anarquia: a ausência de governança. Sem nenhum padrão ou direção comum, cada equipe escolhe suas próprias tecnologias, seus próprios jeitos de fazer as coisas, seus próprios padrões — e o resultado é o caos. O sistema vira uma colcha de retalhos incompatíveis: dez formas diferentes de fazer a mesma coisa, nenhuma delas reutilizável, integrações que não encaixam, conhecimento que não se transfere entre equipes porque cada uma fala uma língua diferente. Há retrabalho massivo (cada equipe reinventa o que outra já fez), inconsistência (o sistema se comporta diferente em cada canto) e fragilidade (peças que não foram pensadas para funcionar juntas). A anarquia parece liberdade, mas produz um sistema ingovernável e uma organização onde ninguém se beneficia do trabalho dos outros. O segundo abismo, a reação natural ao primeiro, é a burocracia: o excesso de governança. Para evitar o caos, institui-se que toda decisão precisa de aprovação — um comitê que revisa cada escolha técnica, um processo formal para cada mudança, uma autorização para cada passo. E isso estrangula. As equipes ficam paradas esperando aprovações; cada decisão, por menor que seja, vira um pedido que entra numa fila; a inovação morre porque experimentar exige permissão; o comitê vira um gargalo por onde tudo precisa passar e onde tudo se acumula. A burocracia traz consistência ao preço da paralisia — um sistema "coerente" que mal evolui, porque mover qualquer coisa custa um processo. E há uma armadilha: a burocracia costuma ser a resposta à anarquia, e a anarquia, a reação à burocracia — organizações oscilam entre os dois abismos, cada um parecendo a cura do outro, sem perceber que ambos são doenças. O problema de fundo é tratar a governança como uma escolha entre liberdade sem ordem e ordem sem liberdade, quando a arquitetura empresarial precisa, justamente, das duas: a ordem que mantém o sistema coerente e a liberdade que o mantém vivo.

Os dois abismos da governança

Anarquia: sem padrões, cada equipe diverge, e o sistema vira um caos de soluções incompatíveis e retrabalho. Burocracia: tudo precisa de aprovação, as equipes ficam paradas em filas, e nada se move. Organizações oscilam entre os dois. Correção: alinhar por construção — tornar o jeito certo o jeito fácil (caminho pavimentado), com guardrails que orientam em vez de portões que travam.

A Decisão

A governança alinha por construção, não por aprovação: define a direção e os limites (guardrails) e os incorpora num caminho pavimentado (§10.5) em que o jeito certo é o jeito mais fácil. As equipes seguem o padrão porque ele é o caminho de menor esforço — não porque um comitê as obriga.

A plataforma encontra o caminho entre os dois abismos com um princípio que o Capítulo X já apresentou e que aqui se revela como a chave da governança: alinhar por construção, tornando o jeito certo o jeito fácil. Em vez de impor o alinhamento por aprovação (a burocracia) ou abandoná-lo à sorte (a anarquia), a governança define a direção comum e a incorpora num caminho pavimentado (§10.5): um conjunto de padrões prontos, ferramentas e defaults em que seguir a boa prática é o caminho de menor esforço. A diferença é profunda. Numa governança por aprovação, a equipe que quer fazer a coisa certa precisa esperar um comitê liberar; numa governança por construção, a equipe que quer fazer a coisa certa simplesmente usa o caminho pavimentado, que já incorpora a coisa certa, e segue em frente sozinha, sem fila. O padrão não é imposto por força — é adotado por conveniência, porque é mais fácil seguir o caminho pavimentado do que construir do zero. Assim se obtém o alinhamento (todos seguem o mesmo padrão, porque é o mais fácil) sem a burocracia (ninguém espera aprovação para fazer o certo). Os instrumentos dessa governança são guardrails, não portões. Um portão é um ponto onde tudo para até alguém autorizar — o mecanismo da burocracia. Um guardrail é um limite que orienta sem travar: como as proteções de uma estrada, ele mantém o tráfego no caminho certo sem exigir que cada carro pare para pedir permissão. Na prática, os guardrails da plataforma são as fronteiras de contexto que estruturam os domínios (§14.1), os padrões embutidos no caminho pavimentado (§10.5), as definições compartilhadas como a camada semântica (§11.5), e a postura verificável que checa automaticamente se os limites estão sendo respeitados (§12.5) — tudo isso orienta e alinha por construção e por verificação, sem precisar de um comitê aprovando cada passo. E a governança que sobra para decisões humanas concentra-se no que realmente exige julgamento coletivo — as poucas escolhas estruturais de grande impacto, registradas como ADRs (§14.2) —, em vez de se diluir em aprovar cada decisão trivial. As consequências escapam aos dois abismos. Há alinhamento sem caos: as equipes seguem padrões comuns, então o sistema é coerente, o trabalho se reutiliza e o conhecimento se transfere — mas porque o caminho pavimentado torna isso fácil, não porque um processo as obriga. E há autonomia sem anarquia: as equipes se movem rápido, decidem por si, experimentam — mas dentro de guardrails que mantêm a coerência, não em terra de ninguém. A governança deixa de ser uma escolha entre ordem e liberdade e passa a ser o que a arquitetura empresarial precisa: a estrutura que dá as duas ao mesmo tempo — coerência e velocidade, alinhamento e autonomia. É o mesmo espírito que percorre o livro inteiro, de "negar por padrão" (§7.1) a "o jeito fácil é o jeito certo" (§10.5): a melhor forma de garantir um comportamento não é policiá-lo, é tornar o caminho desejado o caminho natural. Governar bem, na escala da empresa, é pavimentar o caminho certo tão bem que segui-lo seja mais fácil do que se desviar — e reservar a deliberação humana para as poucas encruzilhadas que de fato a merecem.

Diagrama conceitual Entre a anarquia e a burocracia, o caminho pavimentado
ANARQUIAsem padrão, cada um divergecaos, retrabalho BUROCRACIAtudo precisa de aprovaçãonada se move, gargalo CAMINHO PAVIMENTADOo padrão é o jeito fácilalinha por default,não por aprovação guardrails que orientam, sem portões que travam a governança define a direção; o caminho fácil faz todos a seguirem
Figura 14.3-a. A anarquia (sem padrões) gera caos; a burocracia (tudo aprovado) trava tudo. A governança por construção — o caminho pavimentado (§10.5), com guardrails que orientam em vez de portões que travam — alinha por default, dando coerência e velocidade ao mesmo tempo.

Guardrails que orientam, não portões que travam

governanca.txttexto
  GUARDRAIL (orienta, não trava)        PORTÃO (trava até aprovar)
  ┌─────────────────────────────┐       ┌─────────────────────────────┐
  │ caminho pavimentado (§10.5) │       │ comitê aprova cada decisão  │
  │ → o padrão é o jeito FÁCIL  │       │ → equipe espera na fila     │
  │ fronteiras de contexto §14.1│       │ → gargalo, nada se move     │
  │ camada semântica §11.5      │       │ → inovação pede permissão   │
  │ postura verificável §12.5   │       │                             │
  │ → equipe segue SOZINHA      │  vs   │ → burocracia paralisa       │
  └─────────────────────────────┘       └─────────────────────────────┘
       alinhamento + velocidade               consistência − velocidade

  Deliberação humana: SÓ para as poucas decisões estruturais (→ ADR §14.2).
Guardrails (caminho pavimentado, fronteiras, semântica, verificação) orientam e alinham por construção; portões de aprovação travam. A deliberação humana fica para o que importa.
alinhar_por_construcao.pyPython
# ALINHAR POR CONSTRUÇÃO: o padrão é o caminho de MENOR esforço.
def criar_novo_servico(time: Time, nome: str) -> Servico:
    # O time usa o caminho pavimentado e segue SOZINHO — sem fila.
    servico = caminho_pavimentado.criar(nome)   # §10.5

    # O padrão (fronteiras, semântica, segurança) vem embutido —
    # seguir o certo foi o jeito FÁCIL, não uma aprovação imposta.
    afirmar(servico.respeita_guardrails())   # verificado §12.5, não aprovado

    return servico
    # Alinhamento SEM burocracia; autonomia SEM anarquia.
O time cria o serviço pelo caminho pavimentado e segue sozinho, com o padrão embutido. O alinhamento vem por construção e é verificado — não imposto por aprovação.

A Verificação

governanca_test.pyPython
class TestGovernancaSemBurocracia(TestCase):
    def test_seguir_o_padrao_nao_exige_aprovacao(self):
        # Um time cria um serviço pelo caminho pavimentado, sem fila.
        servico = criar_novo_servico(self.time, "novo")
        self.assertFalse(self.precisou_de_aprovacao_de_comite())  // sem portão

    def test_guardrail_mantem_o_alinhamento(self):
        # Mesmo sem aprovação, o serviço respeita os guardrails (coerência).
        servico = criar_novo_servico(self.time, "novo")
        self.assertTrue(servico.respeita_guardrails())  // alinhado por construção
Os testes provam o equilíbrio: seguir o padrão não exige aprovação (sem burocracia), e ainda assim o resultado respeita os guardrails (sem anarquia). As duas coisas juntas.
Tabela de decisão Anarquia vs burocracia vs governança por construção
AspectoAnarquiaBurocraciaPor construção
PadrõesNenhumImpostosPavimentados
VelocidadeAltaTravadaAlta
CoerênciaCaosSimSim
MecanismoNadaPortões/aprovaçãoGuardrails
Decisão trivialLivreVai a comitêSegue sozinha

Estudo de Caso

Caso aplicado

Alinhar muitas equipes sem virar comitê

Contexto. Com muitas frentes construindo partes da plataforma, havia o risco de cair num dos dois abismos: sem coordenação, as equipes divergiriam num caos de soluções incompatíveis; com coordenação pesada, um processo de aprovação central viraria gargalo, travando todos.

Anarquia/Burocracia
Caos ou gargalo
Por construção
Caminho pavimentado, guardrails
Ganho
Coerência e velocidade juntas

Decisão. Governar por construção: definir a direção comum e incorporá-la num caminho pavimentado (§10.5) em que o jeito certo é o mais fácil, com guardrails que orientam (fronteiras de contexto §14.1, definições compartilhadas §11.5, verificação automática §12.5) em vez de portões que travam — e reservar a deliberação humana, registrada em ADRs (§14.2), apenas para as poucas decisões estruturais de grande impacto.

Resultado. As equipes passaram a se alinhar por seguir o caminho pavimentado — porque era o mais fácil —, obtendo coerência sem esperar aprovações, e a se mover com autonomia dentro de guardrails que mantinham o sistema coerente, sem cair na anarquia. A governança deixou de ser uma escolha entre ordem e liberdade e passou a entregar as duas: alinhamento e velocidade ao mesmo tempo. A oscilação entre o caos e o comitê deu lugar a uma estrutura que pavimenta o caminho certo tão bem que segui-lo é mais fácil do que desviar.

Incidente Real

Severidade · Média

O processo que virou gargalo (ou a divergência que virou caos)

  • Antes Para coordenar muitas frentes, havia a tentação de exigir aprovação central para decisões técnicas — ou, no extremo oposto, de não ter padrão algum, deixando cada frente divergir.
  • Sintoma O excesso de aprovação tendia a travar as equipes em filas, enquanto a ausência de padrões tendia a gerar soluções incompatíveis e retrabalho — os dois abismos da governança, cada um surgindo como reação ao outro.
  • Diagnóstico A governança estava sendo pensada como escolha entre ordem (por aprovação) e liberdade (sem padrão), em vez de alinhar por construção com guardrails.
  • Correção Adoção da governança por construção: caminho pavimentado em que o certo é o fácil, guardrails que orientam e verificam, e deliberação humana só para o estrutural (ADRs).
  • Kaizen Vira regra: governa-se por construção, não por aprovação; o padrão é o caminho fácil, guardrails orientam, e só as poucas decisões estruturais vão à deliberação.

Decisão Arquitetural

ADR-0073

Governança por construção (caminho pavimentado e guardrails), não por aprovação

Aceito
Contexto

A governança técnica vive entre dois abismos: a anarquia (sem padrões, as equipes divergem num caos de soluções incompatíveis e retrabalho) e a burocracia (tudo precisa de aprovação, as equipes ficam em filas, nada se move). Organizações oscilam entre os dois, cada um parecendo a cura do outro, sem perceber que ambos são doenças.

Decisão

A governança alinha por construção: define a direção e a incorpora num caminho pavimentado (§10.5) em que o jeito certo é o mais fácil, com guardrails que orientam (fronteiras §14.1, semântica §11.5, verificação §12.5) em vez de portões que travam. As equipes seguem o padrão por conveniência e se movem sozinhas; a deliberação humana (ADRs §14.2) fica só para as poucas decisões estruturais. Dá coerência e velocidade ao mesmo tempo.

Alternativas consideradas

Anarquia (sem governança) — caos, retrabalho, incompatibilidade. Burocracia (aprovar tudo) — gargalo, paralisia. Padrões só recomendados (sem o caminho fácil) — ignorados, voltam à anarquia.

Consequências
PositivasAlinhamento sem caos (todos no mesmo padrão, por conveniência). Autonomia sem anarquia (rápido, dentro de guardrails). Coerência e velocidade juntas. Deliberação reservada ao que importa.
NegativasExige investir em construir e manter o caminho pavimentado e os guardrails.
RiscosGuardrails frouxos demais voltam à anarquia, rígidos demais viram burocracia disfarçada; mitigado por calibrá-los e permitir sair do caminho quando justificado.

Antipadrões

Exigir aprovação para cada decisão

Um comitê que revisa toda escolha técnica. Vira gargalo e paralisa. Correção: alinhar por construção (caminho pavimentado); deliberação humana só para o estrutural.

Abandonar os padrões à sorte de cada equipe

Sem direção comum, cada frente diverge num caos incompatível. Correção: guardrails e caminho pavimentado que tornam o padrão o jeito fácil.

Exercício

Encontre o seu abismo de governança

  1. Pergunte: na sua organização, como se garante que as partes do sistema sejam coerentes? Há padrões comuns, ou cada equipe diverge? E seguir um padrão exige aprovação, ou é o caminho fácil?
  2. Faça o teste do abismo: vocês estão mais perto da anarquia (caos de soluções incompatíveis, retrabalho) ou da burocracia (filas de aprovação, nada se move)? Ou oscilam entre os dois?
  3. Identifique um portão: existe alguma aprovação que trava as equipes para decisões que não precisariam de deliberação coletiva? Esse é um candidato a virar guardrail.
  4. Esboce um caminho pavimentado: como tornar o jeito certo de fazer algo (seguir o padrão, respeitar as fronteiras) o caminho de menor esforço, de modo que as equipes o sigam sem serem obrigadas?
Meta: identificar se a sua governança caiu na anarquia ou na burocracia — e planejar alinhar por construção, com um caminho pavimentado e guardrails, para obter coerência e velocidade ao mesmo tempo.
✓ Checklist do tópico
  • O alinhamento vem por construção (caminho pavimentado), não por aprovação
  • Seguir o padrão certo é o caminho de menor esforço para as equipes
  • Usam-se guardrails que orientam (fronteiras, semântica, verificação), não portões que travam
  • As equipes se movem com autonomia, sem esperar aprovação para o trivial
  • A deliberação humana (ADRs) é reservada às poucas decisões estruturais de grande impacto
Meta Dados · Engenharia de Plataformas · Capítulo XIV · §14.3Próximo → 14.4 Coerência entre Muitos Produtos
Capítulo XIV · Arquitetura Empresarial
14.4

Coerência entre Muitos Produtos

Uma plataforma de muitos produtos enfrenta uma tensão: ou cada produto é um mundo isolado — uma colcha de retalhos onde nada se compartilha e o usuário se perde — ou todos são forçados no mesmo molde rígido e nenhum pode ser o que precisa ser. A saída é compartilhar a base e libertar o topo.

Pré-requisitos · 14.1 · 14.3 · Cap V Design · §11.5 Semântica · §10.5Conecta com · 14.5 EvoluirTempo de leitura · ~14 min

O Problema

Uma plataforma madura raramente é um único produto — é um ecossistema de muitos: ferramentas para públicos diferentes, aplicações para necessidades distintas, produtos que nasceram em momentos diferentes e resolvem problemas diferentes, todos sob a mesma marca, todos parte da mesma família. E isso cria uma tensão de coerência que, mal resolvida, leva a um de dois extremos opostos, ambos ruins. A questão é: como fazer muitos produtos serem, ao mesmo tempo, uma família coerente e cada um o que precisa ser?

O primeiro extremo ruim é a colcha de retalhos: cada produto é um mundo isolado, construído por uma equipe diferente, em um momento diferente, sem nada em comum com os outros. Cada um tem sua própria identidade visual, seu próprio jeito de fazer login, sua própria forma de chamar as mesmas coisas, sua própria interpretação dos mesmos dados. O resultado é desorientador para quem usa: um usuário que transita entre dois produtos da mesma plataforma sente que mudou de empresa — a aparência é outra, os padrões são outros, o vocabulário é outro. Não há reconhecimento, não há a sensação de família. E é desperdiçador para quem constrói: cada produto reinventa a autenticação, o design, as definições de dados que outro já fez, num retrabalho massivo, e uma melhoria feita em um produto não beneficia os outros. A colcha de retalhos é muitos produtos que por acaso compartilham uma marca, mas nada mais — um ecossistema só no nome. O segundo extremo, a reação a esse, é o molde rígido: para garantir coerência, força-se todos os produtos a serem idênticos — a mesma estrutura, o mesmo layout, as mesmas escolhas, sem espaço para que nenhum se adapte às suas necessidades específicas. E isso sufoca. Produtos diferentes resolvem problemas diferentes e têm públicos diferentes; forçá-los no mesmo molde impede que cada um seja bom no que faz. A ferramenta que precisa de uma interface densa de dados é espremida no mesmo molde da que precisa de simplicidade; o produto voltado a especialistas é forçado nos mesmos padrões do voltado a iniciantes. O molde rígido traz uniformidade ao preço da adequação — produtos "coerentes" que são todos medíocres porque nenhum pôde se moldar ao seu propósito. O problema de fundo é tratar a coerência como uma escolha entre diversidade sem unidade (a colcha de retalhos) e unidade sem diversidade (o molde rígido), quando um ecossistema saudável precisa das duas: a unidade que faz os produtos se reconhecerem como família, e a diversidade que deixa cada um ser o que precisa ser. Uniformidade não é coerência, e variedade não é caos — confundir esses pares é o que leva aos dois extremos.

Colcha de retalhos ou molde rígido

Colcha de retalhos: cada produto é um mundo isolado, nada se compartilha, o usuário se perde e o trabalho se duplica. Molde rígido: todos forçados a serem idênticos, nenhum pode se adequar ao seu propósito. Correção: compartilhar uma base comum (identidade, design, dados) e libertar cada produto a se diferenciar no topo — coeso, não uniforme.

A Decisão

Os produtos compartilham uma base comum — identidade, linguagem de design (Cap V), definições de dados (§11.5), serviços de plataforma (§10.5) — sobre a qual cada um é livre para se diferenciar conforme seu propósito. A coerência vem da base partilhada; a diversidade, da liberdade no topo.

A plataforma resolve a tensão com um princípio de camadas de partilha: há uma base comum que todos os produtos compartilham, e um topo livre onde cada produto se diferencia. A coerência não vem de forçar os produtos a serem iguais — vem de eles repousarem sobre o mesmo fundamento. Essa base comum reúne o que o livro já construiu, agora visto como o tecido que une o ecossistema. Uma identidade compartilhada: a mesma marca, a mesma forma de se autenticar, a mesma noção de quem é o usuário — de modo que transitar entre produtos não exige reaprender quem se é nem fazer login de novo. Uma linguagem de design comum (Cap V): um sistema de design — cores, tipografia, componentes, padrões de interação — que todos os produtos usam, de modo que eles se parecem família, têm um ar comum reconhecível, mesmo sendo distintos. Definições de dados compartilhadas (§11.5, a camada semântica): quando vários produtos falam de "receita" ou "cliente", falam da mesma coisa, porque a definição é única e partilhada — não cada produto inventando a sua, o que recriaria a babel que o §11.5 combateu, agora na escala do ecossistema. E serviços de plataforma comuns (§10.5, o caminho pavimentado): a infraestrutura, a observabilidade, a segurança que todos os produtos herdam por construção, em vez de cada um reconstruir. Sobre essa base, cada produto é livre no topo: pode ter sua própria estrutura, suas próprias telas, suas próprias funcionalidades específicas, adaptando-se ao seu público e ao seu propósito — usando os componentes comuns do design, mas compondo-os como precisa; falando das entidades comuns, mas com as telas que seu trabalho exige. A diferença entre isso e os dois extremos é precisa. Contra a colcha de retalhos: os produtos compartilham a base, então há reconhecimento (parecem família), os dados são consistentes (a mesma "receita" em todos), e o trabalho se reutiliza (ninguém reinventa a autenticação ou o design) — não são mais mundos isolados. Contra o molde rígido: cada produto é livre no topo, então pode se adequar ao seu propósito, ser bom no que faz, diferenciar-se conforme precisa — não são mais cópias espremidas num único formato. O resultado é um ecossistema de verdade: muitos produtos distintos que se reconhecem como família — coerentes sem serem uniformes, variados sem serem um caos. A coerência que importa não é a uniformidade (todos iguais), é a unidade (todos pertencendo ao mesmo todo, partilhando um fundamento), e essa unidade convive perfeitamente com a diversidade no topo — aliás, depende dela, porque um ecossistema sem diversidade não é um ecossistema, é uma monocultura. É a aplicação, na escala dos produtos, dos princípios que percorrem o livro: compartilhar o essencial uma vez (o design, os dados, a plataforma — definidos num lugar, usados por todos, como o metadado e a camada semântica) e deixar o resto livre. Manter muitos produtos coerentes sem enrijecê-los é, no fundo, saber distinguir o que deve ser comum (a base que une) do que deve ser livre (o topo que diferencia) — e partilhar o primeiro tão bem que compartilhá-lo seja mais fácil do que reinventá-lo, deixando cada produto gastar sua energia não em refazer o fundamento, mas em ser excelente naquilo que só ele faz.

Arquitetura lógica Base comum compartilhada, topo livre por produto
COLCHA DE RETALHOScada produto, um mundonada se compartilha MOLDE RÍGIDOtodos iguais à forçanenhum se diferencia ECOSSISTEMAbase comum compartilhada+ liberdade no topocoeso, não uniforme identidade comum design §Cap V dados §11.5 compartilhar a base; deixar cada produto ser ele mesmo no topo
Figura 14.4-a. A colcha de retalhos não compartilha nada (usuário se perde); o molde rígido força todos iguais (nenhum se adequa). O ecossistema compartilha uma base comum (identidade, design Cap V, dados §11.5) e liberta cada produto no topo — coeso, não uniforme.

A base que se compartilha, o topo que se diferencia

base_compartilhada.txttexto
  ECOSSISTEMA: base comum + topo livre (coeso, não uniforme)

  ┌── Produto A ──┐  ┌── Produto B ──┐  ┌── Produto C ──┐
  │ telas/fluxos  │  │ telas/fluxos  │  │ telas/fluxos  │  ← TOPO LIVRE
  │ próprios      │  │ próprios      │  │ próprios      │    (cada um seu)
  └───────┬───────┘  └───────┬───────┘  └───────┬───────┘
          └──────────────────┼──────────────────┘
  ╔═══════════════════════════════════════════════════════╗
  ║  BASE COMUM compartilhada por TODOS:                   ║
  ║   • identidade / autenticação única                    ║
  ║   • linguagem de design (Cap V) — parecem família      ║
  ║   • definições de dados (§11.5) — "receita" = a mesma  ║
  ║   • serviços de plataforma (§10.5) — herdados          ║
  ╚═══════════════════════════════════════════════════════╝
  Coerência = unidade na base, NÃO uniformidade no topo.
Os produtos repousam sobre uma base comum (identidade, design, dados, plataforma) e se diferenciam no topo. A coerência vem do fundamento partilhado, não de telas idênticas.
produto.phpPHP
// Cada produto USA a base comum e se diferencia no topo.
final class ProdutoLogistica extends ProdutoDaPlataforma
{
    public function montarInterface(): Interface
    {
        // BASE COMUM: componentes do design compartilhado (Cap V).
        $ui = $this->design->novaTela();   // mesma linguagem visual

        // TOPO LIVRE: compõe conforme o SEU propósito (densa em dados).
        $ui->adicionar($this->painelDeRotas());     // específico deste produto
        $ui->adicionar($this->mapaDeEntregas());

        return $ui;   // parece família (base), mas é ele mesmo (topo)
    }
    // "Cliente"/"entrega" vêm das definições comuns (§11.5) — consistentes.
}
O produto usa os componentes do design comum (base) e os compõe conforme seu propósito (topo). Parece família pela base, é ele mesmo pela liberdade no topo.

A Verificação

coerencia_test.phpPHP
final class CoerenciaEcossistemaTest extends TestCase
{
    public function test_produtos_compartilham_a_base(): void
    {
        // Todos os produtos usam a MESMA identidade e design (família).
        foreach ($this->produtos() as $p) {
            $this->assertUsa($p, 'identidade-comum');
            $this->assertUsa($p, 'design-compartilhado');
            $this->assertUsa($p, 'definicoes-de-dados');  // §11.5
        }
    }

    public function test_produtos_podem_diferir_no_topo(): void
    {
        // Cada produto tem suas próprias telas/fluxos — não há molde rígido.
        $this->assertNotEquals(
            $this->produto('A')->telas(),
            $this->produto('B')->telas());  // livres no topo
    }
}
Os testes garantem as duas coisas: todos os produtos compartilham a base (família), e cada um pode diferir no topo (não há molde rígido). Coeso e diverso ao mesmo tempo.
Tabela de decisão Colcha vs molde vs ecossistema
AspectoColcha de retalhosMolde rígidoEcossistema
BaseNada comumComumComum
TopoLivreIdêntico à forçaLivre
Usuário reconhece a famíliaNãoSimSim
Produto se adequa ao propósitoSimNãoSim
ResultadoCaosMonoculturaCoeso e diverso

Estudo de Caso

Caso aplicado

Um ecossistema de produtos que se reconhecem como família

Contexto. A plataforma abrange muitos produtos — para logística, dados, recrutamento, armazenamento, treinamento e mais —, voltados a públicos e propósitos distintos. Havia o risco de eles virarem uma colcha de retalhos (cada um um mundo isolado, desorientando o usuário e duplicando trabalho) ou de serem forçados num molde rígido que impediria cada um de servir bem ao seu propósito.

Colcha/Molde
Caos ou monocultura
Ecossistema
Base comum, topo livre
Ganho
Coeso e diverso ao mesmo tempo

Decisão. Estruturar os produtos como um ecossistema sobre uma base comum: identidade e autenticação únicas, uma linguagem de design compartilhada (Cap V), definições de dados comuns (§11.5, a camada semântica) e serviços de plataforma herdados (§10.5) — deixando cada produto livre, no topo, para ter suas próprias telas, fluxos e funcionalidades conforme seu público e propósito.

Resultado. Os produtos passaram a se reconhecer como família: um usuário que transita entre eles encontra a mesma identidade, o mesmo ar visual e as mesmas definições de dados, sem sentir que mudou de empresa. E, ao mesmo tempo, cada produto pôde se adequar ao que faz — o denso em dados sendo denso, o simples sendo simples —, sem o sufoco de um molde único. O trabalho da base deixou de ser reinventado por cada produto, e a energia de cada um voltou-se para ser excelente naquilo que só ele faz. A plataforma tornou-se um ecossistema coerente sem ser uniforme — unidade na base, diversidade no topo.

Incidente Real

Severidade · Média

O produto que parecia de outra empresa

  • Antes Produtos da mesma plataforma haviam sido construídos sem uma base totalmente compartilhada, cada um com elementos próprios de identidade, design ou definições de dados.
  • Sintoma Um usuário que transitava entre certos produtos sentia uma quebra de coerência — aparência, padrões e vocabulário diferentes —, como se tivesse mudado de empresa, enfraquecendo a sensação de família e gerando confusão e retrabalho.
  • Diagnóstico A ausência de uma base comum compartilhada (identidade, design, dados) fazia os produtos derivarem para uma colcha de retalhos, sem o reconhecimento que une um ecossistema.
  • Correção Os produtos foram apoiados numa base comum — identidade, linguagem de design (Cap V) e definições de dados (§11.5) compartilhadas —, mantendo a liberdade de cada um no topo.
  • Kaizen Vira regra: os produtos compartilham uma base comum (identidade, design, dados, plataforma) e se diferenciam no topo; coerência é unidade na base, não uniformidade no topo.

Decisão Arquitetural

ADR-0074

Coerência entre produtos por base comum compartilhada, com liberdade no topo

Aceito
Contexto

Uma plataforma de muitos produtos cai em dois extremos: a colcha de retalhos (cada produto um mundo isolado — usuário se perde, trabalho se duplica) ou o molde rígido (todos forçados idênticos — nenhum se adequa ao seu propósito). Uniformidade não é coerência, e variedade não é caos; confundi-los leva aos extremos.

Decisão

Os produtos compartilham uma base comum — identidade e autenticação únicas, linguagem de design (Cap V), definições de dados (§11.5) e serviços de plataforma (§10.5) — sobre a qual cada um é livre para se diferenciar no topo conforme seu público e propósito. A coerência vem da unidade na base (todos se reconhecem como família); a diversidade, da liberdade no topo (cada um é bom no que faz).

Alternativas consideradas

Colcha de retalhos (nada comum) — desorienta e duplica. Molde rígido (tudo igual) — sufoca o propósito de cada produto. Padronizar só a aparência — sem dados e plataforma comuns, a babel volta.

Consequências
PositivasProdutos se reconhecem como família (base comum). Cada um se adequa ao seu propósito (topo livre). Dados consistentes entre produtos (§11.5). Trabalho da base reutilizado. Ecossistema coeso e diverso.
NegativasExige construir e manter a base comum e a disciplina de os produtos a usarem.
RiscosA linha entre base (comum) e topo (livre) pode ser difícil de traçar; mitigado por compartilhar o que une (fundamento) e libertar o que diferencia, ajustando a fronteira com a experiência.

Antipadrões

Cada produto um mundo isolado

Produtos sem nada em comum — cada um com sua identidade, design e dados. O usuário se perde e o trabalho se duplica. Correção: uma base comum compartilhada que os une como família.

Forçar todos os produtos no mesmo molde

Exigir que todos sejam idênticos para garantir coerência. Nenhum se adequa ao seu propósito. Correção: base comum, mas liberdade no topo para cada produto se diferenciar.

Exercício

Avalie a coerência do seu ecossistema

  1. Se você tem mais de um produto (ou módulo), pergunte: um usuário que transita entre eles sente que são da mesma família, ou que mudou de empresa? O que compartilham, e o que não?
  2. Faça o teste da colcha de retalhos: cada produto reinventa a autenticação, o design, as definições de dados? Ou há uma base comum que todos usam?
  3. Faça o teste do molde rígido: os produtos são forçados a serem idênticos a ponto de algum não conseguir se adequar ao seu propósito? Onde a uniformidade está sufocando a adequação?
  4. Distinga base e topo: o que, no seu caso, deveria ser comum a todos os produtos (a base que une) e o que deveria ser livre em cada um (o topo que diferencia)?
Meta: avaliar se os seus produtos formam uma colcha de retalhos, um molde rígido, ou um ecossistema coerente — e planejar a base comum que os une sem enrijecê-los, deixando cada um livre no topo.
✓ Checklist do tópico
  • Os produtos compartilham uma base comum (identidade, design, dados, plataforma)
  • A identidade e a autenticação são únicas em todo o ecossistema
  • Há uma linguagem de design comum (Cap V) que faz os produtos parecerem família
  • As definições de dados são compartilhadas (§11.5): os mesmos termos significam o mesmo
  • Cada produto é livre, no topo, para se diferenciar conforme seu público e propósito
Meta Dados · Engenharia de Plataformas · Capítulo XIV · §14.4Próximo → 14.5 Evoluir sem Reescrever
Capítulo XIV · Arquitetura Empresarial
14.5

Evoluir sem Reescrever

Diante de um sistema que envelheceu, a tentação é a grande reescrita: parar tudo e refazer do zero. É uma das armadilhas mais caras da engenharia — demora anos, congela a evolução e frequentemente fracassa. A alternativa é evoluir aos poucos, trocando peça por peça com o sistema sempre rodando.

Pré-requisitos · 14.1 Fronteiras · Cap X §10.2 §10.4 · §6.1 ACLConecta com · encerra o Cap XIVTempo de leitura · ~14 min

O Problema

Todo sistema envelhece. As decisões que faziam sentido há anos já não servem; tecnologias melhores surgiram; o que era adequado para a escala de ontem range sob a de hoje. Diante de um sistema que envelheceu, surge uma tentação poderosa e perigosa: a grande reescrita. Parar de evoluir o sistema atual, declarar que ele é velho demais para consertar, e refazer tudo do zero — um sistema novo, limpo, moderno, que substituirá o antigo de uma vez. A proposta é sedutora: livrar-se de toda a bagagem acumulada, começar com a folha em branco, fazer tudo "do jeito certo" desta vez. É também uma das decisões mais caras e mais frequentemente desastrosas da engenharia de software.

A grande reescrita falha por razões estruturais, não por má execução. Primeiro, ela demora muito mais do que se imagina — quase sempre anos —, porque refazer um sistema inteiro significa reconstruir tudo o que ele faz, e um sistema maduro faz uma quantidade imensa de coisas, muitas delas esquecidas ou invisíveis. Segundo, e mais grave, ela congela a evolução durante todo esse tempo: enquanto a reescrita não termina, o sistema antigo não pode receber melhorias (não vale a pena investir no que será descartado) e o novo ainda não está pronto, então a plataforma fica parada — incapaz de evoluir por anos, enquanto concorrentes avançam. Terceiro, a reescrita subestima sistematicamente o que o sistema antigo fazia. Todo aquele código acumulado, por mais feio que pareça, contém anos de aprendizado embutido — cada esquisitice é, muitas vezes, a cicatriz de um caso extremo real que foi descoberto e tratado (exatamente o porquê que o §14.2 ensina a registrar). Reescrevendo do zero, perde-se esse aprendizado: o sistema novo reencontra, um a um, os mesmos casos extremos que o antigo já tinha resolvido, redescobrindo com dor o que já se sabia. Quarto, é uma aposta tudo ou nada: durante todo o longo período da reescrita, não há valor entregue — só ao final, na arriscada "grande virada", quando se desliga o antigo e liga o novo, descobre-se se funciona; e se não funcionar, anos de trabalho podem ruir de uma vez. A história da engenharia está cheia de grandes reescritas que consumiram anos e recursos imensos e foram canceladas, ou que entregaram um sistema novo que era pior do que o velho que substituíram. O problema de fundo é tratar a evolução como um evento descontínuo — parar, refazer, substituir — quando ela deveria ser um processo contínuo. A grande reescrita é a negação de tudo o que o livro defende sobre incrementalismo, entrega contínua e mudança segura: é apostar a sobrevivência do sistema numa única jogada gigante, em vez de evoluí-lo passo a passo, com o todo sempre de pé.

A armadilha da grande reescrita

Parar tudo e refazer do zero demora anos, congela a evolução durante todo esse tempo, subestima o que o sistema antigo fazia (perdendo o aprendizado embutido nas suas cicatrizes) e aposta tudo numa única virada arriscada. Correção: evoluir aos poucos, trocando peça por peça com o sistema sempre rodando — trocar o motor com o carro andando.

A Decisão

O sistema evolui aos poucos, nunca por uma grande reescrita: substitui-se uma peça de cada vez, atrás de sua fronteira (§14.1), com o todo sempre rodando e entregando valor. Cada peça nova convive com as velhas até aposentá-las — trocar o motor com o carro andando.

A plataforma evolui por um princípio que é a antítese da grande reescrita: substituição gradual, peça por peça, com o sistema sempre rodando. Em vez de parar tudo e refazer do zero, identifica-se uma parte do sistema que precisa ser renovada, constrói-se a versão nova dessa parte, e substitui-se a velha pela nova — enquanto todo o resto do sistema continua funcionando e entregando valor. Depois, a próxima parte, e a próxima, num processo contínuo em que o sistema vai sendo renovado de dentro, sem nunca parar. É o que se costuma chamar de "estrangular" o sistema antigo: a cada passo, uma peça nova assume o trabalho de uma peça velha, que é então aposentada, até que, peça por peça, o sistema inteiro tenha sido renovado — sem jamais ter havido uma grande virada, sem jamais ter parado. O que torna isso possível é precisamente a primeira decisão deste capítulo: as fronteiras de contexto (§14.1). Porque o sistema é dividido em domínios com fronteiras claras e contratos explícitos, cada peça pode ser substituída isoladamente: troca-se o que está atrás de uma fronteira sem tocar o resto, porque o resto só dependia do contrato na fronteira, não dos detalhes internos da peça. A peça nova só precisa honrar o mesmo contrato que a velha honrava, e o resto do sistema nem percebe a troca. (Uma camada anticorrupção, §6.1, frequentemente faz a ponte durante a transição, traduzindo entre o velho e o novo enquanto ambos coexistem.) E o que torna isso seguro são as práticas do Capítulo X: a entrega contínua segura (§10.4), com seus portões e seu rollback, permite colocar cada peça nova em produção com confiança e voltar atrás se algo der errado; as imagens imutáveis (§10.2) garantem que cada substituição seja reproduzível. As consequências invertem cada falha da grande reescrita. Em vez de demorar anos sem entregar, cada peça substituída entrega valor imediatamente, e o progresso é contínuo e visível. Em vez de congelar a evolução, o sistema continua evoluindo o tempo todo — a renovação é a evolução, não uma pausa nela. Em vez de perder o aprendizado do sistema antigo, preserva-se peça por peça: cada parte velha continua funcionando (com suas cicatrizes intactas) até que sua substituta esteja pronta e provada, e o conhecimento embutido pode ser estudado e transferido em vez de descartado. E em vez de uma aposta tudo ou nada, cada passo é um pequeno risco contido: se uma peça nova falha, reverte-se aquela peça (§10.4), sem ruir o todo — o risco é fatiado em pedaços pequenos e absorvíveis, em vez de concentrado numa única jogada gigante. É a aplicação à evolução do sistema inteiro de tudo o que o livro construiu: o incrementalismo da entrega contínua, a segurança dos portões e do rollback, a contenção das fronteiras, a reprodutibilidade das imagens — tudo convergindo num modo de evoluir que nunca para, nunca aposta tudo, nunca descarta o aprendizado. Trocar o motor com o carro andando parece impossível só enquanto o carro é um bloco indivisível; quando ele é feito de peças com fronteiras claras, cada peça pode ser trocada com o carro em movimento. A arquitetura que se constrói ao longo de todo este livro — modular, com fronteiras, observável, entregue com segurança — é, no fim, o que torna possível a coisa mais difícil e mais necessária de todas: mudar sem parar. Um sistema só merece durar se puder evoluir; e só pode evoluir, sem morrer no processo, se for construído para ser trocado peça por peça, com a vida sempre correndo por ele.

Fluxograma operacional Substituir peça por peça com o sistema rodando
A GRANDE REESCRITAparar tudo, refazer do zerodemora, congela, fracassa EVOLUIR AOS POUCOStrocar peça por peçacom o sistema sempre rodando peça novaatrás da fronteira §14.1 peça velhaaposentada substitui gradualmente trocar o motor com o carro andando — a fronteira torna a peça substituível
Figura 14.5-a. A grande reescrita para tudo e refaz do zero — demora, congela, fracassa. A evolução gradual troca peça por peça, atrás de sua fronteira (§14.1), com o sistema sempre rodando. A fronteira torna cada peça substituível isoladamente.

A peça nova que estrangula a velha

substituicao_gradual.phpPHP
// EVOLUIR AOS POUCOS: a peça nova assume, atrás da MESMA fronteira (§14.1).
interface ServicoDeEntrega   // o CONTRATO na fronteira (não muda)
{
    public function processar(Pedido $p): Entrega;
}

// A peça VELHA honra o contrato (continua rodando).
final class EntregaLegada implements ServicoDeEntrega { /* ... */ }

// A peça NOVA honra o MESMO contrato — o resto do sistema nem percebe.
final class EntregaRenovada implements ServicoDeEntrega { /* ... */ }

// Troca-se VELHA por NOVA atrás da fronteira; nada mais muda.
// O sistema nunca parou. Trocar o motor com o carro andando.
A peça nova implementa o mesmo contrato da velha na fronteira (§14.1). Troca-se uma pela outra sem o resto do sistema perceber — porque ele só dependia do contrato.
transicao_segura.shBash
# A substituição é SEGURA (Cap X): gradual, com rollback.

# 1) A peça nova entra para uma FRAÇÃO do tráfego (canário §10.4).
rotear-trafego --para entrega-renovada --fracao 5%

# 2) Observa-se (Cap VIII): a peça nova honra o contrato e o SLO (§13.1)?
if metricas-pioraram; then
  rotear-trafego --para entrega-legada --fracao 100%   # ROLLBACK da peça
else
  rotear-trafego --para entrega-renovada --fracao 100%  # peça nova assume
  aposentar entrega-legada                              # velha aposentada
fi
# Risco FATIADO: falhou esta peça? Reverte SÓ ela — o todo segue de pé.
Cada peça nova entra por canário (§10.4), é observada contra o SLO (§13.1), e ou assume ou volta atrás — sozinha. O risco da evolução é fatiado em pedaços absorvíveis.

A Verificação

evolucao_test.phpPHP
final class EvolucaoSemReescritaTest extends TestCase
{
    public function test_peca_nova_honra_o_contrato_da_velha(): void
    {
        // A nova e a velha são intercambiáveis pela fronteira (§14.1).
        $pedido = $this->pedido();
        $this->assertEquivalente(
            (new EntregaLegada())->processar($pedido),
            (new EntregaRenovada())->processar($pedido));
    }

    public function test_substituir_uma_peca_nao_para_o_sistema(): void
    {
        // Durante a troca de uma peça, o sistema segue servindo (sempre de pé).
        with ($this->substituindoUmaPeca()) {
            $this->assertTrue($this->sistemaNoAr());  // nunca parou
        }
    }
}
Os testes provam o método: a peça nova é intercambiável com a velha pelo contrato, e substituí-la não para o sistema — que segue de pé durante toda a evolução.
Tabela de decisão Grande reescrita vs evolução gradual
AspectoGrande reescritaEvolução gradual
AbordagemParar, refazer do zeroTrocar peça por peça
Entrega de valorSó no fim (anos)A cada peça
Evolução nesse meio-tempoCongeladaContínua
Aprendizado do antigoPerdidoPreservado peça a peça
RiscoTudo ou nadaFatiado, contido (§10.4)

Estudo de Caso

Caso aplicado

Renovar o sistema sem nunca pará-lo

Contexto. Partes da plataforma, com o tempo, precisaram ser renovadas — tecnologias antigas, decisões que já não serviam. A tentação seria a grande reescrita: parar e refazer do zero. Mas isso demoraria anos, congelaria a evolução nesse período, perderia o aprendizado embutido no sistema existente e apostaria tudo numa única virada arriscada.

Grande reescrita
Anos parados, aposta tudo ou nada
Evolução gradual
Peça por peça, sempre rodando
Ganho
Mudar sem parar nem arriscar tudo

Decisão. Evoluir aos poucos, nunca por uma grande reescrita: substituir uma peça de cada vez, atrás de sua fronteira de contexto (§14.1), com o sistema sempre rodando. Cada peça nova honra o mesmo contrato da velha, entra com segurança por canário e rollback (§10.4), e a velha só é aposentada quando a nova está provada — com uma camada anticorrupção (§6.1) fazendo a ponte durante a transição quando necessário.

Resultado. O sistema foi sendo renovado de dentro, peça por peça, sem nunca parar e sem nunca apostar tudo de uma vez. Cada substituição entregou valor de imediato, a evolução continuou o tempo todo, o aprendizado embutido nas peças antigas foi preservado e transferido em vez de descartado, e o risco foi fatiado em pedaços pequenos e reversíveis — se uma peça nova falhasse, revertia-se só ela, com o todo de pé. A arquitetura modular, com fronteiras claras e entrega segura, construída ao longo de todo o livro, revelou seu propósito mais profundo: tornar possível a coisa mais difícil de todas — mudar sem parar, trocar o motor com o carro andando.

Incidente Real

Severidade · Alta

A tentação da reescrita que daria lugar à substituição gradual

  • Antes Diante de uma parte envelhecida do sistema, a opção aparente seria reescrevê-la inteira do zero — uma grande reescrita que pararia a evolução daquela área por um longo período e arriscaria tudo numa virada final.
  • Risco Uma grande reescrita demoraria muito mais do que o previsto, congelaria a evolução, tenderia a perder o aprendizado embutido na implementação existente e concentraria o risco num único momento de troca total.
  • Diagnóstico Tratar a renovação como um evento descontínuo (parar e refazer) em vez de um processo contínuo (substituir aos poucos) era a raiz do risco.
  • Correção A renovação foi feita por substituição gradual, peça por peça, atrás das fronteiras de contexto (§14.1) e com entrega segura (§10.4), mantendo o sistema rodando e o risco fatiado.
  • Kaizen Vira regra: o sistema evolui por substituição gradual, nunca por grande reescrita; cada peça é trocada atrás de sua fronteira, com o todo sempre de pé e o risco contido.

Decisão Arquitetural

ADR-0075

Evoluir por substituição gradual, nunca por grande reescrita

Aceito
Contexto

A grande reescrita (parar e refazer do zero) demora anos, congela a evolução durante todo esse tempo, subestima e descarta o aprendizado embutido no sistema antigo (as cicatrizes de casos reais), e aposta tudo numa única virada arriscada. É a negação do incrementalismo e da mudança segura que o livro defende.

Decisão

O sistema evolui por substituição gradual: uma peça de cada vez é renovada atrás de sua fronteira de contexto (§14.1), honrando o mesmo contrato, com o todo sempre rodando e entregando valor. A entrada de cada peça nova é segura (canário e rollback, §10.4; imagens imutáveis §10.2), com camada anticorrupção (§6.1) na ponte quando preciso, e a velha só é aposentada quando a nova está provada. O risco é fatiado em pedaços contidos.

Alternativas consideradas

Grande reescrita (refazer do zero) — anos parados, perde aprendizado, aposta tudo ou nada. Não evoluir (manter o velho para sempre) — o sistema apodrece. Reescrever sem fronteiras — impossível trocar peça isoladamente.

Consequências
PositivasValor entregue a cada peça. Evolução contínua, nunca congelada. Aprendizado do antigo preservado. Risco fatiado e reversível. Mudar sem parar — trocar o motor com o carro andando.
NegativasExige fronteiras de contexto (§14.1) e entrega segura (§10.4); a transição mantém velho e novo coexistindo por um tempo.
RiscosA coexistência de velho e novo adiciona complexidade temporária; mitigada por manter as transições curtas e a ponte (ACL) bem definida, aposentando o velho assim que possível.

Antipadrões

A grande reescrita do zero

Parar tudo para refazer o sistema inteiro de uma vez. Demora anos, congela a evolução, perde o aprendizado e aposta tudo numa virada. Correção: substituir peça por peça com o sistema rodando.

Reescrever uma peça sem honrar o contrato da fronteira

Trocar uma parte mudando o contrato que o resto do sistema espera. Quebra tudo o que dependia dela. Correção: a peça nova honra o mesmo contrato (§14.1); a troca fica invisível ao resto.

Exercício

Planeje uma evolução sem reescrita

  1. Pegue uma parte do seu sistema que você sente que "precisa ser reescrita". Por que a tentação da grande reescrita é perigosa aqui — quanto tempo levaria, e o que ficaria congelado nesse meio-tempo?
  2. Faça o teste do aprendizado: aquela parte velha, por mais feia que pareça, contém cicatrizes de casos reais que uma reescrita do zero redescobriria com dor? Quais?
  3. Faça o teste da fronteira (§14.1): aquela parte tem uma fronteira clara com contrato explícito, que permitiria substituí-la isoladamente? Se não, essa é a primeira coisa a criar.
  4. Esboce a substituição gradual: como você trocaria essa parte peça por peça, com o sistema sempre rodando — entrando a peça nova por canário (§10.4), honrando o contrato, aposentando a velha só quando a nova estivesse provada?
Meta: planejar a renovação de uma parte do seu sistema por substituição gradual em vez de grande reescrita — usando as fronteiras de contexto e a entrega segura para trocar o motor com o carro andando, sem parar nem apostar tudo de uma vez.
✓ Checklist do tópico
  • O sistema evolui por substituição gradual, peça por peça, não por grande reescrita
  • Cada peça é substituída atrás de sua fronteira de contexto (§14.1), honrando o contrato
  • O sistema permanece rodando e entregando valor durante toda a evolução
  • A entrada de cada peça nova é segura (canário, rollback §10.4; imagens imutáveis §10.2)
  • A peça velha só é aposentada quando a nova está provada; o risco fica fatiado e reversível
Meta Dados · Engenharia de Plataformas · Capítulo XIV · §14.5Fim do Capítulo XIV · voltar ao início
Meta Dados · Engenharia de Plataformas
Capítulo XV

Produto e
Growth
Engineering

A engenharia a serviço do produto: construir o que resolve um problema real e não o que apenas impressiona, medir o que de fato importa em vez de números de vaidade, experimentar para aprender com evidência, projetar a ativação e a retenção na própria arquitetura, e encerrar a obra fechando o arco — da fundação ao crescimento, sempre com menos e com propósito.

Edição 1.1 · Junho de 2026 · O molde-padrão da obra
Meta Dados · Engenharia de Plataformas Capítulo XV
Capítulo XV · Produto e Growth Engineering
15.1

Pensar como Produto

Este capítulo final vira a engenharia para fora: para o produto que ela serve. E a primeira virada é a mais difícil para quem ama construir — perguntar, antes de construir, que problema real aquilo resolve e para quem. A proeza técnica que ninguém precisa não é engenharia; é desperdício elegante.

Pré-requisitos · O Manifesto · Cap I · toda a fundaçãoConecta com · 15.2 Métricas · 15.4 AtivaçãoTempo de leitura · ~14 min

O Problema

Catorze capítulos ensinaram a construir bem: com fundamentos sólidos, arquitetura limpa, dados confiáveis, segurança, observabilidade, confiabilidade, a visão do sistema inteiro. Mas construir bem não basta — é preciso construir a coisa certa. E aqui, no volume final, a engenharia se vira para aquilo a que sempre serviu e que às vezes se esquece: o produto. O produto é a razão pela qual o sistema existe — alguém, em algum lugar, tem um problema, e o produto existe para resolvê-lo. Esquecer isso é o erro mais sutil e mais comum de quem ama construir.

O erro tem um nome: a solução à procura de um problema. É construir algo porque é tecnicamente interessante — uma tecnologia nova que se quer experimentar, uma arquitetura sofisticada que impressiona, uma funcionalidade "legal" que seria divertido fazer — sem antes perguntar se isso resolve uma necessidade real de alguém. A sedução é poderosa porque vem de um lugar bom: o prazer de construir, a curiosidade técnica, o orgulho da proeza. Mas o resultado é desperdício. Gasta-se um esforço imenso para entregar algo que ninguém precisava: a funcionalidade brilhante que nenhum usuário usa, a plataforma sofisticada que resolve um problema que ninguém tem, a otimização heroica de algo que não importava. É desperdício elegante — trabalho de alta qualidade técnica gasto no alvo errado, e a qualidade técnica não redime o erro de alvo; pelo contrário, torna o desperdício maior, porque mais esforço foi investido no que não era preciso. Há variações do mesmo erro. Construir guiado pela tecnologia em vez do problema: escolher primeiro a ferramenta interessante e depois procurar onde aplicá-la, em vez de partir do problema e escolher a ferramenta que o resolve. Construir guiado pela impressão em vez do valor: priorizar o que parece sofisticado sobre o que de fato ajuda o usuário, confundindo complexidade com qualidade. Construir mais do que o necessário: adicionar funcionalidades porque é possível, inchando o produto com coisas que poucos usam e que tornam o todo mais complexo para todos — o oposto exato da subtração que o Manifesto prega. O problema de fundo é uma inversão: tratar a engenharia como um fim em si — construir pelo prazer de construir, pela proeza, pela tecnologia — em vez de como um meio a serviço de um fim que está fora dela: resolver um problema real de uma pessoa real. Quando a engenharia se torna o fim, constrói-se muito e serve-se pouco; o sistema fica cheio de proezas e vazio de propósito. E um sistema sem propósito não merece durar, por mais bem construído que seja — porque durar, para um produto, é continuar resolvendo um problema que alguém tem, e o que nunca resolveu problema algum não tem o que continuar fazendo.

A solução à procura de um problema

Construir o que é tecnicamente impressionante ou "legal" sem que resolva uma necessidade real é desperdício elegante — esforço de qualidade gasto no alvo errado. Tratar a engenharia como fim em si leva a construir muito e servir pouco. Correção: pensar como produto — começar sempre pelo problema real e por quem o tem, com a engenharia a serviço disso.

A Decisão

Pensa-se como produto: antes de construir, pergunta-se que problema real a coisa resolve e para quem. A engenharia é um meio a serviço de um fim que está fora dela — resolver a dor de uma pessoa real —, não um fim em si. Constrói-se o que é preciso, não o que apenas impressiona.

A plataforma adota o pensamento de produto como a disciplina que orienta o que se constrói: a engenharia é um meio a serviço de um fim — resolver um problema real de pessoas reais — e toda decisão de construir começa por esse fim, não pela vontade de construir. Na prática, isso significa que antes de qualquer coisa ser construída, faz-se a pergunta do produto: que problema real isto resolve, e para quem? Uma resposta honesta a essa pergunta é o filtro que separa o que merece ser construído do desperdício elegante. Se a resposta é clara — "resolve tal dor concreta de tal usuário" —, constrói-se. Se a resposta é vaga — "é uma tecnologia interessante", "seria legal ter", "impressiona" —, isso é o sinal de uma solução à procura de um problema, e a disciplina é não construir, por mais sedutora que a proeza seja. Essa orientação inverte a inversão. Em vez de partir da tecnologia e procurar onde aplicá-la, parte-se do problema e escolhe-se a tecnologia que o resolve — e às vezes a melhor solução é a mais simples e menos impressionante, o que é uma vitória, não uma decepção. Em vez de priorizar o que impressiona, prioriza-se o que entrega valor ao usuário, medindo o sucesso pela dor resolvida, não pela sofisticação exibida. E em vez de construir mais, constrói-se o necessário — aplicando ao produto a subtração que o Manifesto eleva a princípio: cada funcionalidade que não resolve um problema real é peso a remover, não conquista a celebrar, porque um produto é melhor não quando faz tudo, mas quando faz o que importa e o faz bem. Há uma sutileza que vale nomear: pensar como produto não rebaixa a engenharia — eleva-a. Toda a excelência técnica dos catorze volumes anteriores não perde o valor; ganha direção. A arquitetura limpa, os dados confiáveis, a observabilidade, a confiabilidade — tudo isso passa a servir a um propósito claro, e a engenharia excelente a serviço de um problema real é a forma mais alta do ofício, muito acima da proeza brilhante a serviço de nada. Construir bem e construir a coisa certa não são opostos; são as duas metades do mesmo trabalho, e a segunda dá sentido à primeira. Esta é a virada que abre o volume final: depois de aprender a construir plataformas que merecem durar, reconhecer que merecer durar começa por resolver um problema que alguém de fato tem — porque a engenharia mais bela do mundo, se não serve a ninguém, é um monumento ao desperdício, e o Manifesto não pede monumentos: pede plataformas que sirvam, com menos e com propósito.

Diagrama conceitual Começar pelo problema, não pela proeza
IMPRESSIONARconstruir o que é "legal"solução à procura de problema RESOLVER UM PROBLEMAcomeçar pela dor reala engenharia serve ao produto qual problema real isto resolve, para quem? a proeza técnica que ninguém precisa é desperdício — não engenharia construir o que merece durar começa por construir o que é preciso
Figura 15.1-a. Construir para impressionar gera a solução à procura de um problema — desperdício elegante. Pensar como produto começa pela pergunta "que problema real isto resolve, para quem?", pondo a engenharia a serviço do produto e construindo o que é preciso, não o que apenas impressiona.

A pergunta que filtra o que merece ser construído

pensar_como_produto.pyPython
# Antes de construir: a PERGUNTA DO PRODUTO filtra o desperdício elegante.
def merece_ser_construido(ideia: Ideia) -> bool:
    # Que problema REAL isto resolve, e para QUEM?
    if not ideia.resolve_problema_real():
        # "É legal" / "impressiona" / "tecnologia interessante" → NÃO basta.
        return False   # solução à procura de um problema

    if not ideia.tem_usuario_concreto():
        return False   # resolver a dor de NINGUÉM não é resolver

    # Parte do PROBLEMA → escolhe a solução (às vezes a mais simples).
    return True
    # A engenharia é o MEIO; o fim (resolver a dor real) está fora dela.
A pergunta do produto — que problema real, para quem? — é o filtro que separa o que merece ser construído da solução à procura de um problema. "Impressiona" não passa no filtro.
menos_e_proposito.pyPython
# SUBTRAÇÃO (o Manifesto): construir o NECESSÁRIO, não o máximo possível.
def priorizar_o_produto(funcionalidades: list) -> list:
    # Cada funcionalidade que não resolve dor real é PESO, não conquista.
    essenciais = [f for f in funcionalidades if f.resolve_dor_real()]

    # Medir o sucesso pela DOR RESOLVIDA, não pela sofisticação exibida.
    return sorted(essenciais, key=lambda f: f.valor_para_o_usuario(),
                  reverse=True)
    # Um produto é melhor não quando faz tudo, mas quando faz O QUE IMPORTA.
Pensar como produto aplica a subtração do Manifesto: o que não resolve dor real é peso a remover. O sucesso mede-se pelo valor ao usuário, não pela sofisticação.

A Verificação

produto_test.pyPython
class TestPensarComoProduto(TestCase):
    def test_proeza_sem_problema_e_rejeitada(self):
        # Algo tecnicamente impressionante mas sem problema real → não passa.
        ideia = self.ideia_impressionante_sem_dor_real()
        self.assertFalse(merece_ser_construido(ideia))

    def test_solucao_simples_para_dor_real_e_aceita(self):
        # Algo simples que resolve uma dor concreta → passa (simplicidade é vitória).
        ideia = self.ideia_simples_que_resolve_dor()
        self.assertTrue(merece_ser_construido(ideia))

    def test_sucesso_medido_por_valor_nao_por_sofisticacao(self):
        ordenadas = priorizar_o_produto(self.mix_de_funcionalidades())
        self.assertEqual(ordenadas[0], self.a_de_maior_valor_ao_usuario())
Os testes fixam a disciplina: a proeza sem problema é rejeitada, a solução simples para uma dor real é aceita, e o sucesso é medido por valor ao usuário, não por sofisticação.
Tabela de decisão Engenharia como fim vs como meio (produto)
AspectoEngenharia como fimPensar como produto
Ponto de partidaA tecnologia/proezaO problema real
CritérioImpressiona?Resolve a dor de alguém?
QuantidadeO máximo possívelO necessário (subtração)
Sucesso medido porSofisticaçãoValor ao usuário
ResultadoConstrói muito, serve poucoServe ao propósito

Estudo de Caso

Caso aplicado

Do que era possível ao que o cliente precisava

Contexto. Uma das plataformas de dados começou como uma ferramenta de consulta — capaz, tecnicamente sólida, mas voltada a uma função relativamente genérica. A tentação seria seguir adicionando capacidades técnicas impressionantes. A pergunta de produto, porém, era outra: qual problema real, e para quem, essa plataforma poderia resolver de forma diferenciada?

Capacidade sem foco
Proeza à procura de uso
Pensar como produto
Problema real, público claro
Ganho
Engenharia com direção e propósito

Decisão. Pensar como produto: partir do problema real de um público concreto — a necessidade de inteligência para prospecção e decisão de negócio — em vez de partir das capacidades técnicas disponíveis. Reorientar a plataforma para resolver essa dor específica, priorizando o que entregava valor real ao usuário (como visões e exportações úteis ao trabalho dele) sobre o que apenas exibia sofisticação, e construindo o necessário em vez do máximo possível.

Resultado. A plataforma deixou de ser uma capacidade à procura de uso e passou a ser uma solução para um problema definido, com um público claro. A engenharia — sólida desde o início — ganhou direção: passou a servir a um propósito, em vez de existir como proeza. O sucesso passou a ser medido pela dor resolvida, não pela sofisticação acumulada. É a virada que este volume final propõe: depois de saber construir bem, reconhecer que merecer durar começa por resolver um problema que alguém de fato tem.

Incidente Real

Severidade · Baixa

A funcionalidade impecável que ninguém usava

  • Antes Em algum momento, esforço de engenharia foi investido em capacidades escolhidas mais por serem tecnicamente interessantes ou completas do que por responderem a uma dor concreta e verificada de usuários.
  • Sintoma Funcionalidades tecnicamente bem construídas tinham pouca adoção real, enquanto o esforço que as produziu poderia ter resolvido problemas que os usuários de fato sentiam — qualidade técnica gasta no alvo errado.
  • Diagnóstico A construção fora guiada pela proeza ou pela completude técnica em vez de pela pergunta do produto (que problema real, para quem), gerando solução à procura de problema.
  • Correção Passou-se a filtrar o que construir pela pergunta do produto, partindo do problema real e do público concreto, e medindo o sucesso pelo valor entregue.
  • Kaizen Vira regra: antes de construir, pergunta-se que problema real a coisa resolve e para quem; a engenharia serve ao produto, e constrói-se o necessário, não o que apenas impressiona.

Decisão Arquitetural

ADR-0076

A engenharia serve ao produto: construir o que resolve um problema real

Aceito
Contexto

Quem ama construir tende à solução à procura de um problema: construir o que é tecnicamente impressionante, "legal" ou interessante sem que resolva uma necessidade real. Isso é desperdício elegante — qualidade técnica gasta no alvo errado. Tratar a engenharia como fim em si leva a construir muito e servir pouco; o sistema fica cheio de proezas e vazio de propósito.

Decisão

Pensa-se como produto: a engenharia é um meio a serviço de um fim que está fora dela — resolver um problema real de pessoas reais. Antes de construir, faz-se a pergunta do produto (que problema real isto resolve, e para quem); respostas vagas ("impressiona", "seria legal") são o sinal para não construir. Parte-se do problema (não da tecnologia), prioriza-se valor (não sofisticação) e constrói-se o necessário (a subtração do Manifesto).

Alternativas consideradas

Partir da tecnologia/proeza — solução à procura de problema, desperdício elegante. Priorizar o que impressiona — confunde complexidade com qualidade. Construir o máximo possível — incha o produto, contraria a subtração.

Consequências
PositivasA engenharia ganha direção e propósito. Constrói-se o que serve, não o que apenas impressiona. O produto faz o que importa e o faz bem. A excelência técnica passa a servir a um fim real.
NegativasExige a disciplina de recusar proezas sedutoras que não resolvem problema real.
RiscosA pergunta do produto pode ser respondida de forma autoenganosa; mitigada por exigir um usuário e uma dor concretos, e por verificar o valor de fato entregue (§15.2).

Antipadrões

A solução à procura de um problema

Construir algo porque é tecnicamente impressionante, sem que resolva uma dor real. Desperdício elegante. Correção: começar pela pergunta do produto — que problema real, para quem?

Confundir sofisticação com qualidade

Priorizar o que parece complexo e impressionante sobre o que de fato ajuda o usuário. Correção: medir o sucesso pelo valor entregue; às vezes a melhor solução é a mais simples.

Exercício

Faça a pergunta do produto

  1. Pegue a última coisa significativa que você construiu (ou planeja construir). Responda honestamente: que problema real ela resolve, e para quem especificamente?
  2. Faça o teste do desperdício elegante: se a resposta foi vaga ("é uma tecnologia interessante", "seria legal ter", "impressiona"), você pode estar diante de uma solução à procura de um problema.
  3. Faça o teste da subtração: há funcionalidades no seu produto que poucos usam e que tornam o todo mais complexo? Cada uma é peso — o que aconteceria se fosse removida?
  4. Inverta o ponto de partida: pegue uma dor real e concreta de um usuário seu e pergunte qual a solução mais simples que a resolveria — começando pelo problema, não pela tecnologia.
Meta: submeter o que você constrói à pergunta do produto — que problema real, para quem — para que a sua engenharia, já excelente, ganhe direção e sirva a um propósito, em vez de produzir proezas que ninguém precisa.
✓ Checklist do tópico
  • Antes de construir, pergunta-se que problema real a coisa resolve e para quem
  • Respostas vagas ("impressiona", "seria legal") são sinal para não construir
  • Parte-se do problema real, e então se escolhe a solução (às vezes a mais simples)
  • O sucesso é medido pelo valor entregue ao usuário, não pela sofisticação técnica
  • Constrói-se o necessário, não o máximo possível (a subtração do Manifesto)
Meta Dados · Engenharia de Plataformas · Capítulo XV · §15.1Próximo → 15.2 As Métricas que Importam
Capítulo XV · Produto e Growth Engineering
15.2

As Métricas que Importam

Nem todo número que sobe é uma boa notícia. Há métricas de vaidade — totais que crescem e impressionam, mas não dizem nada sobre o valor real do produto — e há as métricas que importam, que refletem se as pessoas de fato encontram valor. Otimizar as primeiras é se enganar com números bonitos.

Pré-requisitos · 15.1 · §13.1 SLO · §11.5 Camada SemânticaConecta com · 15.3 Experimentar · 15.4 RetençãoTempo de leitura · ~14 min

O Problema

Pensar como produto (§15.1) exige saber se o produto está, de fato, resolvendo o problema — e isso se mede. Mas medir é perigoso, porque o que se mede determina o que se otimiza, e é fácil medir a coisa errada. Há uma categoria de métricas especialmente traiçoeira: as métricas de vaidade. São números que sobem, que parecem ótimos, que dão orgulho de mostrar — e que, no entanto, não dizem quase nada sobre se o produto está realmente entregando valor ou se é saudável.

Os exemplos clássicos de métricas de vaidade são totais acumulados que só podem crescer: o total de cadastros desde o início, o total de downloads, o total de visualizações. Esses números sobem inexoravelmente — cada novo cadastro soma, e nenhum nunca subtrai — então a curva é sempre "para cima e para a direita", sempre animadora. Mas eles escondem as perguntas que importam. Um milhão de cadastros totais parece esplêndido; mas quantos desses usuários ainda usam o produto? Se quase todos se cadastraram e nunca voltaram, o produto está fracassando — e a métrica de vaidade esconde esse fracasso atrás de um número grande e crescente. O total de downloads não diz quantos abriram o aplicativo mais de uma vez; o total de visualizações não diz quantas pessoas encontraram o que procuravam. As métricas de vaidade têm três pecados. Primeiro, não refletem valor nem saúde reais: medem volume bruto, não se as pessoas estão sendo de fato ajudadas. Segundo, mascaram o fracasso: porque sobem mesmo quando o produto vai mal, dão uma falsa sensação de sucesso que adia o reconhecimento dos problemas reais — o número bonito é um anestésico. Terceiro, e mais perigoso, guiam decisões erradas: porque o que se mede é o que se otimiza, otimizar uma métrica de vaidade leva a esforço gasto em inflar o número (mais cadastros!) em vez de em entregar valor (usuários que ficam porque o produto os ajuda), e essas duas coisas podem até ser opostas — inflar cadastros com promessas que o produto não cumpre aumenta a métrica de vaidade e piora o produto real. Há ainda um problema relacionado, que o Capítulo XI já tocou: métricas mal definidas ou ambíguas. Quando "usuário ativo" significa coisas diferentes para pessoas diferentes, ou quando o mesmo número é calculado de formas distintas em lugares distintos, mede-se confusão — e decisões tomadas sobre métricas ambíguas são decisões tomadas sobre areia. O problema de fundo é confundir o que é fácil e agradável de medir com o que importa medir. Os totais que sobem são fáceis de obter e bons de mostrar; o valor real — as pessoas encontram o que precisam? voltam? o problema foi resolvido? — é mais difícil de medir e nem sempre é uma curva animadora. E a tentação de olhar para os números bonitos em vez dos números honestos é a tentação de se enganar — de trocar o espelho que mostra a verdade pelo que mostra o que se quer ver.

Os números bonitos que escondem a verdade

Métricas de vaidade (totais de cadastros, downloads, visualizações) sobem sempre e impressionam, mas não refletem o valor nem a saúde reais, mascaram o fracasso atrás de um número grande, e guiam o esforço para inflar o número em vez de entregar valor. Correção: medir o que importa — se as pessoas encontram valor de fato (uso ativo, retenção, problema resolvido) —, com cada métrica claramente definida.

A Decisão

Medem-se as métricas que importam: as que refletem se as pessoas encontram valor de fato — uso ativo, retenção, o problema resolvido —, não os totais de vaidade que sobem sempre. Cada métrica tem uma definição única (§11.5) e mede o que o usuário sente (§13.1), não um proxy enganoso.

A plataforma escolhe deliberadamente medir o que importa, recusando o conforto enganoso das métricas de vaidade. O princípio orientador é simples e exigente: uma boa métrica reflete se as pessoas estão de fato encontrando valor no produto — e é por isso, não por quão bonita é a curva, que ela merece atenção. Onde a métrica de vaidade pergunta "quantos no total?", a métrica que importa pergunta "quantos estão sendo realmente ajudados?". Em vez do total de cadastros, mede-se o uso ativo: quantas pessoas usam o produto de forma recorrente, encontrando valor o bastante para voltar — um número que pode subir ou descer, e é justamente por poder descer que ele é honesto, porque reflete a realidade em vez de apenas acumulá-la. Em vez do total de downloads, mede-se quantos retornam (a retenção, §15.4); em vez do total de visualizações, mede-se quantos encontraram o que procuravam. Essas métricas têm uma virtude que as de vaidade não têm: elas podem dar más notícias, e uma métrica que só dá boas notícias não está medindo a verdade. A escolha de medir o que importa apoia-se diretamente em dois princípios que o livro já estabeleceu. Do Capítulo XIII (§13.1, os SLOs), herda-se a ideia de medir o que o usuário de fato sente — assim como a confiabilidade se mede pela experiência real e não pela saúde interna dos servidores, o valor do produto se mede pelo que os usuários realmente vivenciam (encontraram valor? voltaram?) e não por um proxy que parece bom mas não reflete a experiência. Do Capítulo XI (§11.5, a camada semântica), herda-se a exigência de que cada métrica tenha uma definição única e clara — "usuário ativo" significa uma coisa precisa, definida num único lugar e usada consistentemente em todo o ecossistema, de modo que não se meça confusão nem se tomem decisões sobre areia. Juntas, essas heranças tornam as métricas do produto ao mesmo tempo honestas (medem o valor real sentido) e confiáveis (cada uma significa exatamente uma coisa). E há uma consequência cultural importante: medir o que importa exige a coragem de olhar para números que podem decepcionar. É confortável contemplar o total de cadastros subindo; é desconfortável olhar para uma retenção baixa que diz que o produto não está retendo. Mas é exatamente esse desconforto que torna a métrica útil — ela mostra onde o produto precisa melhorar, em vez de anestesiar com um número bonito. Escolher a métrica honesta sobre a métrica lisonjeira é a aplicação, ao mundo do produto, da mesma honestidade que percorre o livro inteiro: contra as caixas-pretas, contra o autoengano, a favor de ver as coisas como são. Um produto só pode melhorar o que mede com honestidade; o que se mede para se gabar, mede-se para não melhorar. As métricas que importam são o espelho que mostra a verdade — e só quem ousa olhar nesse espelho pode construir um produto que de fato sirva.

Diagrama conceitual Do número de vaidade à métrica honesta
MÉTRICA DE VAIDADEsobe e parece ótimomas não reflete valor real MÉTRICA QUE IMPORTAreflete valor e saúde reaisguia a decisão certa total de cadastros uso ativo / valor → medir o que o usuário SENTE (§13.1), uma métrica uma definição (§11.5) o número que sobe bonito pode esconder um produto que não entrega
Figura 15.2-a. A métrica de vaidade (total de cadastros) sobe sempre e mascara o fracasso. A métrica que importa (uso ativo, valor real) pode subir ou descer — e por isso é honesta. Herda do SLO (§13.1) medir o que o usuário sente e da camada semântica (§11.5) uma definição única.

A métrica honesta, que pode dar más notícias

metricas.pyPython
# MÉTRICA DE VAIDADE: total acumulado — só sobe, mascara o fracasso.
def total_de_cadastros() -> int:
    return contar("todos os cadastros desde o início")  # sempre cresce
    # 1 milhão? Mas quantos AINDA USAM? O número esconde a resposta.

# MÉTRICA QUE IMPORTA: reflete VALOR REAL — pode subir OU descer (honesta).
def usuarios_ativos(janela: str) -> int:
    # "Ativo" = definição ÚNICA e clara (§11.5), o mesmo em todo lugar.
    # Mede o que o usuário SENTE/FAZ (§13.1), não um proxy bonito.
    return contar(usuarios_que_encontraram_valor_recorrente(janela))
    # Pode CAIR — e é por poder cair que diz a verdade.
A métrica de vaidade (total acumulado) só sobe e esconde o fracasso; a que importa (uso ativo) pode subir ou descer, com definição única (§11.5), medindo valor real (§13.1).
o_que_se_mede_se_otimiza.pyPython
# O QUE SE MEDE É O QUE SE OTIMIZA — então medir o certo é decisivo.
def avaliar_mudanca(antes: Metricas, depois: Metricas) -> str:
    # ERRADO: celebrar porque os cadastros subiram (vaidade).
    # CERTO: perguntar se o VALOR REAL subiu (uso ativo, retenção).
    if depois.uso_ativo > antes.uso_ativo:
        return "valor real cresceu — boa mudança"

    if depois.cadastros > antes.cadastros and depois.uso_ativo <= antes.uso_ativo:
        # Cadastros up, mas uso não → inflou vaidade, NÃO melhorou o produto.
        return "alerta: número bonito mascarando estagnação"
    return "sem ganho de valor real"
Porque o que se mede é o que se otimiza, avaliar mudanças pela métrica que importa (uso ativo) evita o engano de celebrar um número de vaidade que mascara estagnação.

A Verificação

metricas_test.pyPython
class TestMetricasQueImportam(TestCase):
    def test_metrica_honesta_pode_cair(self):
        # Se os usuários param de voltar, a métrica que importa CAI (honesta).
        self.usuarios_param_de_voltar()
        self.assertLess(usuarios_ativos("30 dias"), self.ativos_antes)

    def test_vaidade_esconde_o_fracasso(self):
        # Mesmo com o produto fracassando, o total de cadastros NÃO cai.
        self.produto_fracassando()
        self.assertGreaterEqual(total_de_cadastros(), self.cadastros_antes)  // mascara

    def test_metrica_tem_definicao_unica(self):
        # "Ativo" significa o MESMO em todo lugar (§11.5).
        self.assertEqual(self.definicao_de_ativo_no_painel(),
                         self.definicao_de_ativo_no_relatorio())
Os testes demonstram o contraste: a métrica honesta cai quando os usuários somem, a de vaidade não (mascara), e cada métrica tem uma definição única e consistente (§11.5).
Tabela de decisão Métrica de vaidade vs métrica que importa
AspectoMétrica de vaidadeMétrica que importa
ExemploTotal de cadastrosUso ativo, retenção
DireçãoSó sobeSobe ou desce (honesta)
Reflete valor real?NãoSim
Quando o produto vai malMascara (sobe igual)Mostra (cai)
Guia a decisão paraInflar o númeroEntregar valor

Estudo de Caso

Caso aplicado

Olhar para o número honesto, mesmo desconfortável

Contexto. Ao desenvolver um funil de nutrição por e-mail e os fluxos de entrada de novos usuários, era tentador acompanhar números de vaidade — totais de contatos, de envios, de cadastros — que sobem e impressionam, mas não dizem se as pessoas estão de fato encontrando valor.

Números de vaidade
Sobem bonito, escondem a verdade
Métricas que importam
Valor real, definição única
Ganho
Decidir sobre a realidade, não sobre areia

Decisão. Privilegiar as métricas que importam: medir se os usuários efetivamente avançam, engajam e retornam, em vez de apenas acumular totais; e, criticamente, manter o rigor sobre a integridade dessas medições — uma auditoria de qualidade revelou problemas que poderiam distorcer o que se media (de variáveis não renderizadas a inconsistências), e corrigi-los foi parte de garantir que as métricas refletissem a realidade. Cada métrica com definição única (§11.5) e ancorada no que o usuário de fato vivencia (§13.1).

Resultado. O foco deslocou-se dos números que sobem bonito para os que dizem a verdade sobre o valor entregue — ainda que esses pudessem decepcionar e apontar onde melhorar. A disciplina de garantir medições íntegras evitou decidir sobre areia. A honestidade que percorre o livro — contra o autoengano, a favor de ver as coisas como são — aplicou-se ao produto: medir para melhorar, não para se gabar. Um produto só melhora o que mede com honestidade.

Incidente Real

Severidade · Média

As medições que precisavam ser confiáveis para significar algo

  • Antes Os fluxos de comunicação e entrada de usuários produziam métricas, mas a integridade dessas medições não estava plenamente garantida — havia defeitos que podiam distorcer o que os números diziam.
  • Sintoma Uma auditoria de qualidade identificou problemas (como uma variável de template não renderizada e inconsistências entre fontes de informação) que comprometiam a fidelidade das medições, arriscando decisões baseadas em números que não refletiam a realidade.
  • Diagnóstico Sem integridade e definição clara, as métricas mediam parcialmente confusão; somava-se a isso o risco de focar em totais de vaidade em vez do valor real.
  • Correção Os defeitos foram corrigidos e o foco reorientado para métricas que refletem valor real, cada uma com definição única e consistente, medindo o que o usuário de fato vivencia.
  • Kaizen Vira regra: medem-se as métricas que importam (valor real), com definição única (§11.5) e integridade garantida; números de vaidade não guiam decisões.

Decisão Arquitetural

ADR-0077

Medir as métricas que importam, não as de vaidade

Aceito
Contexto

Métricas de vaidade (totais de cadastros, downloads, visualizações) sobem sempre e impressionam, mas não refletem o valor nem a saúde reais, mascaram o fracasso atrás de um número grande e crescente, e — porque o que se mede é o que se otimiza — guiam o esforço para inflar o número em vez de entregar valor. Métricas ambíguas pioram tudo, fazendo decidir sobre areia.

Decisão

Medem-se as métricas que importam: as que refletem se as pessoas encontram valor de fato (uso ativo, retenção §15.4, problema resolvido), que podem subir ou descer e por isso são honestas. Herda-se do SLO (§13.1) medir o que o usuário de fato sente, e da camada semântica (§11.5) que cada métrica tenha uma definição única e clara. Exige a coragem de olhar para números que podem decepcionar.

Alternativas consideradas

Otimizar métricas de vaidade — infla números, mascara fracasso, engana. Métricas ambíguas/mal definidas — mede confusão, decide sobre areia. Só medir o que dá boas notícias — não está medindo a verdade.

Consequências
PositivasMede-se o valor real, não o volume bruto. O fracasso aparece em vez de se esconder. O esforço vai para entregar valor, não para inflar números. Decisões sobre dados honestos e bem definidos.
NegativasExige a coragem cultural de olhar para métricas desconfortáveis e a disciplina de defini-las bem.
RiscosAté boas métricas podem ser mal interpretadas ou viradas em metas perversas; mitigado por olhar um conjunto delas e manter o foco no valor ao usuário (§15.1).

Antipadrões

Otimizar métricas de vaidade

Perseguir totais que sobem bonito (cadastros, downloads) que não refletem valor real e mascaram o fracasso. Correção: medir o que importa — uso ativo, retenção, valor de fato encontrado.

Medir com definições ambíguas

Deixar que "usuário ativo" signifique coisas diferentes em lugares diferentes. Mede-se confusão. Correção: cada métrica com definição única (§11.5), usada consistentemente.

Exercício

Separe a vaidade do que importa

  1. Liste os números que você costuma olhar para avaliar o seu produto. Para cada um, pergunte: ele pode descer? Se só pode subir (totais acumulados), é forte candidato a métrica de vaidade.
  2. Faça o teste do fracasso: se o seu produto começasse a ir mal (as pessoas parando de usar), quais dos seus números mostrariam isso, e quais continuariam subindo, mascarando o problema?
  3. Para a sua principal métrica, verifique a definição: "usuário ativo" (ou equivalente) significa exatamente uma coisa, definida num lugar e usada igual em todo lugar? Ou é ambígua?
  4. Escolha uma métrica honesta que reflita valor real (uso recorrente, retorno, problema resolvido) e comprometa-se a olhá-la mesmo quando ela decepcionar — porque é aí que ela é útil.
Meta: distinguir, no seu produto, os números de vaidade que o lisonjeiam das métricas que importam e dizem a verdade — para que você meça para melhorar, com honestidade, em vez de medir para se gabar.
✓ Checklist do tópico
  • Acompanham-se métricas que refletem valor real (uso ativo, retenção), não só totais
  • Privilegiam-se métricas que podem descer (honestas) sobre totais que só sobem (vaidade)
  • Cada métrica mede o que o usuário de fato sente/faz (§13.1), não um proxy enganoso
  • Cada métrica tem uma definição única e clara, usada consistentemente (§11.5)
  • Há a coragem cultural de olhar para métricas que podem decepcionar
Meta Dados · Engenharia de Plataformas · Capítulo XV · §15.2Próximo → 15.3 Experimentar para Aprender
Capítulo XV · Produto e Growth Engineering
15.3

Experimentar para Aprender

A maioria das decisões de produto é tomada por opinião — muitas vezes a de quem tem mais poder na sala — e a mudança é lançada na crença de que ajuda, sem nunca se medir se ajudou. Experimentar troca a crença pela evidência: mostra-se A para uns e B para outros, e a realidade, não a opinião, decide.

Pré-requisitos · 15.2 Métricas · §10.4 Entrega Segura · Cap VIIIConecta com · 15.4 AtivaçãoTempo de leitura · ~14 min

O Problema

Pensar como produto (§15.1) e medir o que importa (§15.2) convergem numa pergunta prática: quando se considera uma mudança no produto — um novo fluxo, uma tela diferente, uma funcionalidade —, como saber se ela realmente melhora as coisas? A resposta mais comum, e mais frágil, é: decide-se por opinião. Alguém acha que a mudança é melhor, argumenta convincentemente, e ela é feita — frequentemente a opinião que prevalece é a de quem tem mais poder ou autoridade na sala, não a que tem mais razão. E uma vez feita a mudança, ela é lançada na crença de que ajuda, e raramente se volta para medir se de fato ajudou.

Decidir por opinião e nunca medir é construir o produto sobre achismo, e isso falha de formas previsíveis. A intuição sobre o que os usuários querem está frequentemente errada — não por incompetência, mas porque pessoas são complexas e o que parece obviamente melhor para quem constrói muitas vezes não é o que de fato funciona para quem usa. A história do design de produtos está cheia de mudanças "obviamente boas" que pioraram os resultados, e de mudanças que pareciam arriscadas ou contraintuitivas e que os usuários adoraram. Quando se decide por opinião, aposta-se na intuição falível de alguém — e quando essa opinião é a de quem tem mais autoridade (o fenômeno que se costuma chamar, com ironia, de "a opinião da pessoa mais bem paga"), o problema piora, porque a autoridade não confere acerto, e silencia o questionamento: ninguém mede se o chefe estava certo. O segundo pecado é lançar e nunca verificar: a mudança vai para produção na crença de que melhora, e como ninguém volta para medir, nunca se descobre se a crença era verdadeira. O produto acumula mudanças feitas por fé, algumas das quais podem estar piorando as coisas sem que ninguém perceba, porque o efeito real nunca foi medido. Pior, sem medição, o aprendizado não acontece: a equipe não fica mais sábia sobre o que funciona, porque nunca confronta suas crenças com a realidade — comete os mesmos erros de intuição repetidamente, cada um justificado por uma opinião confiante e nenhum corrigido por evidência. Há um terceiro problema, mais sutil: mesmo quando se mede depois de lançar para todos, é difícil saber se uma mudança no número se deveu à mudança no produto ou a algum outro fator (uma sazonalidade, uma campanha, o acaso) — sem um grupo de comparação, a medição é ambígua. O problema de fundo é tratar decisões de produto como questões de opinião — a serem resolvidas por argumento, persuasão ou autoridade — quando muitas delas são, na verdade, questões empíricas: têm uma resposta certa sobre o que de fato funciona, que pode ser descoberta em vez de debatida. Debater opiniões sobre o que os usuários vão fazer, quando se poderia simplesmente medir o que eles fazem, é escolher a crença quando a evidência está ao alcance — e a crença, por mais confiante, não é conhecimento.

O produto construído sobre achismo

Decidir por opinião (muitas vezes a de quem tem mais autoridade) e lançar a mudança crendo que ajuda, sem medir o efeito real, constrói o produto sobre intuição falível — acumulando mudanças feitas por fé, algumas piorando as coisas sem que ninguém perceba, sem nunca aprender. Correção: experimentar — mostrar A para uns e B para outros, medir o efeito real, deixar a evidência decidir.

A Decisão

Experimenta-se para aprender: em vez de decidir por opinião, expõe-se a variante A a um grupo e a B a outro, mede-se o efeito real de cada uma nas métricas que importam (§15.2), e a evidência decide. A intuição vira hipótese a testar, não verdade a impor.

A plataforma substitui a decisão por opinião pela experimentação: o método de descobrir o que de fato funciona medindo, em vez de debater o que se acredita que funciona. A forma canônica é o experimento controlado (o teste A/B): quando se quer saber se uma mudança melhora o produto, não se lança para todos na crença de que sim — mostra-se a versão atual (A) para um grupo de usuários e a versão nova (B) para outro grupo comparável, ao mesmo tempo, e mede-se o efeito de cada uma nas métricas que importam (§15.2). Se o grupo que viu B tem resultados melhores nas métricas honestas — mais usuários encontrando valor, mais retorno, o problema mais resolvido —, então B é de fato melhor, e isso é sabido, não achado. Se não tem, a mudança que parecia obviamente boa é descartada antes de prejudicar todos, e aprendeu-se algo verdadeiro sobre os usuários. Essa abordagem transforma três coisas. Primeiro, a intuição vira hipótese: a opinião de alguém — mesmo a da pessoa mais sênior — deixa de ser uma verdade a impor e passa a ser uma hipótese a testar; a autoridade não decide, a evidência decide, e isso é profundamente democratizante, porque o experimento dá a mesma voz à ideia do estagiário e à do chefe — vence a que funciona. Segundo, o grupo de comparação (A) resolve a ambiguidade: como A e B rodam ao mesmo tempo, sob as mesmas condições, qualquer diferença nos resultados pode ser atribuída à mudança, e não a sazonalidades ou ao acaso — a evidência é limpa. Terceiro, a equipe aprende de verdade: cada experimento confronta uma crença com a realidade, e ao longo do tempo a organização acumula conhecimento sobre o que funciona para os seus usuários, ficando mais sábia em vez de repetir os mesmos erros de intuição. Há um parentesco importante, que fecha o arco com o Capítulo X: o experimento é primo do lançamento controlado (§10.4, o canário). Lá, expunha-se uma mudança técnica a uma fração do tráfego para observar seu efeito antes de liberá-la a todos, com a possibilidade de reverter; aqui, expõe-se uma mudança de produto a uma fração dos usuários para medir seu efeito antes de adotá-la, com a possibilidade de descartá-la. São a mesma sabedoria — mudança exposta gradualmente, efeito real observado, decisão baseada em evidência, reversão se necessário — aplicada uma à confiabilidade técnica e outra ao valor de produto. A infraestrutura de entrega segura que o livro construiu é, não por acaso, a mesma que torna a experimentação de produto possível. E há uma virtude cultural que a experimentação cultiva: a humildade empírica. Experimentar é admitir que não se sabe a resposta de antemão — que a intuição pode estar errada e que a única forma de saber é medir. Essa humildade, longe de ser fraqueza, é a postura mais forte que uma equipe de produto pode ter, porque a alternativa — a confiança na própria opinião — é exatamente o que leva aos erros caros. É a mesma honestidade que percorre o livro inteiro, agora voltada para o futuro incerto: assim como as métricas honestas (§15.2) mostram a verdade sobre o presente, os experimentos descobrem a verdade sobre o que vai funcionar — e ambos exigem a coragem de preferir a realidade à crença confortável. Aprende-se experimentando porque o produto serve a pessoas reais, e o que pessoas reais farão não se decide numa sala: descobre-se medindo o que elas de fato fazem.

Fluxograma operacional A vs B: a evidência no lugar da opinião
DECIDIR POR OPINIÃOo palpite de quem tem mais poderlança e crê — sem nunca medir EXPERIMENTARA vs B, medir o efeito reala evidência decide variante Ametade vê variante Bmetade vê vs a evidência substitui a opinião — medir se ajuda, não apenas crer que ajuda parente da entrega segura (§10.4): mudança controlada, observada, reversível
Figura 15.3-a. Decidir por opinião (a de quem tem mais poder) e lançar crendo que ajuda constrói sobre achismo. O experimento mostra A a um grupo e B a outro, mede o efeito real, e a evidência decide — primo do lançamento controlado (§10.4): exposto, observado, reversível.

A intuição como hipótese a testar

experimento.pyPython
# EXPERIMENTAR: a intuição vira HIPÓTESE a testar, não verdade a impor.
def testar(versao_atual: Variante, versao_nova: Variante) -> Resultado:
    # A e B rodam AO MESMO TEMPO, em grupos comparáveis (evidência limpa).
    grupo_a = expor(versao_atual, fracao=0.5)   # grupo de comparação
    grupo_b = expor(versao_nova,  fracao=0.5)

    # Mede o efeito real nas MÉTRICAS QUE IMPORTAM (§15.2), não de vaidade.
    if grupo_b.valor_real() > grupo_a.valor_real():
        return Resultado.ADOTAR_B   # a evidência decide — não a autoridade

    # A mudança "obviamente boa" não funcionou? Descarta ANTES de afetar todos.
    return Resultado.DESCARTAR_B   # e aprendeu-se algo verdadeiro
O experimento roda A e B ao mesmo tempo em grupos comparáveis e mede o efeito real nas métricas que importam. A evidência decide, e a intuição vira hipótese testável.
primo_do_canario.shBash
# O experimento é PRIMO do lançamento controlado (§10.4, o canário):
# mesma sabedoria — exposto a uma fração, efeito observado, reversível.

# Expõe a variante nova a uma fração dos usuários (não a todos).
expor-variante B --fracao 50%

# Observa o efeito REAL nas métricas honestas (§15.2, Cap VIII).
if valor-real B > valor-real A; then
  adotar-variante B --para todos   # evidência confirma → adota
else
  descartar-variante B             # não ajudou → descarta (reversível)
fi
# A infraestrutura de entrega segura é a MESMA que viabiliza experimentar.
O experimento reusa a sabedoria do canário (§10.4): expor a uma fração, observar o efeito real, adotar ou reverter. A entrega segura do livro viabiliza a experimentação.

A Verificação

experimento_test.pyPython
class TestExperimentarParaAprender(TestCase):
    def test_evidencia_vence_a_opiniao(self):
        # Mesmo que a opinião favoreça B, se os dados mostram A melhor, A vence.
        self.dados_mostram_A_melhor()
        self.assertEqual(testar(self.A, self.B).vencedor, "A")

    def test_grupo_de_comparacao_isola_o_efeito(self):
        # A e B rodam juntos → a diferença é atribuível à mudança, não ao acaso.
        r = testar(self.A, self.B)
        self.assertTrue(r.tem_grupo_de_comparacao())

    def test_mudanca_ruim_e_descartada_antes_de_afetar_todos(self):
        # Uma mudança que piora as métricas é descartada no experimento.
        self.B_piora_o_valor_real()
        self.assertEqual(testar(self.A, self.B), Resultado.DESCARTAR_B)
Os testes fixam o método: a evidência vence a opinião, o grupo de comparação isola o efeito da mudança, e o que piora as métricas é descartado antes de afetar todos.
Tabela de decisão Decidir por opinião vs experimentar
AspectoDecisão por opiniãoExperimentar para aprender
Quem decideQuem tem mais autoridadeA evidência
A intuição éVerdade a imporHipótese a testar
Verifica o efeito?Não (lança e crê)Sim, mede
Mudança ruimAfeta todos sem avisoDescartada antes
A equipeRepete erros de intuiçãoAprende de verdade

Estudo de Caso

Caso aplicado

Trocar o palpite pela evidência

Contexto. Decisões sobre os fluxos de produto — como os caminhos de entrada de novos usuários e as comunicações — poderiam ser tomadas por opinião, lançadas na crença de que melhorariam os resultados, sem que o efeito real fosse medido e isolado.

Decidir por opinião
Achismo, sem verificar
Experimentar
Hipótese testada, evidência decide
Ganho
Aprender de verdade com a realidade

Decisão. Adotar a experimentação para aprender: tratar mudanças como hipóteses a testar, expondo variantes a grupos comparáveis e medindo o efeito real nas métricas que importam (§15.2), em vez de deixar a opinião (ou a autoridade) decidir. Apoiar-se na mesma infraestrutura de entrega segura e observabilidade (§10.4, Cap VIII) que o livro já construíra, da qual o experimento é primo.

Resultado. As decisões de produto passaram a poder ser fundadas em evidência, não em achismo: a intuição virou hipótese, o grupo de comparação tornou as conclusões limpas, e mudanças que não ajudassem poderiam ser descartadas antes de afetar todos. A equipe ganhou a capacidade de aprender de verdade com cada experimento, em vez de repetir erros de intuição. A humildade empírica — admitir que não se sabe a resposta e medir — revelou-se a postura mais forte, coerente com a honestidade que atravessa todo o livro: preferir a realidade à crença confortável.

Incidente Real

Severidade · Baixa

As mudanças que mereciam evidência, não fé

  • Antes Ajustes em fluxos de produto e comunicação podiam ser decididos com base em convicção sobre o que funcionaria melhor, sem um mecanismo para medir e isolar o efeito real de cada alternativa.
  • Sintoma Sem experimentação, mudanças eram lançadas na crença de que ajudavam, sem verificação posterior limpa; não havia como saber com segurança se de fato melhoravam os resultados, nem como aprender sistematicamente com isso.
  • Diagnóstico Decisões empíricas (com resposta descobrível medindo) estavam sendo tratadas como questões de opinião, sem grupo de comparação que isolasse o efeito.
  • Correção Adoção da experimentação (variantes comparáveis, efeito medido nas métricas que importam), apoiada na entrega segura e observabilidade existentes, deixando a evidência decidir.
  • Kaizen Vira regra: mudanças de produto significativas são tratadas como hipóteses a testar; a evidência decide, não a opinião, e cada experimento gera aprendizado real.

Decisão Arquitetural

ADR-0078

Experimentar para aprender: a evidência decide, não a opinião

Aceito
Contexto

Decidir por opinião (frequentemente a de quem tem mais autoridade) e lançar mudanças na crença de que ajudam, sem medir o efeito real, constrói o produto sobre intuição falível — que está frequentemente errada. Acumulam-se mudanças feitas por fé, algumas piorando as coisas sem que ninguém perceba; sem grupo de comparação, mesmo medir depois é ambíguo; e a equipe nunca aprende.

Decisão

Experimenta-se: a intuição vira hipótese a testar. Expõe-se a variante atual (A) a um grupo e a nova (B) a outro comparável, ao mesmo tempo, e mede-se o efeito real nas métricas que importam (§15.2); a evidência decide. É primo do lançamento controlado (§10.4): mudança exposta a uma fração, efeito observado (Cap VIII), reversível. Democratiza (vence a ideia que funciona, não a autoridade) e ensina (cada experimento confronta crença com realidade).

Alternativas consideradas

Decidir por opinião/autoridade — aposta na intuição falível, silencia o questionamento. Lançar para todos e crer — efeito real nunca medido, erros invisíveis. Medir sem grupo de comparação — não isola o efeito, conclusão ambígua.

Consequências
PositivasA evidência decide, não a autoridade. A intuição vira hipótese. Mudanças ruins descartadas antes de afetar todos. Conclusões limpas (grupo de comparação). A equipe aprende de verdade.
NegativasExige a infraestrutura de exposição parcial e medição (a mesma da entrega segura) e humildade empírica.
RiscosExperimentos mal desenhados ou lidos enganam; mitigado por grupos comparáveis, métricas honestas (§15.2) e cautela com conclusões precipitadas.

Antipadrões

Decidir pela opinião de quem tem mais autoridade

Deixar o palpite do mais sênior decidir, sem medir. A autoridade não confere acerto. Correção: a intuição vira hipótese; o experimento mede e a evidência decide.

Lançar para todos e nunca verificar

Pôr a mudança em produção crendo que ajuda, sem medir o efeito real. Erros ficam invisíveis. Correção: experimentar com grupo de comparação antes de adotar para todos.

Exercício

Troque uma opinião por um experimento

  1. Lembre da última mudança significativa que você fez no produto. Ela foi decidida por opinião ou por evidência? Alguém voltou para medir se ela de fato melhorou os resultados?
  2. Faça o teste da autoridade: nas suas decisões de produto, é a evidência que decide, ou tende a prevalecer a opinião de quem tem mais poder na conversa?
  3. Pegue uma decisão de produto em aberto sobre a qual há opiniões divergentes. Em vez de debatê-la, como você poderia transformá-la num experimento — mostrando A para uns e B para outros e medindo?
  4. Verifique a sua infraestrutura: você tem como expor uma variante a uma fração dos usuários e medir o efeito (a mesma base da entrega segura, §10.4)? Se não, essa é a capacidade a construir.
Meta: transformar uma decisão de produto de uma disputa de opiniões num experimento que a realidade resolve — para que você aprenda de verdade o que funciona para os seus usuários, em vez de apostar na intuição.
✓ Checklist do tópico
  • Mudanças de produto significativas são tratadas como hipóteses a testar, não verdades a impor
  • Experimenta-se com grupo de comparação (A vs B) para isolar o efeito real
  • O efeito é medido nas métricas que importam (§15.2), e a evidência decide
  • Mudanças que não ajudam são descartadas antes de afetar todos os usuários
  • A experimentação reusa a infraestrutura de entrega segura e observabilidade (§10.4, Cap VIII)
Meta Dados · Engenharia de Plataformas · Capítulo XV · §15.3Próximo → 15.4 Ativação e Retenção
Capítulo XV · Produto e Growth Engineering
15.4

Ativação e Retenção

É fácil obcecar por atrair usuários — encher a porta da frente. Mas se eles não chegam ao valor na primeira vez (ativação) e não voltam (retenção), o produto é um balde furado: enche pela frente e vaza por trás. Ativação e retenção não são tarefas de marketing; projetam-se na própria arquitetura.

Pré-requisitos · 15.2 Métricas · Cap V Design · desempenhoConecta com · 15.5 CrescerTempo de leitura · ~14 min

O Problema

Há uma obsessão natural, e perigosa, no mundo do produto: a aquisição. Atrair usuários novos — encher a "porta da frente" — é visível, mensurável (ainda que por métricas de vaidade, §15.2) e gratificante: o número de novos cadastros sobe, parece que o produto está crescendo. Mas atrair usuários é apenas o começo, e focar só nisso ignora as duas perguntas que de fato determinam se o produto vive: os usuários que chegam encontram valor na primeira vez? E eles voltam?

Quando se foca só em atrair e se ignora o resto, o produto vira um balde furado: enche pela porta da frente (novos usuários entrando) e vaza pela de trás (usuários indo embora), e por mais que se despeje água na frente, o balde nunca enche, porque vaza na mesma velocidade. Há dois furos distintos. O primeiro é a falha de ativação: o usuário chega, mas não consegue chegar ao valor na primeira experiência. Encontra um produto confuso, um caminho longo demais até o primeiro benefício, uma fricção que o faz desistir antes de entender por que aquilo seria útil. Ele se cadastrou, mas nunca ativou — nunca teve o momento em que o produto provou seu valor —, e então some, levando consigo todo o esforço gasto para atraí-lo. O segundo furo é a falha de retenção: o usuário até chegou ao valor uma vez, mas não volta. Não houve razão para retornar, ou o produto não se tornou parte da sua rotina, ou a primeira impressão boa não se sustentou. Ele usou uma vez e foi embora. Em ambos os casos, o resultado é o mesmo: a aquisição, por mais bem-sucedida, é desperdiçada, porque atrair quem não se ativa nem se retém é encher um balde furado. E há uma armadilha conceitual que agrava tudo: tratar ativação e retenção como problemas de marketing — coisas a resolver com campanhas, e-mails de reengajamento, promoções para trazer as pessoas de volta. Esses paliativos podem ajudar nas margens, mas atacam o sintoma, não a causa. Se o usuário não se ativa porque o produto é confuso ou lento até o primeiro valor, nenhuma campanha resolve — o problema está no produto, não na comunicação. Se o usuário não volta porque o produto não lhe dá uma razão para voltar, nenhum e-mail de reengajamento sustenta isso por muito tempo — ele pode trazer a pessoa de volta uma vez, mas ela vazará de novo pelo mesmo furo. O problema de fundo é uma miopia: olhar apenas para a entrada (a aquisição) e não para a experiência que determina se a entrada vira valor duradouro (a ativação e a retenção) — e, pior, atribuir essas a outra área (marketing) quando elas dependem, na verdade, do produto em si, que é responsabilidade da engenharia. Um produto que atrai muito e ativa e retém pouco está, no fundo, gastando energia para encher um balde que não foi feito para reter água.

O balde furado

Focar só em atrair usuários, ignorando se eles chegam ao valor na primeira vez (ativação) e se voltam (retenção), é encher a porta da frente enquanto se vaza pela de trás. E tratar ativação e retenção como problemas de marketing ataca o sintoma, não a causa — que está no produto. Correção: projetar a ativação (valor rápido na primeira vez) e a retenção (razão para voltar) na própria arquitetura.

A Decisão

Projetam-se a ativação e a retenção no produto, não no marketing: a ativação, fazendo o usuário chegar ao valor rápido na primeira vez — caminho curto, fricção mínima, desempenho (Cap V); a retenção, dando-lhe uma razão real para voltar. Tapa-se o furo do balde, em vez de despejar mais água.

A plataforma trata a ativação e a retenção como responsabilidades de engenharia de produto — projetadas na arquitetura, não delegadas a campanhas. A ativação é o objetivo de fazer o usuário chegar ao valor o mais rápido possível na primeira experiência: ter, cedo, o momento em que o produto prova por que vale a pena. Isso se projeta no produto de formas concretas. Encurta-se o caminho até o primeiro valor: remove-se cada passo desnecessário entre a chegada e o benefício, cada campo de cadastro que não é essencial, cada fricção que adia o momento da prova — aplicando a subtração do Manifesto (§15.1) à jornada inicial. Cuida-se da experiência (o Capítulo V): a primeira impressão é clara, o produto guia o usuário ao valor sem confundi-lo, a interface não cobra esforço antes de retribuir. E cuida-se do desempenho ao primeiro valor: se chegar ao benefício é lento, o usuário desiste antes — a rapidez não é luxo, é parte da ativação, e toda a engenharia de performance do livro serve aqui ao produto. A retenção é o objetivo de fazer o usuário voltar a encontrar valor: não usar uma vez e sumir, mas retornar porque o produto continua sendo útil. Isso também se projeta no produto: garante-se que haja uma razão real para voltar — o produto resolve um problema recorrente, ou melhora com o uso, ou se torna parte de um fluxo de trabalho —, e que a experiência se sustente além da primeira boa impressão, mantendo a qualidade e a confiabilidade (toda a fundação dos volumes anteriores: um produto que falha, é lento ou perde dados não retém ninguém). A diferença entre essa abordagem e os paliativos de marketing é a diferença entre tapar o furo e despejar mais água. As campanhas de reengajamento despejam água: trazem pessoas de volta sem consertar o furo pelo qual elas vazaram, então o esforço se repete sem fim. Projetar a ativação e a retenção tapa o furo: os usuários se ativam porque o produto os leva ao valor, e voltam porque o produto lhes dá razão — e aí a aquisição deixa de ser desperdiçada, porque cada usuário atraído tem a chance real de virar um usuário que fica. Há uma conexão direta com o tópico anterior: ativação e retenção são, elas mesmas, as métricas que importam (§15.2). Medir a taxa de ativação (dos que chegam, quantos alcançam o valor?) e a retenção (dos que se ativam, quantos voltam?) é medir a saúde real do produto, em oposição às métricas de vaidade da pura aquisição. E são métricas honestas — podem dar más notícias, mostrando onde o balde vaza —, e é exatamente por isso que guiam a melhoria. Projetam-se ainda com a experimentação (§15.3): testa-se o que de fato melhora a ativação e a retenção, em vez de adivinhar. Tudo converge: pensar como produto (resolver o problema), medir o que importa (ativação, retenção), experimentar (descobrir o que ajuda) e projetar a jornada (do primeiro valor ao retorno) são as faces de uma mesma disciplina — a de construir um produto que não apenas atrai pessoas, mas as serve tão bem que elas chegam ao valor e voltam. Um balde só retém água se for feito para isso; um produto só retém usuários se a ativação e a retenção forem projetadas nele, e não esperadas do marketing.

Diagrama conceitual Tapar o furo: ativar e reter, não só atrair
SÓ ATRAIR (balde furado)enche a porta da frentee vaza pela de trás ATIVAR + RETERvalor sentido cedo (1ª vez)e de novo (volta a usar) ATIVAÇÃO: valor rápido RETENÇÃO: valor de novo projetadas na arquitetura (Cap V, desempenho), não enfeites de marketing de nada adianta atrair quem não chega ao valor nem volta
Figura 15.4-a. Focar só em atrair é encher um balde furado — vaza pela ativação (não chega ao valor) e pela retenção (não volta). Projetam-se a ativação (valor rápido na 1ª vez) e a retenção (razão para voltar) no produto — experiência (Cap V), desempenho —, não no marketing.

A ativação projetada: o caminho curto ao primeiro valor

ativacao.pyPython
# ATIVAÇÃO: levar ao valor RÁPIDO na 1ª vez — projetada no PRODUTO.
def jornada_inicial(usuario: Usuario) -> Experiencia:
    # Subtração (§15.1): remover cada passo entre a chegada e o valor.
    passos = remover_friccao_desnecessaria(caminho_ate_o_valor)

    # Experiência clara (Cap V) + desempenho: chegar ao benefício é rápido.
    afirmar(passos.tempo_ate_primeiro_valor() < LIMITE_CURTO)

    # O usuário SENTE por que vale a pena — antes de desistir.
    return conduzir_ao_momento_de_valor(usuario, passos)
    # Sem ativação, a aquisição é desperdiçada (balde furado).
A ativação é projetada no produto: remover fricção (subtração §15.1), experiência clara (Cap V) e desempenho para levar ao primeiro valor rápido, antes que o usuário desista.
retencao.pyPython
# RETENÇÃO: dar RAZÃO REAL para voltar — também no produto, não em e-mails.
def razao_para_voltar(produto: Produto) -> bool:
    # O produto resolve um problema RECORRENTE / vira parte da rotina?
    if not produto.resolve_problema_recorrente():
        return False   # sem razão real → reengajamento só adia o vazamento

    # A experiência se SUSTENTA além da 1ª impressão (a fundação inteira):
    # não falha, não é lento, não perde dados → um produto que MERECE volta.
    return produto.confiavel() and produto.continua_util()

# ATIVAÇÃO e RETENÇÃO são as MÉTRICAS QUE IMPORTAM (§15.2) — honestas.
# Projetadas com EXPERIMENTAÇÃO (§15.3): testa-se o que de fato melhora.
A retenção também se projeta no produto: uma razão real para voltar (problema recorrente) e uma experiência que se sustenta (a fundação inteira), medida como métrica honesta (§15.2).

A Verificação

ativacao_retencao_test.pyPython
class TestAtivacaoERetencao(TestCase):
    def test_caminho_ate_o_valor_e_curto(self):
        # O usuário chega ao primeiro valor sem fricção desnecessária.
        j = jornada_inicial(self.novo_usuario())
        self.assertLess(j.passos_ate_o_valor(), self.limite_de_passos)

    def test_produto_da_razao_para_voltar(self):
        # A retenção vem do produto (problema recorrente), não de campanhas.
        self.assertTrue(razao_para_voltar(self.produto))

    def test_ativacao_e_retencao_sao_medidas(self):
        # São métricas que importam (§15.2) — honestas, podem cair.
        self.assertTrue(self.mede_taxa_de_ativacao())
        self.assertTrue(self.mede_retencao())
Os testes fixam o cerne: o caminho ao primeiro valor é curto (ativação), o produto dá razão para voltar (retenção), e ambas são medidas como métricas honestas (§15.2).
Tabela de decisão Só atrair vs ativar e reter
AspectoFoco só em atrairProjetar ativação e retenção
MetáforaBalde furadoBalde que retém
1ª experiênciaConfusa, lentaValor rápido (ativação)
Volta?Usa uma vez e someRazão real para voltar
Responsabilidade"É do marketing"Do produto/engenharia
AquisiçãoDesperdiçadaVira valor duradouro

Estudo de Caso

Caso aplicado

Tapar o furo em vez de despejar mais água

Contexto. Ao trabalhar os fluxos de entrada de novos usuários e a comunicação de produto, a tentação seria concentrar o esforço em atrair e em trazer pessoas de volta por campanhas — despejando água na frente —, sem garantir que os usuários chegassem ao valor na primeira vez e tivessem razão para voltar.

Só atrair
Balde furado, aquisição desperdiçada
Ativar e reter
Valor cedo e razão para voltar
Ganho
Usuários que chegam ao valor e ficam

Decisão. Tratar ativação e retenção como responsabilidades do produto: encurtar o caminho até o primeiro valor (subtração §15.1), cuidar da experiência (Cap V) e do desempenho para que o usuário sinta o benefício cedo, e assegurar que o produto desse uma razão real para voltar e que a experiência se sustentasse — apoiada na confiabilidade da fundação inteira. Medir ativação e retenção como métricas honestas (§15.2) e melhorá-las por experimentação (§15.3).

Resultado. O foco deslocou-se de apenas encher a porta da frente para tapar os furos do balde: fazer os usuários atraídos efetivamente chegarem ao valor e voltarem. A aquisição deixou de ser desperdiçada, porque cada usuário passou a ter a chance real de virar um usuário que fica. E ativação e retenção, reconhecidas como problemas de produto e não de marketing, voltaram-se para onde a engenharia podia de fato resolvê-las. Todas as disciplinas do volume convergiram: pensar como produto, medir o que importa, experimentar e projetar a jornada do primeiro valor ao retorno.

Incidente Real

Severidade · Baixa

A jornada inicial que precisava levar ao valor

  • Antes Os fluxos de entrada de novos usuários podiam conter fricção ou passos que adiavam o momento em que a pessoa percebia o valor, e a retenção podia ser buscada por reengajamento em vez de por uma razão real, no produto, para voltar.
  • Sintoma Sem ativação e retenção projetadas, o esforço de atrair usuários arriscava ser desperdiçado: pessoas que chegavam podiam não alcançar o valor na primeira vez (e sumir) ou não ter motivo para retornar, como um balde que vaza enquanto se enche.
  • Diagnóstico O foco tendia à aquisição, tratando ativação e retenção como questões de comunicação, quando dependiam do produto — da jornada ao primeiro valor e da razão para voltar.
  • Correção A ativação foi projetada encurtando o caminho ao primeiro valor (experiência e desempenho) e a retenção, assegurando razão real para voltar e experiência que se sustenta; ambas medidas e experimentadas.
  • Kaizen Vira regra: ativação e retenção são projetadas no produto (não no marketing), medidas como métricas que importam e melhoradas por experimentação.

Decisão Arquitetural

ADR-0079

Projetar ativação e retenção no produto, não no marketing

Aceito
Contexto

Focar só em aquisição, ignorando se os usuários chegam ao valor na primeira vez (ativação) e voltam (retenção), faz do produto um balde furado: enche pela frente e vaza por trás, desperdiçando o esforço de atrair. Tratar ativação e retenção como problemas de marketing (campanhas, reengajamento) ataca o sintoma, não a causa, que está no produto.

Decisão

Ativação e retenção são projetadas como engenharia de produto. Ativação: levar ao primeiro valor rápido — caminho curto (subtração §15.1), experiência clara (Cap V), desempenho. Retenção: dar razão real para voltar (problema recorrente) e sustentar a experiência (a fundação inteira: confiável, rápida, sem perda de dados). São as métricas que importam (§15.2), honestas, e melhoram-se por experimentação (§15.3). Tapa-se o furo, em vez de despejar mais água.

Alternativas consideradas

Focar só em atrair — balde furado, aquisição desperdiçada. Ativação/retenção como marketing — paliativo que não conserta a causa no produto. Ignorar a primeira experiência — usuário não se ativa e some.

Consequências
PositivasAquisição vira valor duradouro. Usuários chegam ao valor (ativação) e voltam (retenção). O furo do balde é tapado na causa. A saúde real do produto é medida e melhorada.
NegativasExige projetar a jornada e investir na experiência e no desempenho ao primeiro valor.
RiscosA razão para voltar varia por produto; mitigado por medir a retenção real (§15.2) e descobrir, por experimentação (§15.3), o que de fato faz os usuários voltarem.

Antipadrões

Encher o balde furado

Investir só em atrair usuários, ignorando se chegam ao valor e voltam. A aquisição vaza e se desperdiça. Correção: projetar a ativação e a retenção no produto, tapando o furo.

Terceirizar ativação e retenção ao marketing

Esperar que campanhas resolvam o que é um problema do produto (jornada confusa, sem razão para voltar). Correção: a engenharia projeta o caminho ao valor e a razão para voltar.

Exercício

Encontre os furos do seu balde

  1. Pergunte: você sabe quantos dos usuários que se cadastram chegam de fato ao primeiro valor (ativação)? E quantos voltam depois (retenção)? Ou só acompanha quantos entram?
  2. Cronometre o caminho ao valor: quantos passos e quanto tempo um novo usuário leva, desde a chegada, até sentir o benefício do seu produto? Onde há fricção que poderia ser removida (§15.1)?
  3. Faça o teste da razão para voltar: por que um usuário voltaria ao seu produto amanhã? Há uma razão real no produto (problema recorrente, melhora com o uso), ou você depende de lembrá-lo por e-mail?
  4. Faça o teste do dono: você trata ativação e retenção como problemas seus (do produto) ou os atribui ao marketing? O que, no produto, está fazendo os usuários vazarem?
Meta: localizar os furos do seu balde — onde os usuários atraídos vazam por não se ativarem ou não voltarem — e planejar tapá-los na causa, projetando a ativação e a retenção no produto, para que a aquisição vire valor duradouro.
✓ Checklist do tópico
  • Mede-se a ativação (dos que chegam, quantos alcançam o valor) e a retenção (quantos voltam)
  • O caminho até o primeiro valor é curto, com fricção mínima (subtração §15.1)
  • A primeira experiência é clara (Cap V) e o desempenho ao primeiro valor é rápido
  • O produto dá uma razão real para voltar; a experiência se sustenta (fundação confiável)
  • Ativação e retenção são tratadas como problemas de produto, melhorados por experimentação (§15.3)
Meta Dados · Engenharia de Plataformas · Capítulo XV · §15.4Próximo → 15.5 Crescer sem Quebrar
Capítulo XV · Produto e Growth Engineering
15.5

Crescer sem Quebrar

Eis o paradoxo final: o sucesso pode matar um produto. O crescimento que ele busca traz uma carga que, sem a fundação para sustentá-la, quebra o sistema — e o produto morre não do fracasso, mas da vitória. Este último tópico fecha o arco da obra: o que se constrói para durar é, exatamente, o que pode crescer sem quebrar.

Pré-requisitos · TODA a obra — Cap I a XVConecta com · encerra a obraTempo de leitura · ~16 min

O Problema

Chegamos ao fim. Quatorze capítulos ensinaram a construir bem; este último voltou a engenharia para o produto que ela serve — pensar no problema real, medir o que importa, experimentar, projetar a ativação e a retenção. E tudo isso, dando certo, leva a uma coisa: crescimento. O produto resolve um problema real, as pessoas chegam ao valor e voltam, e cada vez mais gente o usa. É o que se queria. E é aqui que mora o paradoxo final, o mais cruel de todos: o sucesso pode matar o produto. O crescimento que se buscou traz uma carga — mais usuários, mais dados, mais requisições, mais tudo — e essa carga, se o sistema não foi construído para suportá-la, quebra o sistema. O produto morre não do fracasso, mas da vitória.

A morte por sucesso tem uma assinatura reconhecível. Um produto começa a crescer, e de repente tudo o que funcionava sob carga leve começa a falhar sob carga pesada: o banco de dados que respondia rápido com mil usuários se arrasta com um milhão; o servidor único que bastava não dá conta e cai; a operação que uma pessoa tocava à mão vira ingovernável; os custos explodem; o sistema fica lento, depois instável, depois fora do ar — justamente quando mais gente quer usá-lo. A indisponibilidade chega no pior momento possível: no momento do sucesso, quando cada usuário que encontra um produto quebrado é uma oportunidade perdida e uma reputação manchada. E há uma ironia amarga: o crescimento que deveria ser a recompensa pelo bom produto torna-se a causa da sua ruína, porque o produto era bom como produto mas frágil como sistema. Foi construído pensando em resolver o problema, mas não em aguentar o sucesso de resolvê-lo. As causas dessa fragilidade são, ponto por ponto, tudo aquilo contra o que este livro se levantou. O sistema não escala porque não foi pensado para a carga (não houve a otimização de dados do §11.2, nem a separação que permite crescer). Ele cai por inteiro porque tem pontos únicos de falha que a carga expõe (não houve a redundância do §13.5, nem a defesa em profundidade do §12.2). Ninguém percebe os problemas chegando porque o sistema é uma caixa-preta sem instrumentação (não houve a observabilidade do Capítulo VIII). A confiabilidade despenca porque não havia disciplina para geri-la (não houve os SLOs do §13.1). A operação afoga porque tudo era manual (não houve a guerra ao toil do §13.2, nem a automação do Capítulo X). E o sistema não consegue mudar para se adaptar à nova escala porque é um nó rígido (não houve as fronteiras do §14.1, nem a evolução gradual do §14.5). O problema de fundo é uma cisão entre construir um bom produto e construir um bom sistema — como se fossem coisas separadas, como se bastasse resolver o problema do usuário sem se preocupar em aguentar o peso de muitos usuários. Mas não são separadas. Um produto que não aguenta o próprio sucesso não é, no fim, um bom produto — é uma promessa que se quebra exatamente quando é cobrada. Crescer sem quebrar não é um luxo a adicionar depois que o produto der certo; é a condição para que o "dar certo" não se transforme em catástrofe.

A morte por sucesso

O crescimento traz uma carga que, sem a fundação para sustentá-la, quebra o sistema — e o produto morre da própria vitória, ficando fora do ar justamente quando mais gente quer usá-lo. As causas são tudo contra o que este livro se levantou: não escalar, ter pontos únicos de falha, ser caixa-preta, não ter disciplina de confiabilidade, afogar em trabalho manual, não conseguir evoluir. Correção: a fundação inteira da obra é, precisamente, o que permite crescer sem quebrar.

A Decisão

Cresce-se sem quebrar porque o sistema foi construído, desde a fundação, para durar — e o que foi construído para durar é exatamente o que aguenta o crescimento. Toda a obra converge aqui: escala, observabilidade, confiabilidade, segurança e evolução são o que faz o sucesso ser sustentável, não fatal.

A plataforma cresce sem quebrar porque nunca separou construir um bom produto de construir um bom sistema — e a fundação que percorre toda esta obra é, precisamente, o que torna o crescimento sustentável em vez de fatal. Não há, neste tópico final, uma técnica nova a apresentar: a resposta para "como crescer sem quebrar?" é tudo o que veio antes. Cada volume foi, visto deste ponto de chegada, uma peça da capacidade de aguentar o sucesso. O sistema escala sob a carga do crescimento porque os dados foram pensados para a escala (a ingestão e a otimização do Capítulo XI, o índice que cortou a consulta drasticamente no §11.2, a separação entre operação e análise do §11.3) e porque a arquitetura distribui a carga em vez de concentrá-la. O sistema não cai por inteiro quando a carga aperta porque não tem pontos únicos de falha — nem técnicos (a defesa em profundidade do §12.2, a autocura do §10.3) nem geográficos (a sobrevivência à queda de uma região do §13.5). Os problemas são vistos chegando, e tratados antes de virarem crise, porque o sistema é observável (os três pilares do Capítulo VIII, os SLOs e o orçamento de erro do §13.1 que medem a saúde real sob carga). A operação não afoga com o crescimento porque o trabalho repetitivo foi automatizado (a guerra ao toil do §13.2, a infraestrutura como código e a orquestração do Capítulo X), e quem opera não se esgota porque o plantão é sustentável e segue o sol (§13.3, §13.4). O sistema permanece seguro mesmo se tornando um alvo maior com o sucesso (a modelagem de ameaças, a defesa em profundidade e a postura verificável do Capítulo XII). E o sistema consegue evoluir para acompanhar a nova escala, sem parar e sem grandes reescritas, porque foi construído modular, com fronteiras claras e substituição gradual (as fronteiras de contexto do §14.1, a evolução sem reescrita do §14.5, a entrega segura do §10.4). Tudo converge: crescer sem quebrar é ter construído, desde o início, com escala, observabilidade, confiabilidade, segurança e capacidade de evolução — não como enfeites, mas como a substância de um sistema que merece durar. E aqui o arco da obra se fecha sobre si mesmo. O Capítulo I começou com os fundamentos; este Capítulo XV termina com o crescimento — e a tese final é que são as duas pontas do mesmo fio. Não se constrói a fundação para depois, talvez, crescer; constrói-se a fundação porque crescer sem quebrar exige tê-la desde o começo. O que se faz para durar é o que pode crescer; o que se faz às pressas, para apenas funcionar hoje, quebra quando o sucesso cobra amanhã. E é exatamente isto que o Manifesto proclamou, lá no início de tudo, e que agora se cumpre: "Construa com menos. Desenhe com propósito. Forje plataformas que mereçam durar." Construir com menos — a subtração que torna o sistema simples o bastante para escalar e evoluir, em vez de um nó complexo que desaba. Desenhar com propósito — a intenção que põe a engenharia a serviço de resolver um problema real, e de aguentar o peso de resolvê-lo para muitos. Forjar plataformas que mereçam durar — e merecer durar é, no fim, poder crescer sem quebrar, continuar servindo conforme mais gente precisa, não morrer da própria vitória. Toda a obra foi a explicação detalhada, volume a volume, tópico a tópico, decisão a decisão, do que significa cumprir esse Manifesto. E o humano solitário às três da manhã, por quem o Manifesto se comoveu — aquele que mantém o sistema de pé quando algo falha — é também quem colhe o fruto desta obra: porque um sistema construído para crescer sem quebrar é um sistema que falha menos, que avisa antes, que se cura sozinho, que não esgota quem o cuida. Construir assim é um ato de respeito: por quem usa o produto, por quem o opera, e pelo próprio ofício de fazer software que serve e que dura. Este é o fim do livro, mas não da prática — porque cada sistema que se construir daqui em diante é uma chance de construir com menos, desenhar com propósito, e forjar algo que mereça durar. Construa bem. Construa a coisa certa. Forje plataformas que mereçam durar — e que, por isso, possam crescer sem quebrar.

Diagrama conceitual O arco completo: da fundação ao crescimento
CRESCER E QUEBRARo sucesso derruba o sistemaa carga vence a fundação ausente CRESCER SEM QUEBRARa fundação aguenta o sucessoo que se fez para durar, cresce o arco completo: da FUNDAÇÃO (Cap I) ao CRESCIMENTO (Cap XV)escala · observabilidade · confiabilidade · segurança · evolução — tudo converge aqui escalar §11.2 §13.5 observar Cap VIII confiar Cap XIII evoluir §14.5 "Construa com menos. Desenhe com propósito. Forje plataformas que mereçam durar." o que merece durar é o que pode crescer sem quebrar — o Manifesto cumprido
Figura 15.5-a. Crescer sem fundação deixa o sucesso quebrar o sistema. Crescer sem quebrar é o destino de toda a obra: escala, observabilidade, confiabilidade, segurança e evolução convergindo. O que se faz para durar é o que pode crescer — o Manifesto cumprido.

Não há técnica nova: há toda a obra

crescer_sem_quebrar.pyPython
# CRESCER SEM QUEBRAR não é uma técnica nova — é TODA a obra reunida.
def aguenta_o_sucesso(sistema: Plataforma) -> bool:
    return (
        sistema.escala()              # §11.2 dados, §11.3 OLTP/OLAP
        and sistema.sem_ponto_unico()     # §12.2 defesa, §13.5 região
        and sistema.observavel()          # Cap VIII, §13.1 SLO
        and sistema.operacao_automatizada()  # §13.2 toil, Cap X
        and sistema.seguro()              # Cap XII
        and sistema.pode_evoluir()        # §14.1 fronteiras, §14.5 sem reescrita
    )
    # O que se construiu para DURAR é o que pode CRESCER sem quebrar.
    # Fundação (Cap I) e crescimento (Cap XV): as duas pontas do mesmo fio.
Não há técnica nova: crescer sem quebrar é a fundação inteira da obra reunida. O que se construiu para durar — escalável, observável, confiável, seguro, evoluível — é o que cresce.
o_manifesto_cumprido.txttexto
  O MANIFESTO, lá no início, proclamou — e aqui se cumpre:

  ┌─────────────────────────────────────────────────────────────┐
  │  "Construa com MENOS."                                       │
  │     → a subtração que faz o sistema escalar e evoluir,       │
  │       em vez de um nó complexo que desaba sob o peso.        │
  │                                                             │
  │  "Desenhe com PROPÓSITO."                                    │
  │     → a engenharia a serviço de resolver um problema real    │
  │       — e de aguentar o peso de resolvê-lo para muitos.      │
  │                                                             │
  │  "Forje plataformas que mereçam DURAR."                      │
  │     → e merecer durar é poder CRESCER SEM QUEBRAR:           │
  │       continuar servindo conforme mais gente precisa.       │
  └─────────────────────────────────────────────────────────────┘

  Da fundação (Cap I) ao crescimento (Cap XV): o arco está completo.
  Construa bem. Construa a coisa certa. Forje algo que mereça durar.
O tópico final fecha o arco da obra sobre o Manifesto: construir com menos, desenhar com propósito e forjar plataformas que mereçam durar é, no fim, poder crescer sem quebrar.

A Verificação

crescimento_test.pyPython
class TestCrescerSemQuebrar(TestCase):
    def test_sistema_aguenta_a_carga_do_sucesso(self):
        # Sob carga muitas vezes maior, o sistema segue de pé e responde.
        self.aplicar_carga(fator=100)
        self.assertTrue(self.sistema_no_ar())
        self.assertTrue(self.dentro_do_slo())   // §13.1, sob sucesso

    def test_a_fundacao_inteira_sustenta_o_crescimento(self):
        # Crescer sem quebrar = a obra inteira reunida e verificada.
        self.assertTrue(aguenta_o_sucesso(self.plataforma))

    def test_o_que_dura_e_o_que_cresce(self):
        # A tese final: durar e crescer sem quebrar são a mesma coisa.
        self.assertEqual(self.foi_construido_para_durar(),
                         self.pode_crescer_sem_quebrar())
Os testes encerram a obra: o sistema aguenta a carga do sucesso dentro do SLO, a fundação inteira sustenta o crescimento, e durar e crescer sem quebrar se confirmam como a mesma coisa.
Tabela de decisão Crescer e quebrar vs crescer sem quebrar
DimensãoSem a fundação (quebra)Com a fundação (cresce)
Escala (§11.2)Trava sob cargaAguenta a carga
Ponto único (§13.5)Cai por inteiroResiste à falha
Observar (Cap VIII)Caixa-pretaVê o problema chegar
Operar (§13.2)Afoga no manualAutomatizado
Evoluir (§14.5)Nó rígidoMuda sem parar
ResultadoMorre da vitóriaSucesso sustentável

Estudo de Caso

Caso aplicado

Um ecossistema construído para aguentar o próprio sucesso

Contexto. A plataforma cresceu de um sistema a um ecossistema de muitos produtos, processando volumes de dados que chegaram à casa de centenas de milhões de registros e atendendo operações reais e críticas. Cada passo de crescimento poderia ter sido um ponto de ruptura — onde a carga venceria um sistema não preparado e o sucesso se tornaria indisponibilidade.

Crescer sem fundação
O sucesso quebraria o sistema
A obra inteira
A fundação aguenta o crescimento
Ganho
Sucesso sustentável — o arco completo

Decisão. Construir, ao longo de toda a sua história, sobre a fundação que esta obra descreve: dados otimizados para a escala (a ingestão massiva e o índice que cortou drasticamente o tempo de consulta), ausência de pontos únicos de falha, observabilidade, disciplina de confiabilidade, automação contra o toil, segurança verificável e capacidade de evoluir sem grandes reescritas — nunca separando construir um bom produto de construir um bom sistema.

Resultado. O crescimento foi sustentado em vez de fatal: o sistema aguentou o peso do próprio sucesso porque foi forjado, desde a fundação, para durar — e o que se faz para durar é exatamente o que pode crescer sem quebrar. As duas pontas do arco se encontraram: os fundamentos do início e o crescimento do fim revelaram-se o mesmo fio. É o Manifesto cumprido — construir com menos, desenhar com propósito, forjar uma plataforma que merece durar e que, por isso, pôde crescer sem se quebrar.

Incidente Real

Severidade · Alta

Cada salto de escala que a fundação tornou sustentável

  • Antes Ao longo do crescimento da plataforma — em volume de dados, número de sistemas e criticidade das operações —, cada salto de escala representava um risco de que a carga superasse a capacidade e o sucesso se convertesse em instabilidade.
  • Risco Sem dados otimizados, redundância, observabilidade, disciplina de confiabilidade, automação e capacidade de evolução, o crescimento tenderia a expor fragilidades e a derrubar o sistema justamente quando mais demandado — a morte por sucesso.
  • Diagnóstico A única defesa real contra a morte por sucesso é ter construído a fundação inteira desde o início, em vez de tratá-la como algo a adicionar depois que o produto crescesse.
  • Correção A plataforma foi construída sobre toda a fundação da obra — escala, ausência de ponto único, observabilidade, confiabilidade, automação, segurança e evolução —, tornando cada salto de crescimento sustentável.
  • Kaizen Vira regra, e é a tese final do livro: constrói-se para durar desde a fundação, porque o que se faz para durar é exatamente o que pode crescer sem quebrar.

Decisão Arquitetural

ADR-0080

Crescer sem quebrar: a fundação inteira é a condição do crescimento sustentável

Aceito
Contexto

O sucesso pode matar o produto: o crescimento traz uma carga que, sem a fundação para sustentá-la, quebra o sistema, que fica fora do ar justamente quando mais demandado. A causa é a cisão entre construir um bom produto e um bom sistema — resolver o problema do usuário sem se preparar para aguentar o peso de resolvê-lo para muitos.

Decisão

Cresce-se sem quebrar porque o sistema foi construído, desde a fundação, para durar — e isso é a obra inteira reunida: escala (§11.2, §11.3), ausência de pontos únicos de falha (§12.2, §13.5), observabilidade (Cap VIII, §13.1), operação automatizada e sustentável (§13.2, §13.3, §13.4, Cap X), segurança verificável (Cap XII) e capacidade de evoluir sem reescrita (§14.1, §14.5, §10.4). Fundação (Cap I) e crescimento (Cap XV) são as duas pontas do mesmo fio. É o Manifesto cumprido.

Alternativas consideradas

Crescer sem a fundação — o sucesso quebra o sistema, morte por vitória. Adicionar a fundação "depois" — tarde demais, a carga já chegou. Separar bom produto de bom sistema — uma promessa que se quebra quando é cobrada.

Consequências
PositivasO crescimento é sustentável, não fatal. O sistema aguenta o peso do próprio sucesso. Durar e crescer sem quebrar confirmam-se como a mesma coisa. A obra inteira cumpre o seu propósito.
NegativasExige ter construído com escala, observabilidade, confiabilidade, segurança e evolução desde o início — o trabalho de toda a obra.
RiscosNenhum sistema é infinitamente escalável; mitigado por crescer com a disciplina de toda a obra, medindo a saúde sob carga (§13.1) e evoluindo continuamente (§14.5).

Antipadrões

Deixar o sucesso quebrar o sistema

Crescer sem ter construído a fundação para aguentar a carga. O produto morre da própria vitória, fora do ar quando mais demandado. Correção: construir para durar desde o início — a fundação inteira é a condição do crescimento.

Tratar a fundação como algo para depois

Adiar escala, observabilidade e confiabilidade para "quando o produto crescer". Quando a carga chega, é tarde. Correção: a fundação vem desde o começo, porque crescer sem quebrar a exige presente.

Exercício

Pergunte se o seu produto aguentaria o próprio sucesso

  1. Imagine que o seu produto crescesse dez ou cem vezes amanhã. O que quebraria primeiro? O banco de dados, o servidor, a operação manual, os custos? Esse é o seu furo de escala.
  2. Percorra a obra como uma lista de verificação do crescimento: o seu sistema escala (§11.2)? Tem pontos únicos de falha (§13.5)? É observável (Cap VIII)? Tem disciplina de confiabilidade (§13.1)? A operação afoga em trabalho manual (§13.2)? Consegue evoluir sem reescrita (§14.5)?
  3. Faça o teste da cisão: você tem tratado "bom produto" e "bom sistema" como coisas separadas — focando em funcionalidades e adiando a fundação? Onde essa cisão deixaria o sucesso te quebrar?
  4. Releia o Manifesto com os olhos de quem terminou o livro: onde, no seu sistema, você construiu com menos, desenhou com propósito, e forjou algo que merece — e pode — durar? E onde ainda não?
Meta: submeter o seu produto à pergunta final desta obra — ele aguentaria o próprio sucesso? — e usar toda a fundação do livro como o caminho para que o crescimento que você busca seja sustentável, e não a causa da sua ruína.
✓ Checklist do tópico
  • O sistema escala sob a carga do crescimento (dados e arquitetura preparados, §11.2)
  • Não há pontos únicos de falha, técnicos ou geográficos (§12.2, §13.5)
  • O sistema é observável e tem disciplina de confiabilidade sob carga (Cap VIII, §13.1)
  • A operação é automatizada e sustentável, sem afogar com o crescimento (§13.2, §13.3, §13.4)
  • O sistema pode evoluir para acompanhar a escala, sem parar nem reescrever (§14.1, §14.5)
  • Construiu-se para durar desde a fundação — porque o que dura é o que cresce sem quebrar
Meta Dados · Engenharia de Plataformas · Capítulo XV · §15.5 · Fim da obravoltar ao início · Construa com menos. Desenhe com propósito. Forje plataformas que mereçam durar.
metadados.io · Sobre o Autor

Djeferson Capelli

Empresário, arquiteto de soluções e especialista em infraestrutura digital — computação em nuvem, software corporativo e transformação digital. Fundador da Mettric Tecnologia.

Mais de duas décadas construindo plataformas que precisam funcionar de verdade — e durar.

Atuação
Engenharia de software, segurança cibernética, redes corporativas, inteligência de dados e automação de processos.
Fundador
Mettric Tecnologia — plataformas digitais para logística, gestão empresarial, análise de dados e infraestrutura crítica.
Pesquisa
IA, sistemas distribuídos, engenharia de plataformas e métodos modernos de desenvolvimento.

Trajetória

Djeferson Capelli atua há mais de duas décadas no setor de tecnologia da informação, liderando projetos que integram engenharia de software, segurança cibernética, redes corporativas, inteligência de dados e automação de processos. É empresário, arquiteto de soluções e especialista em infraestrutura digital, computação em nuvem, desenvolvimento de software corporativo e transformação digital.

Fundador da Mettric Tecnologia e idealizador de diversas plataformas digitais voltadas para logística, gestão empresarial, análise de dados e infraestrutura crítica, possui experiência prática na concepção, no desenvolvimento e na operação de ambientes de alta disponibilidade para empresas de diversos segmentos.

Transformar desafios operacionais complexos em soluções escaláveis e sustentáveis — conectando tecnologia, negócios e inovação para gerar resultados concretos.

Sua atuação combina visão executiva, experiência técnica e capacidade de execução. É reconhecido por uma abordagem multidisciplinar, que une tecnologia, negócios e inovação. Como pesquisador independente e entusiasta de tecnologias emergentes, dedica-se ao estudo contínuo de Inteligência Artificial, arquitetura de sistemas distribuídos, engenharia de plataformas digitais e metodologias modernas de desenvolvimento de software.

Este livro reflete a experiência acumulada em projetos reais, reunindo conceitos, práticas e estratégias aplicadas em ambientes corporativos modernos — com o objetivo de compartilhar conhecimento de forma acessível, prática e orientada à inovação.

O espírito do artesão

Há uma diferença entre fazer software funcionar hoje e forjar algo que mereça durar. O artesão não esconde a obra atrás de caixas-pretas: domina-a de ponta a ponta, da interface que antecipa a necessidade de quem usa ao elétron invisível que a sustenta. Conhece a diferença entre a rocha e o vento — o que é invariante vira código disciplinado; o que respira e muda vira metadado.

É a disciplina de subtrair até a essência: decidir olhando o trabalho real, não a suposição; remover o desnecessário; refletir sem culpa; melhorar continuamente. Cada linha que sobrevive a esse corte ganha o direito de existir. A arquitetura é a medida exata da empatia — escreve-se software não só para a máquina fria que o executa, mas para o humano que terá de operá-lo às três da manhã.

Construir com menos. Desenhar com propósito.
Forjar plataformas que mereçam durar.

Mapa de Competências

01

Arquitetura & Engenharia de Plataformas

  • Arquitetura de software corporativo
  • Clean Architecture & Domain-Driven Design
  • Plataformas metadata-driven
  • Desenvolvimento Full Stack (PHP moderno, TypeScript)
  • Sistemas distribuídos e de alta concorrência
02

Cloud Native & Infraestrutura Crítica

  • Computação em nuvem e virtualização
  • Contêineres & Infraestrutura como Código
  • Infraestrutura Linux e missão crítica
  • Ambientes de alta disponibilidade
  • SRE e operações sustentáveis
03

Dados & Inteligência

  • PostgreSQL (OLTP) · ClickHouse (OLAP) · Redis
  • Business Intelligence & Data Analytics
  • Engenharia de dados e pipelines
  • IA aplicada aos negócios & RAG governado
  • Camada semântica e inteligência operacional
04

Segurança & Redes

  • Segurança da informação
  • Redes corporativas
  • Defesa em profundidade & menor privilégio
  • Governança de acesso e LGPD
  • Ambientes de infraestrutura crítica
05

Integração & Automação

  • Integração de sistemas ERP, WMS e TMS
  • APIs, contratos e idempotência
  • Automação de processos e RPA
  • Observabilidade e instrumentação
  • Orquestração de fluxos operacionais
06

Estratégia, Produto & Governança

  • Visão de produto e growth engineering
  • Governança e estratégia de tecnologia
  • Transformação digital
  • Liderança técnica multidisciplinar
  • Visão executiva e geração de resultado

"Este livro reflete a experiência acumulada em projetos reais — para compartilhar conhecimento de forma acessível, prática e orientada à inovação."

Djeferson CapelliAutor · Mettric Tecnologia
Meta Dados · Engenharia de Plataformas · Edição Definitiva 1.1 · Junho de 2026
Obra completa · 80 decisões de arquitetura · Construído para durar.