Quando quebrar as regras do banco de dados:3 exceções
Quando quebrar as regras do banco de dados: as três exceções que resistem — JSONB para dados flexíveis, contadores armazenados e dinheiro em centavos.
Quando quebrar as regras do banco de dados? Só quando tiver um motivo específico e documentado — e, na prática, só três motivos sobrevivem a um exame sério. Dados realmente flexíveis podem morar em JSONB em vez de tabelas rígidas. Contadores caros demais para calcular na hora podem ser armazenados. E dinheiro é sempre guardado em centavos inteiros. Todo o resto continua rigorosamente estruturado.
Já gastei muitas palavras pregando estrutura: cada fato num só lugar, tudo conectado por referências, nenhum atalho. Essa regra — a disciplina de mais de 90% descrita em normalização de banco de dados explicada — construiu uma plataforma em que centenas de tabelas ficam sincronizadas sem nenhum dado duplicado. Este post é sobre o outro lado: os 10% em que quebrar as regras não é atalho, e sim a decisão de engenharia correta.
Quando quebrar as regras do banco de dados: a regra primeiro
Primeiro, o enquadramento. Na minha plataforma, provavelmente 95% dos dados são relacionais — tabelas certas, referências certas, estrutura certa. As exceções são exatamente isso: exceções, cada uma com um motivo por escrito. Essa ordem importa, porque a IA vai oferecer quebras de regra o tempo todo, e “é mais simples” nunca é o motivo. O perigo não é a exceção; é a exceção não documentada, que depois ninguém consegue distinguir de um erro.
Dito isso: aqui estão as três quebras de regra que eu uso de propósito.
Exceção 1: JSONB para dados realmente flexíveis
O PostgreSQL — o motor de banco de dados por trás da maioria dos produtos SaaS modernos — tem um superpoder chamado JSONB. Ele permite que uma coluna guarde um documento inteiro de formato livre: quaisquer campos, qualquer formato, diferente em cada linha. É a saída de emergência do mundo estruturado.
Pense no seu banco de dados como um arquivo com gavetas etiquetadas. O JSONB é a gaveta etiquetada “diversos” — e uma gaveta de diversos é realmente útil, desde que nunca vire o lugar onde você guarda o passaporte. O conteúdo legítimo: coisas cujo formato você não tem como saber de antemão. Se os seus usuários criam formulários personalizados, cada envio tem campos diferentes — não dá para projetar antes uma gaveta para campos que ainda não existem. O mesmo vale para campos personalizados definidos pelo usuário, preferências e configurações por usuário, payloads que chegam de sistemas de outras empresas, metadados soltos e os snapshots de antes/depois dentro dos logs de auditoria.
A regra de uma frase que eu sigo:
Se você precisa conectar aos outros dados, consultar com frequência ou garantir relacionamentos — faça relacional. Se é flexível, definido pelo usuário ou raramente consultado — JSONB serve.
| JSONB é certo para | JSONB é um erro para |
|---|---|
| Respostas de formulários (cada formulário é diferente) | Entidades centrais do negócio: usuários, organizações, projetos |
| Campos personalizados definidos pelo usuário | Qualquer coisa que você consulta ou filtra o tempo todo |
| Preferências e configurações | Dados com relacionamentos com outros registros |
| Payloads recebidos de sistemas externos | Dados cuja correção o schema deveria garantir |
| Snapshots de antes/depois em logs de auditoria | Qualquer coisa que alimenta relatórios e analytics |
O sinal de que o JSONB é seguro: os dados pertencem a uma única linha e nunca são consultados de forma independente dela. É exatamente a situação em que os problemas de atualização que a estrutura existe para evitar não podem acontecer. O sinal de que é um erro: você se pega querendo fazer perguntas através do JSONB — “todas as tarefas marcadas como urgentes”, “todos os pedidos deste cliente”. Isso são relacionamentos, e relacionamentos pertencem a tabelas.
12 semanasA sua carreira corporativa nunca foi o planoCandidate-se à Aceleradora de EUpreendedores.Candidatar-se →Exceção 2: contadores que você não pode se dar ao luxo de calcular na hora
A estrutura rigorosa diz: nunca armazene o que você pode calcular. O número de membros de um canal, a nota média de um anúncio, a quantidade de candidaturas de uma vaga — tudo isso pode ser calculado sob demanda contando os registros de base, e em pequena escala é exatamente o que você deve fazer.
Aí chega a escala. Uma comunidade em que os canais têm 100 mil posts não pode recontá-los a cada renderização de página. Um marketplace com 500 mil anúncios não pode recalcular notas médias dentro de cada resultado de busca. Um site de vagas em que as empresas recebem 50 mil candidaturas não pode contá-las ao vivo a cada visualização de perfil. Nesse ponto você armazena o número — uma coluna de contador pré-calculado — e lê-lo não custa nada.
Esta é a disciplina que impede que isso vire podridão: todo contador armazenado precisa de um mecanismo de manutenção — um trigger ou uma função no banco que o atualiza no instante em que os dados de base mudam. Um contador sem isso não é uma otimização; é uma mentira esperando alguém acreditar nela. No meu schema, cada coluna de contador tem um mecanismo documentado que a mantém sincronizada, e as notas de design registram qual é e quando ele dispara.
Um refinamento que vale copiar: contagens de visualização são a exceção dentro da exceção. Elas são atualizadas de forma assíncrona — disparadas pela página depois que ela carrega — e não por um trigger, então contar visualizações nunca deixa uma página lenta, bots podem ser filtrados e visualizações repetidas podem ser deduplicadas. Contadores exatos (membros, votos, candidaturas) recebem triggers; contadores de vaidade recebem o tratamento relaxado.
Exceção 3: dinheiro é sempre centavos inteiros
Esta é menos “quebrar uma regra” e mais recusar um padrão que parece natural e é silenciosamente perigoso. Guardar R$ 77,94 como o número decimal 77.94 convida a aritmética de ponto flutuante do computador para dentro das suas finanças — e a matemática de ponto flutuante produz pequenos erros de arredondamento que são só feios num gráfico e inaceitáveis numa fatura.
A solução: guarde todo valor monetário como um número inteiro de centavos. R$ 77,94 é guardado como 7794. Números inteiros não têm erros de arredondamento, nunca. Todo o sistema de pagamentos da Stripe fala essa convenção — valores na menor unidade da moeda — então seu banco de dados combina nativamente com o seu provedor de pagamento. A divisão por 100 só acontece no último momento, na camada de exibição, onde um erro custa um bug cosmético em vez de dinheiro.
No meu schema isso é absoluto: todo preço, taxa, gorjeta e total é em centavos inteiros. Nada de decimais para dinheiro, em lugar nenhum, nunca.
A meta-regra: exceções ficam por escrito
Repare no que as três exceções têm em comum. Cada uma tem um gatilho estreito (formato impossível de prever; custo medido do cálculo ao vivo; a natureza do dinheiro). Cada uma tem um mecanismo compensatório (o teste do JOIN; triggers de sincronização; conversão na camada de exibição). E cada uma é documentada — minha plataforma mantém um arquivo de decisões de design listando cada desvio intencional, justamente para que auditorias futuras (humanas ou de IA) consigam distinguir engenharia deliberada de desleixo acumulado.
Essa é a verdadeira diferença entre uma quebra de regra profissional e dívida técnica: uma vem com um motivo por escrito, a outra vem com um dar de ombros.
O livroA Revolução do EUpreendedorLuis Gonçalves sobre como construir um negócio de EUpreendedor com IA sem largar o emprego.Quero o livro →Perguntas frequentes
JSONB é má prática?
Não — JSONB mal usado é má prática. Para dados realmente flexíveis e pertencentes a uma linha, é a ferramenta certa e evita complexidade inútil. O erro é usá-lo para fugir do design do banco: enfiar tags, responsáveis ou relacionamentos num blob JSON porque projetar as tabelas certas parecia trabalhoso. Aplique o teste do JOIN e o JSONB continua sendo uma ferramenta, não uma armadilha.
Como sei quando um contador deve ser armazenado?
Quando a contagem ao vivo prejudica de forma mensurável — uma página que reconta grandes conjuntos de registros a cada renderização — e não antes. Comece calculando as contagens sob demanda; armazene um contador só quando a escala obrigar, e nunca sem o trigger ou a função que o mantém correto. Contadores prematuros são complexidade não conquistada.
Por que não usar simplesmente uma coluna decimal para dinheiro?
Porque centavos inteiros tornam toda uma classe de bugs financeiros impossível, e não só improvável, e porque o seu provedor de pagamento já pensa na menor unidade da moeda. Duas representações de dinheiro — centavos na Stripe, decimais no seu banco — significam conversões por toda parte, e cada conversão é um lugar para errar a fatura de alguém.
O que acontece se eu usar JSONB para tudo?
Você ganha um banco de dados que não consegue responder perguntas. Sem relacionamentos, sem garantia de integridade, sem filtragem eficiente, relatórios dolorosos — você reconstruiu uma pilha de documentos com passos extras. Flexibilidade de que você não precisava, comprada com a capacidade de consulta da qual o seu negócio depende. Mantenha a estrutura para os 95% e use JSONB onde o formato é realmente imprevisível.
Quão preso você está, de verdade?
Faça o Índice de Sufocamento Corporativo, a avaliação gratuita para saber se você está pronto para ser um EUpreendedor.