Engenharia de plataforma

Isto não é um diagrama do que eu faria.
É a frota que está no ar agora.

Projetei, construí e opero sozinho um ecossistema SaaS multi-tenant em produção, com clientes reais pagantes. Esta página mostra os números medidos, como uma publicação acontece, e — a parte que importa — os incidentes que derrubaram o sistema e o que mudou depois de cada um.

Todos os números abaixo foram medidos na frota, não estimados
Leitura de 30 segundos

O que eu projeto, implemento, opero e meço

Esta tabela existe para você não precisar ler a página inteira. A coluna que mais importa é a última: medir é o que separa operar de supor. E onde eu só tenho formação, e não prática, está dito — quem marca tudo não informa nada.

Domínio ProjetoImplemento OperoMeço
Linux, rede e diagnóstico firewall com SSH único · rede segmentada em duas sub-redes · diagnóstico de contenção de bloqueio no banco
Containers e composição 224 containers · limites de CPU e concorrência declarados · uma imagem por repositório
Entrega contínua e estratégia de publicação canário com validação e rollback por digest · trava de frota · livro-caixa de publicação
Observabilidade e resposta a incidente 9 alvos · 12 regras · três post-mortems escritos, cada um com a guarda que ficou
Banco de dados e continuidade primário + réplica quente · WAL contínuo · RTO e RPO cronometrados
Segurança, identidade e multi-tenant zero porta exposta · isolamento no banco fail-closed · OAuth2 e JWKS próprios · cofre de segredos
Arquitetura distribuída e mensageria outbox transacional · 12 grupos de consumo · deduplicação ativa · fila de falhas vigiada
Infraestrutura como código módulos Terraform autorais para nuvem gerenciada (rede, banco, segurança, observabilidade) — não é o que opero hoje: a frota é declarativa por composição
Kubernetes formação concluída e manifestos escritos; decisão consciente de não operar — o porquê está no Ato III
Nuvem gerenciada de terceiros formação concluída; a operação real é em nuvem pública com serviços próprios — digo isso de propósito
em produção, hoje parcial ou fora de produção não — e eu digo
I

O que está em jogo

Não é laboratório. Há clientes pagando, e quando isto cai alguém deixa de vender. Todo o resto desta página existe por causa desta frase.

A frota

O tamanho da operação

31
microsserviços com deploy de produção
224
containers no ar — 118 de aplicação, 106 de plataforma
87
recriações de serviço por dia, num nó (mediana 79; de 2 a 172)
11.047
testes executados numa rodada de 3 serviços (31.438 definidos em 14)

Dois nós ativo-ativo

ARM64 em nuvem pública, 8 vCPU e 30 GB cada, atrás de túnel Cloudflare. Os dois servem tráfego: não existe nó ocioso esperando desastre.

Multi-tenant com isolamento no banco

Row-Level Security no PostgreSQL, fail-closed: sem o escopo do cliente na sessão, a consulta devolve zero linhas. O papel de runtime não tem BYPASSRLS.

Portão de qualidade que reprova

Cobertura mínima que falha o build: 95% em dois serviços, 90,3% em outro. Não é meta de parede — é condição para publicar.

De onde vem cada número acima
a pergunta que eu faria se fosse você — e a resposta, com o limite de cada medição
abrir

Número sem procedência é opinião com cara de dado. Cada linha abaixo diz o que foi contado, como, quando — e o que a medição NÃO prova. A última coluna é a que me interessa: medição honesta declara o próprio limite.

NúmeroComo foi medidoO que isto NÃO prova
31 contagem de arquivos de composição de produção nos repositórios que todos estejam ativos ao mesmo tempo — conta o que tem deploy definido
224 docker ps nos dois nós: 112 cada. Dos 112, 59 são processos de aplicação (web, trabalhador, publicador, consumidor, agendador); os outros 53 são barramento, dados, observabilidade, borda e cofre não é medida de carga nem de utilização — é contagem de processos
87 livro-caixa de publicação de um nó, 1.212 linhas em 14 dias. Cada linha = uma recriação de serviço com data, digest e commit. Mediana 79; varia de 2 a 172 por dia não é "87 versões novas": uma publicação lógica recria o serviço nos dois nós, e a média esconde a variação
11.047 testes executados numa rodada real de 3 serviços. Em paralelo: 31.438 funções de teste definidas em 14 serviços (contagem estática) os dois números medem coisas diferentes, e nenhum deles é cobertura — cobertura é o piso que reprova o build
RPO medido agora: atraso de replicação de 1,01 s e zero byte de WAL pendente de envio ao standby é uma amostra, não um teste de perda do primário. O RPO real sob falha só se conhece provocando a falha — está no plano, não no histórico
47 min exercício de provisionar um nó do zero e subir a frota nele, em setembro de 2026, no dia seguinte à migração para a nuvem atual (instâncias criadas em 12 e 16/09, conferidas nos metadados do provedor). Critério de “pronto”: autenticar com usuário e senha e entrar no sistema — o que exercita borda, túnel, roteamento, identidade, banco e sessão de ponta a ponta não prova que os 31 serviços estavam saudáveis nem que o trabalho de fundo já processava — prova que o caminho crítico do usuário estava de pé. É um critério de ponta a ponta, mais forte que “o container subiu” e mais estreito que “a frota inteira convergiu”
12 min tempo de a frota inteira subir e convergir, cronometrado no mesmo exercício de setembro de 2026, com o mesmo critério de ponta a ponta mesma ressalva: um exercício, numa data, num provedor. Repetir em outro provedor é o que transformaria o número em evidência de portabilidade, não só de recuperação
12 regras arquivo de alertas lido direto do serviço de métricas; 9 alvos ativos, todos respondendo no momento da leitura regra existir não é alerta testado: nem toda uma foi disparada de propósito para conferir o caminho até mim

“Mostre-me o diagrama e eu desconfio. Mostre-me o que acontece quando ele falha e eu acredito.”

o que esta página tenta responder
II

Como ela se sustenta

O desenho completo: por onde o tráfego entra, onde cada peça roda, como se chega ao dado — e o que acontece quando cada parte falha.

Antes da infraestrutura: o sistema visto de fora, e por dentro

