Abri o checkout e o BFF de pagamento devolveu HTTP 200. No botão, TypeError. Seller e comprador falhavam sem stack, sem release, sem rota. Infra já tinha métrica. O navegador, onde o dinheiro vive, era ponto cego.
Eu contei da ordem de ~900 catch vazios ou logs que engoliam a exceção. Quatro fronts. Sem dono. Se eu ligasse o SDK no default do wizard, ia queimar quota e ainda assim não ter golden signal.
A conta foi grep no repo, não runtime. Se você rodar as duas buscas aí, já vê a classe. Não é o regex que fechou o ~900:
rg -n --glob '*.{js,ts,tsx,vue}' 'catch\s*\([^)]*\)\s*\{\s*\}'
rg -n --glob '*.{js,ts,tsx,vue}' 'catch\s*\([^)]*\)\s*\{\s*console\.(log|error|warn)'
HTTP 200 não é o sinal do usuário
O servidor pode estar verde e o clique morto. Métrica de API responde tráfego e saturação. Ela não vê o TypeError no onClick do pagar. Eu tratei isso como gap de receita, não como "mais uma ferramenta".
Quatro repos, squads distintas. LGPD: e-mail, documento e token não podem ir pro vendor. Calendário de checkout não parava pra um projeto de observabilidade. A premissa era complementar Grafana e APM, não substituir.
Empty catch não é sinal
catch vazio é o oposto de telemetria. A exceção existe, o usuário sente, o time descobre no ticket dias depois. Sem tag de domínio, erro de PIX misturava com erro de upload. Sem máscara, qualquer SDK virava risco de PII.
Eu recusei três coisas no mesmo dia: só APM (não vê o botão), captureException nu (sem dono) e sampling 1.0 (custo + ruído). Sem essas recusas, o dashboard mentia com mais volume.
Golden Signals: o browser só responde dois
Google SRE lista quatro sinais pra sistema de usuário: Latency, Traffic, Errors, Saturation. No client, o pedaço que um SDK de erro consegue carregar barato é Errors e um pouco de Latency.
Traffic (RPS, sessão como capacidade) e Saturation (CPU, fila, thread) ficam no backend de métrica. Eu não pedi pro Sentry contar request. Pedi issue com release e vital amostrado (LCP, INP, CLS) na mesma rota do clique. Se o setup promete os quatro sinais num projeto de front, a premissa está errada.
A premissa vem antes do DSN
Antes de colar o DSN, eu escrevi o contrato. Só em produção. Sampling baixo. Tag de domínio obrigatória. PII fora. Source map no release. Um guia, quatro mapas, dono por repo. Sem isso o SDK morre na minha agenda.
Recusei big bang nos quatro fronts no mesmo dia e recusei session replay sempre ligado. O kit público está em sentry-golden-path. Ele prova o padrão. Não prova que estes rates rodaram numa marca.
Target sample: o número que eu recusei
O wizard gosta de tracesSampleRate: 1.0 e replay em 10% das sessões. Isso é demo. Em produção eu comecei abaixo: sampleRate 0.1, tracesSampleRate 0.05, replay de sessão 0, replay no erro 1.0.
sampleRate é evento de erro. tracesSampleRate é span de pageload e navegação. São independentes. Cortar trace não esconde 100% das exceptions. Subir os dois pra 1.0 "pra ver mais" queima quota e ensina o time a mutar alerta.
export const ERROR_SAMPLE_RATE = 0.1;
export const TRACES_SAMPLE_RATE = 0.05;
export const REPLAY_SESSION_SAMPLE_RATE = 0;
export const REPLAY_ON_ERROR_SAMPLE_RATE = 1;
Regra de bolso: se você não consegue escrever por que o número subiu, ele não sobe. O PR que muda a constante é o lugar da premissa, não o Slack depois da fatura.
Tag de domínio é contrato
Issue sem domain e flow é stack com ombro encolhido. O módulo que conhece a jornada marca, depois captura. captureException global é backstop, não o caminho feliz.
Sentry.withScope((scope) => {
scope.setTag("domain", "checkout");
scope.setTag("flow", "pay");
Sentry.captureException(error);
});
Alerta no par domain:checkout flow:pay. Não em "qualquer erro de produção". Sem isso o volume vira ruído e ninguém dona. O guia vale mais que o PR do SDK. SDK sem dono é quota queimada.
Isso aqui não é issue de produção. É o formato que eu cobrei depois que o SDK entrou:
release: checkout@git-a1b2c3
title: TypeError: Cannot read properties of undefined (reading 'pix')
culprit: PayButton.tsx:onClick
tags: domain=checkout flow=pay
Se falta release ou domain, a issue ainda é ticket com stack. Não é sinal.
O irmão de 30 dias
No mesmo tipo de buraco, por outro canal: milhares de sellers (ordem de quatro mil, arredondado) ficaram ~30 dias sem push. HTTP aparentava sucesso. API de terceiro deprecada, client antigo sem header, catch otimista, zero alerta de queda de entrega.
Achei no dashboard, não na fila de ticket. Happy path mente. $response->failed() (ou o equivalente) é o teste de sanidade de toda integração. Fallback gracioso sem alerta é o mesmo silêncio com nome bonito.
O que eu não medi
Contei ~900 catches na estática. Vi quatro projetos adotarem o padrão. PII deixou de ser risco aberto (beforeSend, sendDefaultPii: false). Não publico "60% dos bugs antes do suporte" nem "crash-free 99,5%". Isso é OKR de mercado, não before/after que eu tenha fechado neste texto.
Latência de detecção menor que 24h continua meta. Taxa de incidente de integração cair pra um dígito baixo continua alvo. Se eu estiver errado no sample, o blast é quota + alerta mutado + PII no vendor. Por isso o número começa baixo e só sobe com motivo escrito.
O case está em staff-impact-cases. Números de marca, ticket e TPV ficam fora.
Premissa, target sample, dono. Depois o golden signal. Sem isso, você tem dashboard.
Qual catch vazio ou integração "200" o seu time ainda trata como silêncio?
Top comments (0)