Quando a promessa do nome não é validada na entrada.
Quatro casos onde o sistema declara um pré-requisito mas o esquece de checar. Cada um vira fogo no chão dos componentes downstream — que nem sabiam ter sido eleitos guardiões.
- 01
Rota protegida sem porteiro
A pasta se chama `(protected)`. O nome promete sessão autenticada. Mas o arquivo `page.tsx` renderiza sem checar cookie, dispara fetch que volta 403, e cada componente downstream herda a obrigação de defender o que o portão devia ter recusado.
- 02
Função pública que confia no chamador
Assinatura diz `(user: User) => void`, mas em runtime aceita `undefined` porque algum call site passa o resultado de um `find()` sem checar. O TS mente — a peneira é só visual.
- 03
Prop required no tipo, opcional no runtime
`pocketType: { key: string; value: string }` no tipo. O componente lê `pocketType.value` sem hesitar. Em alguma rota patológica, o pai renderiza com `pocketType={undefined}` — e o React explode em produção sem aviso.
- 04
Migration que assume FK existente
O DBA escreveu `ALTER TABLE orders ADD CONSTRAINT fk_account FOREIGN KEY (account_id) REFERENCES accounts(id)` sem rodar antes `SELECT ... WHERE account_id NOT IN (SELECT id FROM accounts)`. A linha quebra no deploy às 3 da manhã.
As patrulhas mal pagas que nascem da peneira faltante.
Quatro anti-padrões que aparecem quando o portão falhou e cada cômodo tenta proteger sozinho. Parecem cuidado. São silêncio. Defendem a estética, não a verdade do dado.
- 01
Optional chaining como tapete
`a?.b?.c?.d` em toda leitura, sem nunca perguntar por que `a` poderia ser undefined. A tela não quebra mas mostra dado errado em silêncio. O usuário acha que clicou no botão certo. O ferreiro não percebeu que a lâmina amassou.
- 02
Fallback fictício
Quando dado falta, retornar `{ key: "", value: "" }` ou `0` ou `[]` — um valor que parece neutro mas envenena cálculo abaixo. Total vira R$ 0,00 mentindo. Lista vazia esconde erro de carregamento. Default fictício é impureza com gravata de robustez.
- 03
try/catch que engole
`} catch (e) { console.log(e); }`. O erro vira ruído no console e a função retorna como se nada tivesse acontecido. A UI segue. O bug some. Quem investigar daqui a três meses não vai achar a origem — porque ela foi enterrada no momento que aconteceu.
- 04
Estado inicial mentiroso
`useState({ name: "", orders: [] })` num componente que precisa do servidor pra ter sentido. O primeiro render mostra "0 pedidos" e o usuário acha que comprou nada. O dado real chega 200ms depois e a UI pisca. O default mentiu durante 200ms — e em conexões ruins, durante muito mais.
Quatro andares que aceitam o porteiro.
A defesa de contrato tem domicílio. Não é todo lugar — é o primeiro ponto da cadeia que pode dizer não com autoridade. Saber qual dispensa todo o código abaixo de checagem redundante.
- 01
O middleware do framework
Next, Remix, SvelteKit, Rails — todos têm um lugar antes do render onde dá pra cortar a requisição. `if (!session) redirect("/entrar")` em uma linha resolve o que cinquenta `?.` espalhados nunca resolveriam. O portão é o lugar mais barato pra bater o martelo.
- 02
Schema na borda do dado externo
Toda resposta de API, todo payload de webhook, todo arquivo lido do disco — passa por `schema.parse(input)` antes de virar tipo do domínio. Zod, Yup, ou um parser feito à mão. O contrato fica no parser. Depois disso, o tipo é verdade — não promessa.
- 03
Newtype que recusa criação inválida
`type Email = string & { __email: never }` com `function asEmail(s: string): Email | null`. Se o construtor recusou, o resto do programa não vê o caso inválido. A defesa fica no construtor — não em cada função que recebe um Email. Dá pra fazer o mesmo com OrderId, UserId, qualquer coisa cujo formato importa.
- 04
Gate antes do import dinâmico
Feature flag, role, ambiente — checar antes de carregar o módulo, não depois. `if (flagOff) return null; const Mod = await import(...)`. Dispensa todo o componente de saber que pode ser desligado. A peneira fica antes da forja, não dentro.
Quatro perguntas antes de cravar a defesa.
O ferreiro que defende em cada cômodo não tem portão.
Quem tem portão raramente precisa defender em cômodo.
- 01
Quem foi o primeiro a tocar nesse dado?
Suba a stack. Rastreia até a origem — fetch, parse de URL, leitura de cookie, deserialização de localStorage. Se o ponto de entrada não validou, todo mundo abaixo tá patrulhando o que devia ser garantido.
- 02
Quem deveria tê-lo recusado?
O middleware? O parser? O type system? O construtor? Pelo menos um desses tinha jurisdição pra dizer não. Identificar qual e mover a defesa pra lá. O resto do código relaxa.
- 03
Se eu apagar todos os ?. deste arquivo, onde a tela explode?
Experimento mental brutal. Cada explosão é um contrato esquecido lá em cima. Cada `?.` que sobreviver é uma cobertura legítima de caso opcional. Os outros são ruído defensivo herdado.
- 04
A peneira atual fica em qual andar?
Mapeie a cadeia: portão, parser, tipo, componente. A peneira efetiva é a primeira que recusa entrada inválida — todas as outras são redundância (boa ou má, depende). Se a peneira tá no quinto andar e a impureza entrou no térreo, sobrou poeira em todos os cômodos do meio.
A peneira mais barata é a primeira da cadeia.