Duas vistas que vêm antes do desenho de implantação. A primeira responde “quem usa isto e com que mundo externo ele fala”. A segunda responde “quais aplicações existem e como elas conversam” — sem nenhuma infraestrutura, de propósito: misturar as duas coisas num diagrama só é o erro que torna a maioria deles ilegível.

Diagrama de contexto: quem usa o sistema e com que mundo externo ele fala NÍVEL 1 · CONTEXTO quem usa, e com que mundo lá fora o sistema conversa Operador do cliente vende, compra, fatura Cliente final portal, recibo, assinatura Operador da plataforma uma pessoa — eu Zenithus SaaS multi-tenant 31 microsserviços uma origem única para todas as telas Adquirente · TEF cartão e conciliação Bureaus · Open banking crédito e extrato APIs de governo fiscal e trabalhista Assinatura · Marketplace contrato e venda externa Mensageria WhatsApp e e-mail tudo que sai passa por UM serviço de integração Fronteira de confiança nenhum ator alcança um serviço diretamente: toda entrada passa pela borda gerenciada e pela identidade própria ● pessoa ■ o sistema ▣ sistema externo
Diagrama de contêineres: quais aplicações existem e como elas se falam NÍVEL 2 · CONTÊINERES as aplicações e como conversam — sem infraestrutura, de propósito Navegador do usuário UMA ORIGEM — tudo abaixo responde sob o mesmo endereço Identidade Java · Spring Boot emite o token publica as chaves endpoints padrão OAuth2/OIDC Casca da aplicação Python · Django menu, contexto do cliente e sessão — embute as verticais 12 verticais de negócio Python · Java · Go vendas · estoque · fiscal financeiro · RH · frota… montadas por caminho Control Plane quem é o cliente, que plano tem, de quem é filho chamada SÍNCRONA, tipada Barramento de eventos um fato → 12 grupos dedup · fila de falhas caminho PADRÃO: assíncrono Integração 22 conectores · a ÚNICA que fala com terceiro devolve FATO normalizado PostgreSQL — isolamento de cliente no próprio banco um banco por vertical · a regra de quem-vê-o-quê não depende do código da aplicação A infraestrutura (nós, rede, balanceamento) está no diagrama de implantação logo abaixo — aqui, só aplicação.
Arquitetura da frota Zenithus — diagrama lógico e físico Do usuário ao banco: borda Cloudflare com WAF e túnel de saída, nenhuma porta de entrada no firewall, identidade própria servindo OAuth2 e JWKS na mesma origem única em que as verticais são montadas por caminho, rede segmentada em sub-rede de aplicação e sub-rede de dados, dois nós ativo-ativo, PostgreSQL primário com réplica quente e arquivamento contínuo de WAL, backup fora do provedor, e uma camada transversal de autorização, observabilidade e entrega contínua. Os pontos únicos de falha estão marcados. Arquitetura Zenithus — lógica e física Medida na frota, não estimada · identificadores e endereços omitidos de propósito 12 minRTO — frota inteira 47 minRTO — nó novo do zero segundosRPO — WAL contínuo 1 Usuários, parceiros e APIs HTTPS BORDA GERENCIADA — CLOUDFLARE o ataque morre antes de chegar ao nó DNS + TLS certificado gerenciado WAF filtro antes da origem 2 Tunnel — conector por nó conexão de SAÍDA; drenar = publicar sem risco R2 — backup fora do provedor dump 3/3h · base semanal · mídia · retenção Sem egress cobrado · sem lock-in a origem pode mudar de provedor sem reescrita FIREWALL DO SISTEMA — serviços liberados: apenas SSH. As portas 80 e 443 não existem na regra: o tráfego entra porque o nó SAI. OCI · região São Paulo · rede virtual privada SPOF aceito: os 2 nós no mesmo domínio de disponibilidade SUB-REDE DE APLICAÇÃO · /24 sem IP público · egresso por NAT NÓ A — canário (publica primeiro) ATIVO ARM64 · 8 vCPU · 30 GB · 112 containers cloudflared Traefik — roteia host + caminho 3 IDENTIDADE PRÓPRIA (Spring Boot) — OAuth2 + JWKS autenticação e chaves na MESMA origem das telas sessão num domínio só: sem CORS, sem cookie de terceiro ORIGEM ÚNICA — 12 verticais montadas por CAMINHO /app/<vertical> — um prefixo por domínio de negócio uma casca hospeda as demais; nenhuma vertical tem rota pública própria Barramento local — 14 Redis (um por vertical) outbox transacional → relay → Streams → inbox com dedup → DLQ 5 HAProxy — porta única de banco (um backend; promoção manual) NÓ B — estável ATIVO idêntico · nenhum dos dois é reserva ociosa cloudflared Traefik 4 SDK ÚNICO DA PLATAFORMA uma implementação de autorização para todas as verticais — não 12 cópias divergentes escopo por requisição, fail-closed: sem escopo, a chamada é NEGADA máquina-a-máquina: chave por serviço a verdade de plano e tenant vem do Control Plane STACKS POLIGLOTAS, UM CONTRATO SÓ Java/Spring Boot · Go · Rust · Python o contrato é o evento e o escopo, não a linguagem Barramento local — 15 Redis · fila em disco PostgreSQL 16 RÉPLICA QUENTE streaming ativo instância separada SSH fechado + pg_receivewal arquiva WAL contínuo recuperação a ponto no tempo leitura, não escrita SUB-REDE DE DADOS · /24 — segmentada da aplicação alcançável só de dentro da rede privada pgbouncer provisionado — ainda NÃO no caminho pool em transaction, pronto para entrar; hoje as apps vão direto ao HAProxy PostgreSQL 16 — PRIMÁRIO host dedicado, fora dos nós de aplicação · 24 bancos isolamento de cliente por Row-Level Security, fail-closed papel de runtime sem BYPASSRLS: esquecer o escopo devolve zero linhas SPOF CONHECIDO o primário é o ponto único. Sem failover automático — de propósito: evita split-brain. replicação contínua → ⑥ backup cifrado fora do provedor SAÍDA CONTROLADA — UMA PORTA SÓ PARA O MUNDO medido: zero SDK de terceiro nas verticais de negócio 7 Integração — 22 conectores adquirente · bureau · governo · open banking assinatura · marketplace · mensageria · armazenamento Disjuntor · retentativa com recuo · idempotência terceiro lento não vira fila presa aqui dentro credencial vem do COFRE, nunca do ambiente Retorno do terceiro: origem SEPARADA para webhooks assinatura HMAC verificada — payload não assinado é descartado o terceiro nunca toca a origem das telas o resultado volta ao ecossistema como FATO normalizado no barramento — o consumidor nunca fala com o terceiro CAMADA TRANSVERSAL — vale para tudo acima SEGREDOS Cofre OpenBao · token por consumidor menor privilégio · MFA em operação crítica alerta dedicado de token expirando OBSERVABILIDADE Prometheus · Grafana · Alertmanager cAdvisor · exporters · sondas externas 9 alvos · 12 regras — 3 vigiam AUSÊNCIA ENTREGA CONTÍNUA ramo → PR → portão local → canário valida no nó drenado · rollback por digest produção nunca compila · trava global de frota CONTINUIDADE dump 3/3h · base semanal · WAL contínuo destino fora do provedor · restore verificado livro-caixa: data, digest e commit de cada serviço O QUE ACONTECE QUANDO FALHA — a parte que um diagrama bonito costuma omitir Cai um nó de aplicação O outro já serve 100% do tráfego. A fila do nó parado está em disco e retoma quando ele volta. Impacto ao usuário: nenhum. Sem ação humana. Cai o banco primário Parada real. A réplica quente existe e está em dia; a promoção é manual, com runbook escrito. Escolha consciente: automatizar sem fencing dá split-brain. Perde-se a região inteira Backup cifrado vive fora do provedor. Nó novo provisionado e frota de pé: 47 minutos, medidos. Perda de dados: segundos, pelo WAL contínuo.
borda gerenciada computação e rede privada dados e continuidade limite de segurança · ponto único de falha assumido
  Por que os pontos únicos de falha estão marcados. Um diagrama que esconde o SPOF não é um diagrama, é um folheto. Os três que existem aqui — os dois nós no mesmo domínio de disponibilidade, o banco primário sem promoção automática e a porta única de banco — são escolhas, e cada uma tem o seu motivo registrado mais abaixo.
