Erros Comuns em System Design: 10 Falhas que Você Deve Evitar

Erros Comuns em System Design: 10 Falhas que Você Deve Evitar

“`html




Erros comuns em System Design que você deve evitar



System Design
— erros que custam caro

Erros comuns em System Design que você deve evitar

Se você já teve arquitetura “bonita” que falhou em produção, provavelmente não foi falta de tecnologia:
foi falta de decisões explícitas sobre confiabilidade, consistência,
capacidade e observabilidade.

1 Requisitos vagos
2 Consistência sem estratégia
3 Escalabilidade “por sorte”
4 Operação esquecida


1) Definir requisitos de forma vaga (ou só funcional)

Um dos maiores desperdícios em system design é começar a desenhar componentes antes de transformar
“precisa funcionar” em métricas testáveis.

  • Tempo de resposta sem SLA/SLO (ex.: “rápido”) — em produção isso vira
    discussão interminável e nenhuma ação concreta.
  • Capacidade sem números (ex.: picos, sazonalidade, tamanho médio e p95) —
    você só percebe quando a fila cresce e o custo explode.
  • Disponibilidade sem entender impacto (ex.: “99,9% é ok”) —
    precisa saber o que quebra, o que pode atrasar e o que não pode falhar.

Regra prática

Antes de escolher banco, fila ou cache, feche: tráfego, latência alvo,
taxa de erro tolerável, consistência necessária e
comportamento em falha.

2) Tratar consistência e concorrência como detalhe

Em sistemas distribuídos, consistência é uma decisão de produto e engenharia — não um “depois a gente ajusta”.
O erro comum é assumir que “o banco resolve”, ignorando concorrência, reprocessamento e bordas de integridade.

  • Não definir idempotência em fluxos com retries — mensagens ou requisições reprocessadas
    podem gerar cobranças duplicadas, updates duplicados ou inventário inconsistente.
  • Escolher o modelo de consistência tarde — e quando percebe, já espalhou suposições por serviços.
  • Atualizações concorrentes sem controle (ex.: sobrescrita “por último que chega”)
    — sem versão, sem locks lógicos ou sem invariantes, você cria bugs difíceis de reproduzir.
  • Usar transações locais como “garantia global” — transação em um banco não protege
    consistência em múltiplos recursos/serviços.

O que eu busco no desenho

Uma estratégia explícita para: deduplicação, ordem (quando importa),
reprocessamento, garantia de integridade e como lidar com
parcialidade.

3) Escalar sem um plano: “vamos adicionar instâncias”

Escalabilidade não é um botão. É um conjunto de escolhas sobre gargalos: CPU, memória, rede,
armazenamento, concorrência e coordenação.

  • Ignorar hot partitions e sharding — o sistema parece escalar até que um conjunto de chaves
    concentra a maior parte do tráfego.
  • Subestimar o custo de sincronização — operações que exigem coordenação (ou locks distribuídos)
    podem destruir throughput.
  • Usar filas sem pensar em backlog — se a produção cresce mais rápido que o consumo,
    você transforma latência em um problema estrutural.
  • Não medir p95/p99 — média esconde caudas; caudas ditam a experiência do usuário.
  • Cache sem estratégia — cache “por padrão” vira inconsistência e invalidação mal resolvida.

Como evitar

Identifique primeiro o gargalo provável, depois defina mecanismos: backpressure,
rate limiting, particionamento, capacidade de consumo e
tratamento de cauda.

4) Esquecer operação: observabilidade e recuperação

Um design que não foi pensado para operar falha no dia em que algo inesperado acontece.
E, quase sempre, “algo inesperado” vira “silêncio” (erros sem rastreio) ou “efeito cascata”.

  • Falta de logs estruturados e correlação (request-id / trace-id) —
    você não encontra a causa raiz.
  • Alertas sem critério — notificação demais vira ruído; notificação de menos vira incêndio tardio.
  • Sem plano de falha — o sistema deve saber quando degradar, pausar, atrasar ou recusar.
  • Reprocessamento perigoso — reter mensagens e reprocessar sem idempotência amplifica o problema.
  • Backups/restore sem testes — “tem backup” não é um plano; o plano é “restaurar e validar em X minutos”.

Anti-padrão

Não espere o primeiro incidente para descobrir métricas faltantes. O design precisa nascer com
telemetria e mecanismos de recuperação.

Exemplo prático: idempotência para evitar efeitos duplicados

Quando você tem retries (HTTP, filas, eventos), a forma mais comum de “quebrar” é processar a mesma ação duas vezes.
Uma abordagem simples é usar uma chave de idempotência registrada antes de aplicar efeitos.

-- Tabela (exemplo conceitual)
-- idempotency_keys: (idempotency_key, processed_at, result_hash, status)
--
-- Fluxo:
-- 1) Recebe idempotency_key no request
-- 2) Tenta "reservar" a chave com INSERT que falha se existir
-- 3) Se já existe, retorna o resultado anterior (ou status)
--
-- Em SQL (pseudo): 
BEGIN;

-- tenta registrar a chave
INSERT INTO idempotency_keys (idempotency_key, processed_at, status)
VALUES (:key, NOW(), 'PROCESSING');

-- se falhar por duplicidade:
-- SELECT status, result_hash FROM idempotency_keys WHERE idempotency_key = :key;

COMMIT;

-- aplica o efeito real...
-- UPDATE idempotency_keys SET status='DONE', result_hash=:hash WHERE idempotency_key=:key;

O ponto não é “qual banco” e sim o contrato: repetição não deve mudar o resultado.
Isso reduz cascata de bugs quando a malha de mensagens ou o cliente reenvia por engano.

Quer elevar seu nível em arquitetura?

Eu recomendo continuar com os próximos conteúdos do yurideveloper.com para você construir um repertório
consistente: desde decisões de modelagem até trade-offs de escalabilidade e operação.

Dica: pegue este checklist e aplique no seu próximo desenho — se você não consegue justificar as escolhas com métricas,
consistência e comportamento em falha, ainda é rascunho.



“`