System design é fundamentalmente sobre decisões
com trade-offs — não há resposta certa única.
As cinco decisões abaixo aparecem repetidamente
em problemas de design diferentes e são as que
entrevistadores exploram para avaliar profundidade
de entendimento.
SQL vs NoSQL — como escolher para um sistema novo?
A pergunta não é "qual é melhor" — é "quais
são os padrões de acesso e os requisitos de
consistência?". SQL (Postgres, MySQL) é a
escolha padrão quando você precisa de
transações ACID, schema bem definido, e
queries ad-hoc. NoSQL faz sentido quando
o padrão de acesso é por chave primária
(DynamoDB), você precisa de schema flexível
com evolução rápida (MongoDB), ou precisa
de escrita de alta throughput com consistência
eventual (Cassandra). O erro mais comum é
escolher NoSQL por escala imaginária: Postgres
com sharding adequado suporta dezenas de
milhões de registros sem problema. Para a
maioria dos sistemas, SQL até 10M req/dia;
NoSQL quando o padrão de acesso não se
encaixa em relacional ou a escala exige
sharding automático.
Cache aside vs read-through vs write-through — qual estratégia de cache?
Cache aside (lazy loading): a aplicação verifica
o cache, em miss lê do banco e popula o cache.
Simples, mas o primeiro acesso sempre é lento
e há risco de cache stampede (muitos processos
vão ao banco simultaneamente após expiração).
Read-through: o cache busca do banco em miss
automaticamente — simplifica a aplicação mas
requer cache que suporte lógica de busca.
Write-through: escreve no cache e no banco
simultaneamente — dados sempre atualizados,
mas aumenta latência de escrita. Write-behind
(write-back): escreve só no cache, persiste
no banco de forma assíncrona — throughput de
escrita alto, mas risco de perda se o cache
cair antes da persistência. A escolha depende
da tolerância a stale data e ao risco de perda.
Sincronização síncrona vs assíncrona entre serviços — quando cada uma?
Chamadas síncronas (REST, gRPC) criam acoplamento
temporal: se o serviço downstream está lento
ou indisponível, o upstream fica bloqueado.
Isso cria cascata de falhas. Comunicação
assíncrona via filas (RabbitMQ, Kafka) desacopla
produtor de consumidor — o produtor não espera
o processamento, a fila garante entrega.
A decisão não é uma coisa ou outra: é qual
operação precisa de resposta imediata para
poder continuar. Criar pedido → confirmar
pagamento é síncrono (não posso confirmar
sem saber se o pagamento foi aprovado).
Enviar email de confirmação → é assíncrono
(não preciso esperar o email chegar para
mostrar "pedido confirmado" ao usuário). A
heurística: síncrono para decisões, assíncrono
para efeitos colaterais.
Horizontal scaling vs vertical scaling — como pensar a decisão?
Vertical scaling (máquinas maiores) é mais
simples: sem mudança de código, sem problema
de estado distribuído. O limite é o maior
servidor disponível — e custo sobe
não-linearmente. Horizontal scaling (mais
máquinas) escala sem limite teórico, mas
requer que os serviços sejam stateless (ou
que o estado seja externalizado para Redis/banco).
A estratégia prática: começar vertical até
onde fizer sentido economicamente, então
horizontalizar. O erro comum em system design
é partir para sharding horizontal sem justificar
que a escala realmente exige. Para bancos de
dados: leitura escala bem com réplicas de
leitura (sem sharding); escrita escala só
com sharding. Read replicas são a primeira
opção, não a última.
Consistência vs disponibilidade — como o teorema CAP se manifesta em decisões reais?
CAP (Brewer, 2000) diz que num sistema
distribuído com partição de rede, você escolhe
entre consistência (todos os nós veem o mesmo
dado) e disponibilidade (o sistema sempre
responde). A formulação mais útil para design
é PACELC: sem partição, você escolhe entre
latência e consistência. DynamoDB e Cassandra
escolhem disponibilidade + latência: respondem
sempre, possivelmente com dado stale. HBase
e Zookeeper escolhem consistência: param de
responder em vez de retornar dado errado.
Postgres com replicação síncrona escolhe
consistência; com assíncrona, escolhe
disponibilidade. A pergunta de design é: "o
que é pior para o usuário — receber dado de
ontem, ou não receber resposta nenhuma?" A
resposta determina o ponto no espectro de
consistência.