A plataforma

Camada por camada

Os 224 containers não são 224 cópias da mesma coisa. Contados por papel, eles desenham a plataforma — e cada camada existe porque alguma dor pediu.

Borda — sem porta aberta para a internet

Nenhum dos dois nós expõe porta pública. O tráfego entra por túnel Cloudflare (conector de saída) e cai no Traefik, que faz roteamento por host, TLS automático e serve de ponto de balanceamento. Drenar um nó do túnel é o que torna a publicação canária possível: é assim que se tira um nó do balanceamento sem derrubar nada.

Dados — pool, proxy e isolamento no banco

PostgreSQL em host dedicado, compartilhado pelas verticais, com HAProxy como ponto único de acesso e pgbouncer provisionado mas ainda fora do caminho (medido: zero cliente ativo; as aplicações vão direto ao HAProxy) — entra quando o número de conexões pedir. São duas instâncias: primário e réplica quente. Isolamento de cliente é Row-Level Security, não WHERE tenant_id espalhado pelo código: o papel de runtime não tem BYPASSRLS, então esquecer o escopo devolve zero linhas em vez de vazar.

Honestidade sobre o alvo: não há failover automático de banco. A promoção é manual e documentada — e eu prefiro dizer isso a desenhar um Patroni que não existe.

Mensageria — 12 relays, 12 consumers, 10 agendadores

Serviços não se chamam em cadeia: publicam fato. O caminho é outbox transacional (o fato commita junto com o dado, ou nenhum dos dois) → relay → Redis Streams → inbox com dedup por grupo de consumo → DLQ para o que falhou. São 14 instâncias de Redis: o barramento é por vertical e por nó, e é justamente por isso que a convergência se resolve no Postgres, não num lock distribuído que não atravessaria os nós.

Segredos — cofre, não arquivo .env

Credenciais vivem em cofre (OpenBao) com token de serviço por consumidor. Há alerta dedicado para token do cofre expirando — porque segredo que vence silenciosamente derruba tudo de uma vez, no pior horário.

Como os serviços conversam — e por que quase nunca é por chamada direta

A regra é publicar fato, não chamar serviço. O caminho padrão é assíncrono: outbox transacional (o fato só existe se o dado existir), relay, fila, inbox com deduplicação por grupo de consumo e DLQ para o que falhou. Isso significa que um serviço fora do ar atrasa o outro — não o derruba junto.

Quando a resposta é necessária agora — “este cliente tem direito a esta funcionalidade?”, “esta empresa é filha daquela?” — a chamada é síncrona, mas nunca é HTTP solto espalhado pelo código: passa por um cliente tipado do SDK único, com operações nomeadas e autenticação máquina-a-máquina por chave própria de cada serviço. A autoridade sobre plano, hierarquia e direito de uso tem um dono só; os outros perguntam, não deduzem.

O mesmo SDK está carregado nas 9 verticais Python — uma implementação de autorização, não nove divergentes

O barramento de eventos — um fato, doze leitores, nenhum acoplamento

Quando uma venda acontece, o serviço de vendas não avisa ninguém. Ele publica o fato — e doze grupos de consumo independentes leem aquilo no ritmo de cada um. Estoque baixa a peça, financeiro gera o recebível, fiscal prepara o documento, contabilidade lança. Nenhum deles sabe que os outros existem, e um deles fora do ar atrasa a própria fila, não a venda.

a escrita e o fato na MESMA transação relay publica stream por domínio 12 grupos de consumo inbox descarta repetido DLQ para o que falhou
10.003fatos no stream da plataforma
12grupos de consumo independentes
242consumidores registrados
0pendentes — o barramento está em dia
5.362marcas de deduplicação ativas
20na fila de falhas, sob vigilância

  Trabalhadores com limite escrito

Por nó: 13 trabalhadores, 11 publicadores, 10 consumidores e 10 agendadores. Cada um roda com concorrência 1, declarada no arquivo de composição. O padrão da ferramenta é 8 — e foi assim que a frota caiu uma vez. Limite que não está escrito não existe.

  Filas separadas por natureza

Trabalho de IA, de análise e de rotina têm filas próprias. Uma chamada de modelo que demora trinta segundos não pode sentar na frente da fila que confirma uma venda. Separar é o que impede que o lento atrase o urgente.

  Fila em disco: atraso, não perda

O barramento grava em disco. Se um nó cai com trabalho enfileirado, aquele trabalho não evapora — ele espera o nó voltar e retoma. A diferença entre “o cliente esperou” e “o pedido sumiu” é esta linha de configuração.

  O detalhe distribuído que mais me custou entender. O barramento é por nó — cada um tem o seu. Isso significa que trava distribuída via barramento não funciona entre os dois: cada nó veria a própria trava e os dois executariam. Como o banco é o único recurso realmente compartilhado, a convergência se resolve lá, com reivindicação atômica da linha — quem pegar primeiro executa, o outro segue adiante sem bloquear. Não é eleição de líder, e é de propósito: eleição concentra o trabalho num nó e reduz o paralelismo que os dois nós existem para dar.

Observabilidade — e o cuidado de alertar sobre ausência

Prometheus raspando 9 alvos (os dois nós, cAdvisor, exportador do PostgreSQL, Traefik, sondas HTTP), Grafana, Alertmanager, cAdvisor, node e postgres exporter, blackbox sondando rotas de fora para dentro. 12 regras de alerta no ar.

O que eu considero a parte madura não é a quantidade — é o que elas vigiam. Três delas não perguntam “algo quebrou?”, perguntam “alguma coisa deixou de acontecer?”: backup off-site vazio, backup atrasado, backup sem o banco dentro. Um cofre de backup que existe mas está vazio passa por saudável em qualquer verificação de presença — e só se descobre no dia do restore. As outras cobrem queda do PostgreSQL, disco enchendo em cada nó, nó fora do ar, slot de WAL acumulando e container em ciclo de reinício.

9 de 9 alvos respondendo no momento desta medição

“A melhor ferramenta é o planejamento.”

por que cada peça está onde está
III

Por que ela é assim

Toda escolha aqui tem um ganho, um custo que eu pago e uma consequência comercial. Quem só lista o ganho está vendendo, não projetando.

Docker Compose — e não Kubernetes ou k3s
a decisão que mais me perguntam, e a que tem o trade-off mais claro
ver trade-off

O orçamento de computação inteiro são dois nós de 8 vCPU e 30 GB, e eles já rodam 224 containers. Um plano de controle de Kubernetes — servidor de API, etcd, agendador, controladores, rede sobreposta, controlador de entrada — consome CPU e memória do mesmo orçamento, de forma sustentada. E carga sustentada de CPU é exatamente o que já me custou duas suspensões de infraestrutura.

o que eu ganho Um modelo mental que uma pessoa sozinha consegue operar às 3 da manhã. A unidade de implantação é a mesma coisa que roda na minha máquina. Sem rede sobreposta, sem controlador de entrada, sem ciclo de atualização de cluster, sem recursos personalizados para depurar durante um incidente.
o que eu pago por isso Não tenho reagendamento automático entre nós nem escala horizontal automática. Capacidade é decisão humana. Se os dois nós saturarem ao mesmo tempo, não existe nada que cresça sozinho — eu teria de provisionar, e isso leva minutos, não segundos.

Como decisão de negócio: Kubernetes resolve um problema de escala que eu ainda não tenho, cobrando hoje em complexidade operacional e em consumo do recurso mais escasso que eu tenho. Enquanto a carga couber em dois nós, adotá-lo seria pagar adiantado por um seguro contra um risco que não existe — e criar um risco novo e real: uma superfície que eu não consigo sustentar sozinho em plantão. Isso não é rejeitar a ferramenta; é não trocar de ferramenta por moda. No dia em que a conta virar — mais de um operador, ou carga que não cabe em dois nós — a migração está desenhada, e é barata justamente porque tudo já é imagem de container e configuração declarativa.

Túnel de saída — e não balanceador com IP público
a superfície de ataque que deixa de existir
ver trade-off
o que eu ganho Não existe porta de entrada para varrer. O firewall libera apenas SSH; 80 e 443 não constam na regra. Filtro de aplicação, certificado e mitigação de volume ficam na borda gerenciada, antes de qualquer byte tocar o meu nó. E drenar um nó do balanceamento é parar um processo — é isso que torna a publicação canária possível.
o que eu pago por isso Dependência de um terceiro no caminho crítico de entrada: se a borda cair, o sistema fica inalcançável mesmo de pé. É um fornecedor a mais na cadeia, e eu não controlo a janela de manutenção dele.

Como decisão de negócio: um balanceador gerenciado custa por hora e por volume, e ainda me devolve um IP público para defender. O túnel custa zero no plano em uso, elimina a superfície exposta e entrega filtro de aplicação junto. Troquei um risco que eu teria de operar (exposição) por um risco que eu monitoro (disponibilidade de terceiro) — e este segundo tem sonda externa e alerta.

Promoção manual do banco — e não failover automático
a decisão mais incômoda de defender, e a mais correta
ver trade-off
o que eu ganho Impossível acontecer cisão de cérebro. Duas instâncias aceitando escrita ao mesmo tempo, num sistema multi-tenant que movimenta dinheiro, é um estrago que nenhum backup desfaz direito — porque os dois lados ficam “certos”.
o que eu pago por isso O banco primário é ponto único de falha declarado. Se ele cair, há parada real até a promoção, e o tempo depende de um humano atender. Não escondo isso no diagrama: está marcado lá.

Como decisão de negócio: failover automático sem camada de consenso e isolamento do nó defeituoso troca uma parada rara e recuperável por uma corrupção silenciosa e permanente. Para quem guarda dado financeiro de terceiros, essa troca é ruim em qualquer cenário. A mitigação não é automatizar a promoção: é a réplica estar quente e em dia, o procedimento estar escrito, e a recuperação a ponto no tempo existir — tudo medido.

Dois nós ativo-ativo — e não um nó maior
resiliência que se usa todo dia, não que dorme esperando desastre
ver trade-off
o que eu ganho Os dois servem tráfego o tempo todo, então o caminho de resiliência é exercitado a cada requisição — não no dia do desastre. É também o que permite publicar num nó que nenhum usuário está acessando.
o que eu pago por isso Tudo precisa ser seguro em dobro: tarefa agendada roda nos dois, então a proteção contra duplicação vive no banco, não num bloqueio distribuído — que nem funcionaria, porque o barramento é local a cada nó.

Como decisão de negócio: reserva ociosa é custo que só paga no pior dia — e que costuma falhar justamente lá, porque ninguém a exercita. Com os dois ativos, o mesmo dinheiro compra capacidade e resiliência, e a prova de que funciona é o tráfego de ontem. Honestidade sobre o limite: hoje os dois estão no mesmo domínio de disponibilidade, então a falha correlacionada de domínio ainda me derruba. Está marcado no diagrama porque é o próximo passo, não porque eu não enxergo.

Esteira e portão de qualidade próprios
sem executor hospedado — com uma consequência que eu digo em voz alta
ver trade-off
o que eu ganho A suíte roda contra PostgreSQL com isolamento de cliente armado, não contra banco de brinquedo — defeito de segurança multi-tenant aparece no portão, não em produção. Sem fila de executor, sem minuto cobrado, sem segredo de produção num serviço de terceiro.
o que eu pago por isso Não existe rede de segurança remota. Se eu não rodar o portão, ninguém roda — e já descobri que ele podia terminar verde sem ter executado um único teste.

Como decisão de negócio: o valor do portão não está em onde ele executa, está em o que ele reprova. Um executor hospedado rodando a suíte contra um banco que não tem isolamento de cliente dá a sensação de cobertura e deixa passar a classe de defeito que mais machuca num SaaS multi-tenant: vazamento entre clientes. Compensação para o custo que eu assumi: um teste que vigia o próprio portão e falha alto.

Identidade própria e origem única — e não identidade terceirizada
a decisão que elimina uma classe inteira de ataque, em vez de filtrá-la
ver trade-off

As doze verticais não têm endereço público próprio. Todas são montadas por caminho sob uma origem só, e o servidor de identidade — OAuth2 e publicação de chaves — vive nessa mesma origem. A autorização não fica só num portão na frente: ela viaja com o serviço, por um SDK único que todas as verticais carregam e que nega a chamada quando o escopo não está presente.

o que eu ganho Origem única significa sessão num domínio só: sem compartilhamento entre origens, sem cookie de terceiro — que os navegadores estão removendo — e sem a superfície que um portal por subdomínio criaria. E como a autorização está no serviço, não só na borda, quem chegar por dentro da rede também é barrado: não existe “já passei pelo portão, agora sou confiável”.
o que eu pago por isso Identidade é o componente mais caro de operar com responsabilidade: rotação de chaves, expiração, federação. Já me custou cinco horas e meia de login fora do ar — eu havia rotacionado o conjunto de chaves de assinar achando que era o de publicar. Terceirizar teria evitado aquele incidente específico.

Como decisão de negócio: o produto é multi-tenant e vende para empresas que precisam herdar hierarquia de cliente, plano contratado e segregação de funções. Identidade terceirizada cobra por usuário ativo — o custo cresce junto com a base, no exato momento em que a margem deveria melhorar — e empurra a regra de negócio para fora do sistema que é dono dela. Mantendo em casa, o custo é de engenharia (uma vez) em vez de assinatura (sempre), e a regra de quem-vê-o-quê fica onde ela é verdade. A mitigação do custo é operacional: há alerta dedicado para chave e token expirando, e o portão de publicação valida que o emissor de chaves responde antes de devolver o nó ao tráfego.

Quatro linguagens — e um contrato só
poliglota por decisão, não por acidente histórico
ver trade-off

Java com Spring Boot na identidade, na contabilidade e no motor de fluxo. Go nos serviços novos — binário único, sem runtime, sobe rápido e ocupa pouca memória, que é o recurso escasso aqui. Rust em dois lugares precisos: o agente que observa o núcleo do sistema operacional e o motor de cálculo fiscal. Python com Django nas verticais de negócio, onde velocidade de mudança vale mais que microssegundos.

o que eu ganho Cada problema na ferramenta que o resolve melhor, sem reescrever o que já funciona. Como o contrato entre serviços é o evento e o escopo — não a linguagem — trocar a implementação de um serviço não obriga ninguém a mudar. É o que torna a migração gradual possível, em vez de um salto arriscado.
o que eu pago por isso Quatro cadeias de construção, quatro conjuntos de dependências para manter atualizados e quatro formas de depurar sob pressão. Para um time pequeno isso é real — por isso cada linguagem está confinada a onde ela paga, e não espalhada por gosto.

Como decisão de negócio: reescrever o que funciona é a forma mais cara de não entregar nada. O legado produtivo segue rendendo em Python enquanto o que nasce já nasce em Go, e o contrato estável entre eles é o que permite trocar peça por peça sem parar a operação — sem o risco da reescrita total, que historicamente derruba empresa.

Uma porta só para o mundo — e não SDK de terceiro em cada serviço
22 integrações externas, um único lugar onde elas existem
ver trade-off

Adquirente de cartão, bureau de crédito, APIs de governo, open banking, assinatura eletrônica, marketplace, mensageria — tudo entra e sai por um serviço só. Ele traduz cada fornecedor para uma capacidade normalizada e devolve o resultado ao ecossistema como fato no barramento. Nenhuma vertical de negócio conhece o nome do fornecedor; a regra de negócio fica em quem consome, não no adaptador.

o que eu ganho Trocar de fornecedor é mudar um adaptador, não caçar chamadas em doze serviços. A credencial do terceiro existe em um lugar só e vem do cofre. E o alcance de uma falha externa fica contido: disjuntor, retentativa com recuo e idempotência moram ali — terceiro lento não vira fila presa dentro de casa.
o que eu pago por isso Um salto de rede a mais no caminho, e um serviço que, se cair, derruba todas as integrações de uma vez. É concentração de risco — assumida, e por isso ele é dos mais vigiados.

Como decisão de negócio: integração com terceiro é onde mora o risco contratual — credencial vazada, cobrança indevida, dado de cliente saindo para fora. Concentrar significa que auditar “quem fala com o mundo lá fora, e com qual credencial?” é olhar um serviço, não doze. E isso é verificável, não promessa: medi as seis verticais de negócio e nenhuma tem SDK de terceiro instalado — eles só existem no serviço de integração. Regra que depende de disciplina humana falha calada; esta dá para medir.

Custo como requisito de arquitetura — e não como fatura no fim do mês
a conta que decide o que entra e o que fica de fora
ver trade-off

Operar sozinho com clientes reais impõe uma restrição que empresa grande não tem: cada serviço gerenciado que eu contrato é dinheiro todo mês e uma peça que eu não controlo. Por isso o desenho evita as três fontes clássicas de conta que cresce sem aviso: tráfego de saída cobrado, balanceador por hora e identidade por usuário ativo.

o que eu ganho Saída de dados sem cobrança por volume, porque o backup vai para um armazenamento sem taxa de egresso e a entrada vem por túnel, não por balanceador tarifado. Identidade própria significa custo fixo de engenharia, não assinatura que cresce junto com a base de usuários — exatamente quando a margem deveria melhorar. E a folga é medida: 47% da memória e carga média abaixo de 1 nos dois nós.
o que eu pago por isso Pago em trabalho. Serviço gerenciado é caro em dinheiro e barato em tempo; o meu arranjo é o inverso. Cada peça que eu não contratei é uma peça que eu opero, atualizo e conserto às 3 da manhã. Essa conta só fecha enquanto a escala couber em duas máquinas e a operação couber numa pessoa.

Como decisão de negócio: num SaaS, o custo de infraestrutura é o que separa margem de prejuízo quando a base cresce. A pergunta que eu faço antes de adotar qualquer serviço é “este custo cresce com o número de clientes?” — se cresce junto com a receita de forma linear, ele come o ganho de escala que o modelo deveria dar. Dimensionamento, limites de CPU e concorrência declarados e o livro-caixa de publicação existem tanto por confiabilidade quanto por custo: recurso que ninguém mede é recurso que ninguém corta.

E o teste que quase ninguém faz: quanto tempo leva para voltar?

Plano de recuperação que nunca foi cronometrado é intenção, não plano. Estes três números foram medidos de verdade, não estimados.

12 min
para a frota inteira subir e convergir
47 min
para provisionar um nó do zero e ter a frota de pé nele
segundos
de perda máxima de dados — o WAL é arquivado continuamente
  Os 47 minutos são também a prova mais honesta de portabilidade de computação e aplicação: é o tempo de reconstruir a camada de computação e aplicação em qualquer provedor que entregue uma máquina Linux: o que roda são imagens de container, PostgreSQL e Redis, sem serviço proprietário. Não é independência total — borda, registro de imagens e o próprio provedor continuam sendo dependências externas, e trocar cada uma tem o seu próprio custo.

“Nada é publicado à mão — e nada compila no servidor de produção.”

uma regra que nasceu de duas suspensões
IV

Como ela muda sem cair

Publicar é a hora em que um sistema estável fica perigoso. O caminho abaixo existe para que mudar deixe de ser um ato de coragem.

Entrega contínua

Como uma publicação acontece

Nada é publicado à mão e nada compila no servidor de produção. A restrição nasceu de dor real: a infraestrutura anterior foi suspensa duas vezes por carga sustentada de CPU — compilar no host era um dos gatilhos. A arquitetura mudou por causa disso.

1

Constrói fora da produção

A imagem é construída na estação/CI e publicada no registry com a tag do commit. O servidor de produção só executa pull — ele nunca compila.

2

Drena o nó canário do balanceamento

O conector do túnel é parado no nó A. 100% do tráfego vai para o nó B. A publicação acontece num nó que nenhum usuário está acessando.

3

Valida com dado real — e com a pergunta certa

Sem crashloop, emissor de chaves respondendo, e zero migração pendente. Esta última nasceu de um incidente: processo de pé não significa banco em dia.

4

Reprovou? Rollback automático por digest

Volta para o digest anterior no próprio nó e aborta — o nó estável nunca foi tocado. E o conector do túnel é restaurado com verificação: se falhar, grita em stderr com o comando do conserto manual.

5

Aprovou? Volta ao balanceamento e espelha

O nó validado retorna ao túnel e só então o segundo nó recebe a mesma imagem, um serviço por vez. Uma trava global serializa a frota inteira: dois deploys simultâneos já derrubaram a infraestrutura uma vez.

E fica escrito

Cada serviço recriado entra num livro-caixa no nó com data, digest da imagem e o commit. É assim que se responde “o que exatamente está rodando?” sem adivinhar.

A esteira

O portão é local — e é por isso que ele tem dente

A esteira é própria: não há runner hospedado checando o pull request. O GitHub guarda código e serve de registry das imagens; o portão de qualidade roda antes, na estação. Isso tem uma consequência desconfortável que eu prefiro dizer em voz alta: não existe rede de segurança remota — se eu não rodar o portão, ninguém roda.

  O que o portão exige

Formatação e lint com catraca (o piso sobe, nunca desce), verificação de integridade do projeto e a suíte rodando em PostgreSQL com RLS armado — não em banco de brinquedo. Cobertura abaixo do piso reprova. No serviço em Java, o mesmo portão soma testes de arquitetura, integração com containers reais e análise estática.

  Nada entra direto na linha principal

Trabalho em ramo, publicação por pull request, e a linha principal é protegida. push --force, reset --hard e apagar a principal são operações bloqueadas por política. Antes de refatoração grande, uma marca de retorno.

  Portão verde que nunca começou

O alvo do portão chamava o interpretador do sistema e morria logo depois de o lint imprimir “tudo certo”. Quem lesse a tela via verde num portão que não havia executado um único teste. Hoje um teste vigia o próprio portão — e ele falha alto.

“Sistema que nunca quebrou é sistema que ninguém usa.”

três incidentes reais, e a cicatriz de cada um
V

Quando quebrou — e o que ficou

A parte que normalmente não se publica. Causa, o que falhou na detecção, o que eu piorei tentando consertar, e a guarda que impede a reincidência.

Confiabilidade

Incidentes, e o que mudou depois

Sistema que nunca quebrou é sistema que ninguém usa. O que diferencia não é a ausência de incidente — é o que sobra depois dele. Estes três são reais, do mesmo dia, e cada um deixou uma guarda no código.

Produção caiu

A migração que travou contra si mesma

Sintoma
Toda tela que lia vendas respondia erro: coluna inexistente. A publicação tinha sido reportada como completa.
Causa
O serviço usa dois papéis de banco — um dono, que executa DDL, e um de runtime, com RLS. Uma migração criou a coluna na transação do dono e, na linha seguinte, atualizou os dados pelo papel errado: a segunda conexão não enxergava a coluna e ficava esperando o bloqueio que a própria migração segurava. Auto-deadlock — e o PostgreSQL nem acusa, porque a sessão dona não espera bloqueio, espera o cliente.
Por que passou
A suíte roda com os dois papéis colapsados num só. O defeito só existe em produção. E a validação do deploy media “o processo subiu”, não “o banco acompanhou”.
Eu piorei
Minha primeira tentativa de aplicar a migração à mão morreu por timeout e deixou a sessão idle in transaction ainda segurando o bloqueio. Empilhei mais duas tentativas em cima antes de entender.
Resolução
Encerrar as sessões presas, reverter puxando a imagem anterior do registry e recriar um nó por vez. Depois: reproduzir o defeito num banco descartável — a versão quebrada pendura, a corrigida aplica — antes de voltar a publicar.
Ficou
Guarda estrutural por AST na suíte (migração de dados tem de usar a conexão da migração) e contagem de migração pendente no deploy, que aborta e reverte.

A guarda nasceu com o mesmo vício que foi caçar: contava só a pendência, e grep -c em saída vazia imprime zero — igual a “nenhuma pendente”. Hoje ela conta o total também: lista vazia não é schema em dia, é falta de evidência.

Falha silenciosa · dinheiro

A divergência de cartão que era engolida

Sintoma
Nenhum. É esse o problema.
Causa
A conciliação diária compara as transações do adquirente com os pagamentos registrados. Cada divergência publica um evento — mas o tipo do evento nunca foi declarado no catálogo, e o publicador recusa tipo não declarado. A exceção caía num except largo que envolvia o cliente inteiro: toda divergência era detectada e descartada. Transação fantasma no adquirente, ou pagamento sem confirmação — dinheiro — virava uma linha de log.
Como apareceu
Não apareceu: foi uma guarda nova que varre o código em busca de evento publicado fora do catálogo. Ela achou dois — este e outro.
O segundo defeito
Declarar o evento, sozinho, ia soltar um alerta falso por venda de cartão: o adaptador em produção não sabe listar transações do adquirente, e o código tratava “não consegui ler” como “o adquirente não tem nada”.
Ficou
Sem leitura, a rotina não acusa nada e reporta explicitamente que não houve evidência. Lista vazia vinda de leitura bem-sucedida continua valendo como divergência real.

Ausência de medição não é ausência do fato. E alerta que grita sempre cega o guardião — teria sido pior que o defeito original.

Integração morta no meio

O evento que ninguém escutava

Sintoma
Mercadoria devolvida ao fornecedor saía do estoque corretamente — e a dívida com ele continuava integral. A loja pagava por peça que devolveu.
Causa
O serviço de estoque publicava o evento da devolução, com a documentação dizendo literalmente que o documento financeiro “vive no outro serviço”. Nenhum serviço consumia. Suíte verde nas duas pontas, funcionalidade morta no meio.
E não dava para consumir
O evento identificava o fornecedor pelo identificador local de quem publicava. Do outro lado, esse identificador não significa nada — e o resolvedor cria um fornecedor genérico quando não reconhece a referência. Consumir como estava criaria crédito pendurado num fornecedor fantasma.
Ficou
O evento passou a carregar a identidade que atravessa a fronteira (cadastro central ou documento fiscal). Sem ela, não é publicado — e a tela avisa ao operador, pelo mesmo predicado, que o crédito não foi gerado e o que fazer.

Ao medir uma integração, não pare em “o evento é publicado”. Pergunte quem escuta — e se o que ele carrega significa alguma coisa do outro lado.

Qualidade por construção

Guardas como código

Todo incidente acima virou um teste que reprova o build se a classe do defeito voltar. Convenção escrita em documento envelhece; guarda executável, não. E toda guarda é testada com um mutante: se eu reintroduzo o defeito e ela continua verde, ela não serve.

  Uma pergunta, um dono

“Este depósito conta como saldo vendável?” estava escrita, literal, em cinco lugares. Com um segundo tipo de depósito, cada cópia esquecida vira um buraco silencioso. A regra passou a ter dono único — e um teste reprova quem a escrever à mão.

  Evento publicado está no catálogo

Varredura por AST: todo tipo de evento publicado no código tem de estar declarado. Achou dois casos vivos — um deles engolia divergência de cartão.

  Migração escreve pela conexão certa

Guarda estrutural, não comportamental: em teste os dois papéis de banco colapsam, então o defeito só existe em produção. Teste de comportamento ficaria verde com o sistema no chão.

  Link simbólico não escapa da árvore

Dois links absolutos apontando para a máquina de quem os criou foram parar dentro da imagem de produção — e eram autorreferentes: recursão infinita para qualquer varredura que os siga.

Cultura SRE

Não é o cargo — é o que a prática obriga

SRE, aqui, não é um título: é um conjunto de regras que já me custaram caro para aprender e hoje estão escritas em script, em teste e em política. Seis delas:

  Post-mortem sem culpado — inclusive de mim

O relato do primeiro incidente desta página tem um campo chamado “Eu piorei”: minha tentativa de conserto manual empilhou três sessões travadas antes de eu entender o problema. Post-mortem que omite o erro do autor não serve para ninguém aprender — serve para proteger o autor.

  Restaurar primeiro, entender depois

Diante de produção no chão, o caminho é voltar ao digest anterior e só então investigar. O rollback é automático e por digest — não por “reconstruir a imagem de antes”, que não é a mesma coisa e já me enganou.

  Alerta que grita sempre cega o guardião

Um alerta disparado por total acumulado fica aceso para sempre e passa a ser ignorado — então a morte nova chega sem ninguém ver. Alerta tem de medir a mudança, e silenciar quando a mudança para.

  A restrição vem da dor, não do manual

A infraestrutura anterior foi suspensa duas vezes por carga sustentada de CPU, e compilar no servidor era um dos gatilhos; a terceira suspensão seria definitiva. Hoje “produção nunca compila” não é preferência de estilo — é regra com revisão que bloqueia o pull request.

  Mudança serializada, nunca em paralelo

Dois deploys simultâneos já derrubaram a frota. Existe uma trava global: um serviço por vez, um nó por vez, e a trava órfã é reclamada em vez de travar a frota para sempre.

  Toda mudança deixa rastro consultável

Cada recriação de serviço entra num livro-caixa no nó: data, digest e commit. A pergunta “o que exatamente está rodando?” tem resposta por consulta, não por memória de quem publicou. Foi daí que saiu a média medida de 87 recriações de serviço por dia (mediana 79, variando de 2 a 172).

Como eu trabalho

Três princípios que vieram de cicatriz

Medir antes de afirmar

Já relatei “é configuração do operador” sobre um campo que era impossível de configurar — o tipo não cabia o identificador do outro serviço. Zero de doze registros preenchidos na frota inteira não era negligência: era um defeito. Hoje eu meço antes de concluir.

Falta de evidência não é ausência de defeito

Uma validação que não conseguiu medir não pode reportar “tudo certo”. Essa confusão já apareceu três vezes: no deploy, na conciliação de cartão e numa guarda minha — sempre com cara de verde.

Gate verde não é funcionalidade funcionando

Suíte com milhares de testes verdes convivia com a tela principal quebrada. Teste com dublê não mede a regra que só existe na borda real — e verificação visual pega o que teste nenhum pega.

  Sobre esta página. Os incidentes são reais e os números foram medidos na frota. Nomes de clientes, endereços de rede e identificadores internos foram omitidos de propósito — é informação de terceiros e superfície de ataque. Quem quiser aprofundar, eu demonstro ao vivo.

“O que eu ainda não testei vale tanto quanto o que eu testei — desde que eu diga qual é qual.”

o estado honesto do que falta
VI

Capacidade, continuidade e governança

O que está feito, o que está parcial e o que ainda não existe. Separar os três é o que torna as duas primeiras colunas confiáveis.

Engenharia de capacidade

feitoLimites de CPU, memória e concorrência declarados por serviço no arquivo de composição — não no padrão da ferramenta
feitoFolga medida: 47% da memória usada e carga média abaixo de 1 nos dois nós
feitoFilas segregadas por natureza, para que o trabalho lento não atrase o urgente
parcialPercentis de latência: medidos. p50 50 ms · p95 96 ms · p99 100 ms, do histograma do roteador de borda, com zero resposta 5xx no período. A ressalva que importa: isso foi a 0,43 req/s — prova caminho saudável, não resistência. O teste de stress, empurrando até saturar para achar o joelho da curva, continua não feito
parcialN-1 exercitado na prática, dezenas de vezes por dia. A publicação canária drena um nó do balanceamento por ~200 s (mediana) a cada serviço: foram 82 janelas em 72 h. Durante elas o nó remanescente sustentou todo o tráfego com carga entre 0,13 e 2,5 em 8 vCPU e zero resposta 5xx. A ressalva: é N-1 sob tráfego normal, não sob pico — a prova sob carga máxima é o próximo exercício

Engenharia de segurança

feitoSuperfície de entrada eliminada por desenho: firewall só com SSH, sem porta de aplicação aberta
feitoIdentidade própria, autorização por escopo em cada serviço e segredos em cofre com token por consumidor
feitoIsolamento de cliente aplicado no banco, fail-closed — não depende do código da aplicação acertar
parcialRotação de credenciais: existe alerta de expiração e procedimento, mas a rotação não é automática — e já me custou um apagão de login
nãoModelagem de ameaças formal, inventário de dependências assinado e gestão contínua de vulnerabilidade. Há atualização de dependência e revisão, não um processo com artefato

Continuidade e recuperação

feitoBackup cifrado fora do provedor, em três cadências: dump de 3 em 3 h, base semanal e WAL contínuo
feitoRestauração já exercitada, e alerta que vigia ausência de backup — não só falha de backup
feitoRTO cronometrado e documentado: 12 min para a frota, 47 min para um nó novo do zero — setembro de 2026, no provedor atual, com critério de ponta a ponta (autenticar e entrar no sistema). O que falta para virar evidência de portabilidade: repetir em outro provedor
nãoExercício de desastre com perda real do primário, cronometrando a promoção manual de ponta a ponta. O RPO de hoje é amostra de atraso de replicação, não resultado de falha provocada

Governança

feitoLinha principal protegida, trabalho em ramo e publicação por revisão; operações destrutivas bloqueadas por política
feitoLivro-caixa de publicação: data, digest e commit de cada recriação de serviço
feitoDecisões de arquitetura registradas com o custo que cada uma impõe — é o Ato III desta página
parcialO portão de qualidade roda na estação, por execução manual. Ele é rigoroso (banco real, isolamento armado, cobertura que reprova), mas não é esteira automatizada de ponta a ponta: sem execução remota por revisão, a rede de segurança depende de mim lembrar
  Por que publicar a coluna “não”. Porque a alternativa é pior. Quem avalia este tipo de sistema vai perguntar exatamente por teste de carga, N-1 e exercício de desastre — e descobrir a lacuna na conversa vale menos que encontrá-la escrita aqui, com a razão e o próximo passo. Lacuna declarada é plano; lacuna descoberta é surpresa.

Quer ver rodando?

Posso demonstrar uma publicação canária de ponta a ponta — incluindo o rollback automático quando a validação reprova.

contato@magnosilva.com LinkedIn Voltar ao início