System Design & Scalability
Material de apoio para entrevistas técnicas — como pensar, comunicar e projetar sistemas escaláveis. Baseado em Cracking the Coding Interview (G. McDowell) e referências de engenharia de software de alto nível, conectado aos tópicos e hands-ons da disciplina.
Sumário
Algorithms vs. System Design
Dois tipos de problema, duas habilidades distintas — mas a mesma lógica de raciocínio estruturado.
Problema fechado
Geralmente têm solução determinística. O foco está na correção do algoritmo, na complexidade O(n) e na implementação limpa. Exemplos: "encontre o caminho mais curto", "verifique se uma árvore é BST".
Problema aberto
Não existe resposta única. O foco está nos tradeoffs, na escalabilidade, na comunicação e no raciocínio de engenharia. Exemplos: "projete o Twitter", "projete um sistema de encurtamento de URLs para 100M de usuários".
"Tackle the problem by doing it just like you would at work."
Faça perguntas. Engaje o entrevistador. Discuta os tradeoffs. Não existe resposta perfeita — existe raciocínio bem articulado e soluções justificadas.
Em system design, o entrevistador quer ver como você pensa, não apenas o que você memorizou. Um candidato que faz boas perguntas, explicita suas assunções e admite incertezas com raciocínio sólido frequentemente supera um candidato que tenta recitar uma arquitetura pronta.
O que o entrevistador avalia
| Aspecto | Algorithm Interview | System Design Interview |
|---|---|---|
| Resposta correta | Determinística, geralmente única | Múltiplas soluções válidas |
| Foco | Correção e complexidade | Tradeoffs e escalabilidade |
| Duração típica | 20–45 min | 45–60 min |
| Comunicação | Importante | Crítica — é a principal avaliação |
| Lousa/diagrama | Útil | Obrigatório |
Handling the Questions
8 princípios para conduzir a entrevista de system design com confiança e clareza.
Dica prática: Use transições explícitas entre etapas. Ao passar de "componentes principais" para "identificar gargalos", diga: "Agora que temos os componentes principais, vou identificar onde estão os pontos críticos desse sistema." Isso mantém o entrevistador orientado na sua estrutura.
Design: Step-By-Step
Estrutura em 5 passos para projetar sistemas complexos de forma organizada durante a entrevista.
Sempre pergunte: "Este sistema é read-heavy ou write-heavy?" — isso muda tudo.
Read-heavy (ex: URL shortener, Twitter timeline) → caching agressivo, DB replicas para leitura.
Write-heavy (ex: log ingestion, chat em tempo real) → filas + processamento assíncrono, eventual consistency.
Algorithms that Scale: Step-By-Step
Quando o problema é algorítmico mas precisa operar em escala massiva — dados que não cabem em uma máquina.
Diferente do system design puro, aqui você começa com um algoritmo correto e vai iterando para que ele funcione com bilhões de registros. A chave é reconhecer onde a solução "quebra" ao escalar e abordar especificamente esses pontos.
Top-K palavras em 1TB de logs
Make believe: HashMap<String, Integer> para contar + MinHeap de tamanho K para os top-K. O(N log K).
Get real: 1TB não cabe em RAM (assume ~10GB de heap).
Solve: Particione o arquivo em N chunks. Cada worker processa um chunk → emite (palavra, count). Redutor central agrega contagens por palavra e mantém MinHeap global de tamanho K.
Alternativa: MapReduce — Map emite (palavra, 1), Reduce soma e cada reducer mantém top-K local, merge final.
Quando usar cada abordagem
MapReduce: dados em batch, paralelizáveis, fits word count, PageRank, join de tabelas grandes.
HyperLogLog: contagem de elementos distintos com ~1% de erro usando O(log log N) memória. Ideal para "quantos usuários únicos visitaram?"
Count-Min Sketch: frequência aproximada de elementos. Ideal para top-K com alta cardinalidade.
Bloom Filter: verificação probabilística de pertencimento. "Este URL já foi visitado?" com zero falsos negativos.
Back-of-the-Envelope
Números de referência para estimativas rápidas — a ordem de grandeza é o que importa, não a precisão.
Latências que todo engenheiro deveria conhecer
| Operação | Latência aprox. | Implicação |
|---|---|---|
| Referência ao cache L1 | ~1 ns | CPU é praticamente "grátis" frente a I/O |
| Acesso à memória principal (RAM) | ~100 ns | Por isso caches em memória (Redis) respondem em sub-ms |
| Ler 1 MB sequencial da RAM | ~5–10 μs | Varrer dados em memória é barato |
| Leitura aleatória em SSD (4 KB) | ~20–100 μs | ~1.000× mais lenta que RAM |
| Round trip no mesmo datacenter | ~0,5 ms | Cada chamada entre microservices custa isso no mínimo |
| Ler 1 MB sequencial do SSD | ~0,2–1 ms | Leitura sequencial é muito mais barata que aleatória |
| Seek em disco rotacional (HDD) | ~5–10 ms | Evite acesso aleatório em HDD no caminho crítico |
| Round trip São Paulo ↔ Virgínia (us-east-1) | ~110–130 ms | Evite chamadas síncronas entre regiões; use CDN/edge |
Disponibilidade — os "noves"
| SLO | Downtime / ano | Downtime / mês |
|---|---|---|
| 99% | ~3,65 dias | ~7,3 h |
| 99,9% | ~8,77 h | ~43,8 min |
| 99,95% | ~4,38 h | ~21,9 min |
| 99,99% | ~52,6 min | ~4,38 min |
| 99,999% | ~5,26 min | ~26,3 s |
Unidades de armazenamento
2¹⁰ ≈ 10³ → KB (mil)
2²⁰ ≈ 10⁶ → MB (milhão)
2³⁰ ≈ 10⁹ → GB (bilhão)
2⁴⁰ ≈ 10¹² → TB (trilhão)
2⁵⁰ ≈ 10¹⁵ → PB
Para estimar storage
ID numérico / timestamp: 8 B
UUID: 16 B (36 B como texto)
Registro "pequeno" (URL, post curto): ~0,5–1 KB
Foto comprimida: ~200 KB – 2 MB
1 min de vídeo HD: ~50–100 MB
Arredonde agressivamente, faça a conta em voz alta e, principalmente, diga o que o número muda na arquitetura. "São ~3 TB em 5 anos — cabe em um banco com réplicas, então sharding é uma otimização futura, não um requisito do dia 1." Estimativa que não leva a uma decisão é só aritmética.
Key Concepts
Os conceitos que aparecem em toda entrevista de system design — clique em cada item para expandir a explicação.
Vertical scaling (scale up): adiciona mais CPU, RAM ou disco na mesma máquina. É simples — não requer mudança de código. Mas tem limite físico e hardware (não existe uma máquina com RAM infinita), cria single point of failure e o custo cresce exponencialmente com a capacidade.
Horizontal scaling (scale out): adiciona mais máquinas. Teoricamente ilimitado e mais econômico em escala. Exige design stateless nos servidores de aplicação, load balancing, e coordenação entre nós. A maioria dos sistemas web de grande escala usa horizontal.
Estratégia prática: comece com vertical (mais simples, zero refactoring), mas projete para horizontal desde o início. Quando vertical atingir custo ou limite físico, migre para horizontal. O erro é não planejar isso desde o início.
| Aspecto | Vertical | Horizontal |
|---|---|---|
| Implementação | Simples | Complexa |
| Limite | Hardware máximo | Praticamente ilimitado |
| Custo | Cresce exponencialmente | Cresce linearmente |
| Single point of failure | Sim | Não (com redundância) |
| Downtime na expansão | Geralmente sim | Não |
Distribui requisições entre múltiplos servidores backend, impedindo que qualquer servidor fique sobrecarregado enquanto outros ficam ociosos. Também executa health checks e remove automaticamente servidores com falha do pool.
Algoritmos de distribuição:
Round-robin:cada servidor recebe requisições em rotação. Simples, mas ignora carga atual.Weighted round-robin:servidores mais potentes recebem proporção maior de requisições.Least connections:encaminha para o servidor com menos conexões ativas. Melhor para requests de duração variável.IP hash:shard = hash(client_IP) % N. Garante sticky sessions (mesmo cliente → mesmo servidor). Útil quando há estado no servidor.
Tipos de implementação: Hardware (F5, Cisco), software (Nginx, HAProxy, Envoy), cloud-managed (AWS ALB/NLB, GCP Cloud Load Balancing).
Cuidado: o load balancer em si pode se tornar single point of failure. Solução: pares ativo/passivo (primary + standby com failover automático) ou DNS-based load balancing (múltiplos IPs no DNS com TTL curto).
Denormalization: desnormalizar um banco relacional significa duplicar dados estrategicamente para evitar JOINs caros. Você troca consistência (updates precisam ser feitos em múltiplos lugares) por performance de leitura. Funciona bem quando leituras superam escritas em grande proporção e JOINs são um gargalo comprovado.
NoSQL — quando e qual usar:
- Key-Value (Redis, DynamoDB): acesso O(1) por chave. Ideal para sessions, caching, leaderboards, rate limiting.
- Document (MongoDB, Couchbase): dados semi-estruturados, schema flexível. Ideal para catálogos de produtos, perfis de usuário.
- Column-family (Cassandra, HBase): escrita massiva distribuída, queries por partition key. Ideal para séries temporais, IoT, logs.
- Graph (Neo4j, Amazon Neptune): relacionamentos complexos entre entidades. Ideal para social graphs, sistemas de recomendação, detecção de fraude.
CAP Theorem: em um sistema distribuído, você pode garantir no máximo 2 das 3 propriedades: Consistency (todos os nós veem os mesmos dados), Availability (toda request recebe resposta), Partition tolerance (o sistema funciona mesmo com falhas de rede). Na prática, P é inevitável — você escolhe entre CP (consistência) ou AP (disponibilidade).
Divide o banco de dados em múltiplos nós (shards), onde cada nó armazena um subconjunto dos dados. Permite escalar horizontalmente o armazenamento e a throughput de queries além dos limites de uma única máquina.
Estratégias de sharding:
- Vertical partitioning: diferentes tabelas em diferentes servidores. Simples, mas limita o crescimento de tabelas individuais.
- Horizontal (range-based): usuários com id 1–1M no shard A, 1M–2M no B. Bom para range queries, mas pode criar hot spots se a distribuição for desigual.
- Hash-based:
shard = hash(user_id) % N. Distribuição uniforme, evita hot spots. Dificulta range queries. Consistent hashing minimiza o remapeamento de dados ao adicionar ou remover nós. - Directory-based: uma lookup table mapeia chave → shard. Flexível (permite mover dados sem rehashing), mas a tabela vira um gargalo e um ponto único de falha.
Problema clássico: queries que cruzam shards (cross-shard joins) são extremamente caras. Projete o sharding key levando em conta os access patterns mais frequentes. Se 90% das queries são por user_id, use user_id como sharding key.
Armazena resultados computados ou dados frequentemente acessados em memória (muito mais rápida que disco) para evitar recomputation ou I/O ao banco. É uma das otimizações de maior impacto em sistemas read-heavy.
Políticas de eviction (quando a memória está cheia):
LRU (Least Recently Used):remove o item acessado há mais tempo. É o padrão — funciona bem na maioria dos workloads.LFU (Least Frequently Used):remove o item acessado com menos frequência. Melhor quando a popularidade dos dados é estável ao longo do tempo.TTL (Time-To-Live):cada item tem um prazo de expiração. Essencial para dados com validade temporal (ex: preços, sessões).
Write strategies:
Write-through:escreve no cache e no banco simultaneamente. Consistência garantida, mas latência de escrita maior.Write-back (write-behind):escreve no cache imediatamente, propaga ao banco de forma assíncrona. Maior throughput, mas risco de perda de dados se o cache falhar.Write-around:escreve direto no banco, bypassa o cache. O cache só é populado na leitura seguinte. Útil quando os dados escritos raramente são lidos logo após.
Problemas clássicos: Cache stampede (expiração simultânea de muitas chaves causa flood de queries ao banco — use probabilistic expiration ou mutex lock), cold start (cache vazio após deploy — use warm-up scripts), cache poisoning (dados inválidos no cache).
Em vez de processar tudo de forma síncrona dentro da request do usuário, você enfileira o trabalho pesado para execução assíncrona. O usuário recebe uma resposta rápida (confirmação de recebimento), e workers processam em background.
Casos de uso clássicos:
- Envio de email/SMS após cadastro
- Geração de thumbnail de imagem após upload
- Processamento de pagamento com retry automático
- Indexação de conteúdo para mecanismo de busca
- Notificações push para dispositivos móveis
Ferramentas e tradeoffs:
RabbitMQ:AMQP, garantia de entrega (at-least-once), roteamento complexo por exchange/binding. Ideal para workloads que precisam de garantias de entrega.Apache Kafka:log distribuído, retenção persistente de mensagens, possibilidade de replay, throughput muito alto (milhões de msg/s). Ideal para event streaming, auditoria, múltiplos consumidores independentes.Amazon SQS:managed, zero ops, SQS Standard (at-least-once, sem ordem) ou FIFO (exactly-once, ordenado). Ideal para quem quer simplicidade na nuvem AWS.
O padrão produtor-consumidor desacopla serviços, absorve picos de tráfego (o produtor pode ser muito mais rápido que o consumidor por curtos períodos) e permite retry automático em caso de falha do worker.
Três métricas fundamentais para entender e dimensionar a capacidade de rede de um sistema distribuído:
Componentes da latência:
Propagation delay:velocidade da luz × distância. São Paulo → Nova York ≈ 100ms mínimo.Transmission delay:tamanho do pacote ÷ bandwidth. Um arquivo de 1GB em link de 100Mbps leva ~80 segundos.Processing delay:tempo que roteadores levam para processar o header e decidir o próximo salto.Queuing delay:tempo esperando em buffers de roteadores congestionados. Variável e imprevisível.
Regra prática: otimizar throughput (batching, compressão) pode aumentar latência. Otimizar latência (requests menores, conexões persistentes) pode reduzir throughput. Entenda qual é mais crítico para o seu sistema antes de otimizar.
Paradigma de programação para processar grandes datasets em paralelo num cluster. O programador define apenas as funções Map e Reduce; o framework cuida de distribuição, paralelismo, fault tolerance e shuffle.
Fluxo das duas fases:
- Map: cada worker lê um chunk dos dados de entrada e emite pares
(key, value). Ex.: para word count, cada linha → emite(palavra, 1)para cada palavra encontrada. - Shuffle & Sort: o framework agrupa automaticamente todos os valores emitidos pela mesma key e os envia para o mesmo reducer.
- Reduce: o reducer recebe
(key, [v1, v2, v3, ...])e produz o resultado final. Ex.:(palavra, [1,1,1]) → (palavra, 3).
O framework cuida de: distribuição de dados entre workers, execução paralela, re-execução automática de tasks que falharam (fault tolerance), shuffle/sort entre Map e Reduce, e monitoramento.
Implementações: Hadoop MapReduce (batch, I/O em disco — robusto mas lento), Apache Spark (in-memory — 10-100x mais rápido para iterative algorithms como ML, streaming via Spark Streaming).
Quando NÃO usar: queries em tempo real com requisito de baixa latência, transações ACID que precisam de consistência forte, sistemas de cache ou lookup simples. MapReduce é para processamento batch de grandes volumes.
# Pseudocódigo — Word Count em MapReduce
def map(document_id, text):
for word in text.split():
emit(word, 1) # emite (key, value)
def reduce(word, counts): # recebe (key, [v1, v2, ...])
emit(word, sum(counts)) # agrega e emite resultado final O problema: com shard = hash(key) % N, mudar N (adicionar um nó ou perder um) remapeia quase todas as chaves. Num cache distribuído isso equivale a esvaziar o cache inteiro de uma vez — e todo o tráfego cai no banco.
A solução: nós e chaves são posicionados num anel de hash (ex.: 0 … 2³²−1). Cada chave pertence ao primeiro nó encontrado no sentido horário. Ao adicionar ou remover um nó, só as chaves do intervalo vizinho mudam de dono — em média K/N chaves.
- Virtual nodes: cada nó físico ocupa várias posições no anel, o que uniformiza a distribuição e permite dar mais peso a máquinas maiores.
- Replicação: a chave também é guardada nos próximos R−1 nós do anel (modelo do Dynamo e do Cassandra).
- Onde aparece: Cassandra, DynamoDB, Riak, clientes de Memcached (ketama), Nginx (
hash $request_uri consistent;).
Replicar dados aumenta disponibilidade e capacidade de leitura, mas obriga você a escolher o que o cliente pode ver enquanto as réplicas convergem.
Topologias:
- Leader-follower (primary-replica): escritas no líder, leituras escaladas nas réplicas. Com replicação assíncrona existe replication lag — uma leitura na réplica pode retornar dado antigo.
- Multi-leader: escritas aceitas em várias regiões. Exige resolução de conflitos (last-write-wins, CRDTs, merge na aplicação).
- Leaderless (quorum): N réplicas; a escrita é confirmada por W e a leitura consulta R. Se
R + W > N, toda leitura encontra ao menos uma réplica com a última escrita confirmada. Ex.: N=3, W=2, R=2.
Modelos de consistência (do mais forte ao mais fraco): linearizability (strong) → read-your-writes → monotonic reads → eventual consistency.
Truque prático: para garantir read-your-writes sem consistência forte global, leia do líder por alguns segundos depois que o próprio usuário escreveu (ex.: após editar o perfil).
Na disciplina: CAP Theorem · Consensus & Algorithms
Uma CDN (Content Delivery Network) é uma rede de servidores de borda que guardam cópias do conteúdo perto do usuário. Reduz a latência (o RTT até a borda é muito menor que até a origem) e tira carga da origem.
- Pull CDN: a borda busca na origem no primeiro acesso (cache miss) e guarda conforme
Cache-Control/TTL. Simples, padrão na maioria dos casos. - Push CDN: você publica o conteúdo na CDN explicitamente. Útil para arquivos grandes e pouco alterados.
- Invalidação: prefira versionar o nome do arquivo (
app.3f9a1c.js) a invalidar cache — invalidação é lenta e cara. - Além de estáticos: TLS termination, proteção DDoS/WAF, edge compute e cache de respostas dinâmicas com TTL curto.
Exemplos: Amazon CloudFront, Cloudflare, Akamai, Fastly.
Limita quantas requisições um cliente (usuário, API key, IP) pode fazer numa janela de tempo. Protege contra abuso e picos acidentais, garante fair use e controla custo. A resposta padrão é HTTP 429 Too Many Requests com header Retry-After.
Algoritmos:
Token bucket:tokens são repostos a uma taxa fixa; cada request consome um. Permite bursts até o tamanho do balde. O mais usado em APIs.Leaky bucket:requests entram numa fila que é drenada a taxa constante. Suaviza o tráfego (é o modelo dolimit_reqdo Nginx).Fixed window counter:contador por janela (ex.: por minuto). Simples, mas permite até 2× o limite na virada da janela.Sliding window (log ou counter):mais preciso que a janela fixa, com custo maior de memória/cálculo.
Distribuído: com várias réplicas do gateway, o contador precisa ser compartilhado — tipicamente Redis (INCR + EXPIRE, ou um script Lua para atomicidade). Decida também o comportamento se o Redis cair: fail-open (deixa passar) ou fail-closed (bloqueia).
Na disciplina: Hands-on 2 — Gateway · Hands-on 8 — In-Memory Cache
API Gateway: ponto único de entrada do sistema. Concentra preocupações transversais — roteamento, autenticação (validação de JWT), rate limiting, TLS, CORS, agregação de respostas — para que os serviços internos não precisem repeti-las.
- Comunicação: síncrona (REST/gRPC) quando o cliente precisa da resposta agora; assíncrona (eventos em fila) quando o trabalho pode acontecer depois.
- Database per service: cada serviço é dono dos seus dados. Consistência entre serviços via Saga (orquestrada ou coreografada) e Outbox pattern para publicar eventos de forma confiável.
- Service discovery: DNS do orquestrador (Kubernetes Services) ou registry (Eureka, Consul).
Tradeoff que impressiona em entrevista: saber quando não usar microservices. Time pequeno e domínio ainda instável → comece com um monolito modular e extraia serviços quando houver motivo concreto (escala independente, deploy independente, times distintos).
Na disciplina: Microservices — Architecture & Components · Hands-on 2 — Gateway · Hands-on 3 — Security
Em sistemas distribuídos, falhas parciais são a regra. Estes padrões evitam que a falha de uma dependência derrube o sistema inteiro (falha em cascata).
Timeout:toda chamada remota precisa de um. Sem timeout, uma dependência lenta esgota as threads de quem a chama.Retry com exponential backoff + jitter:apenas para erros transitórios e operações idempotentes. O jitter evita que todos os clientes tentem de novo ao mesmo tempo (thundering herd).Circuit breaker:closed → open (falha rápido, sem chamar a dependência) → half-open (testa se ela voltou).Bulkhead:pools de recursos isolados por dependência, para que uma não consuma todas as conexões.Idempotency key:o cliente envia um identificador único; o servidor deduplica repetições da mesma operação.Fallback / graceful degradation:resposta degradada (cache, valor padrão) em vez de erro.Load shedding:rejeitar parte do tráfego cedo quando saturado, em vez de degradar todas as requisições.
Na disciplina: Resilience Patterns · Chaos Engineering
Projetar o sistema não basta: você precisa saber quando e onde ele está falhando. Mencionar observabilidade no fim da entrevista mostra maturidade de produção.
- Logs: o que aconteceu, evento a evento. Estruturados (JSON) e com trace id.
- Métricas: quanto e com que frequência. Método RED para serviços (Rate, Errors, Duration) e USE para recursos (Utilization, Saturation, Errors).
- Traces: onde o tempo foi gasto numa requisição que atravessa vários serviços.
SLI / SLO / SLA: SLI é a medida (ex.: % de requests com latência < 300 ms); SLO é a meta interna (ex.: 99,9% em 28 dias); SLA é o contrato com o cliente, com penalidade. O error budget (1 − SLO) define quanto risco você pode gastar com deploys e experimentos.
Use percentis, não médias: p50, p95 e p99. A média esconde a cauda — e é na cauda que estão os usuários insatisfeitos.
Na disciplina: Observability · SLI / SLO / SLA · Hands-on 6 · Hands-on 10
Exemplo Guiado: URL Shortener
Os 5 passos aplicados ao sistema do diagrama acima — o roteiro de uma resposta de ~45 minutos.
POST /api/v1/urls {longUrl, alias?, expiresAt?} → 201 {shortUrl} e GET /{code} → 301/302 Location: longUrl. Modelo: urls(code PK, long_url, created_at, expires_at). Componentes: load balancer → app servers stateless → banco. code só se o volume exigir. Cada clique vira um evento numa fila (Kafka/SQS) consumido por workers que gravam no banco de analytics — o redirect não espera por isso. Como gerar o código curto?
Hash + truncar
Hash (MD5/SHA-256) da URL, primeiros 7 caracteres em base62. Determinístico: a mesma URL gera o mesmo código. Contra: colisões precisam ser detectadas e resolvidas (re-hash com salt).
Contador + base62
ID único (sequence, Snowflake ou faixas pré-alocadas por servidor) convertido para base62. Sem colisões. Contra: códigos sequenciais são previsíveis e permitem enumerar links.
Key Generation Service
Um serviço pré-gera chaves aleatórias e as entrega em lotes aos app servers. Sem colisão no caminho da requisição e imprevisível. Contra: mais um componente para operar e replicar.
# base62: 62^7 ≈ 3,5 trilhões de códigos — sobra para 6B URLs
ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
def base62(n: int) -> str:
s = ""
while n > 0:
n, r = divmod(n, 62)
s = ALPHABET[r] + s
return s or "0" 301 (Moved Permanently) é cacheado pelo navegador: menos carga no servidor, mas os cliques seguintes não passam por você — a contagem fica incompleta.
302 (Found) passa sempre pelo servidor: analytics preciso, mas mais tráfego. Não existe resposta certa — escolha e justifique com os requisitos do passo 1.
Problemas para Praticar
Perguntas clássicas de entrevista. Tente resolver com os 5 passos antes de abrir os pontos-chave.
- Escolha do algoritmo (token bucket vs. sliding window) e justificativa.
- Estado compartilhado entre réplicas do gateway (Redis) e atomicidade (script Lua).
- Regras por cliente/endpoint, resposta
429comRetry-After. - Fail-open vs. fail-closed se o armazenamento de contadores cair.
- Fan-out on write (push para timelines pré-computadas) vs. fan-out on read (monta a timeline na leitura).
- Modelo híbrido: push para usuários comuns, pull para celebridades com milhões de seguidores.
- Timeline em cache (lista de IDs no Redis), mídia em object storage + CDN.
- Paginação por cursor, não por offset.
- Conexões persistentes (WebSocket) e um serviço que sabe em qual servidor cada usuário está conectado.
- Ordenação por conversa (sequence number por
conversation_id) — ordem global não é necessária. - Entrega para usuários offline (store-and-forward) e confirmações de entrega/leitura.
- Particionamento do armazenamento de mensagens por conversa (ex.: Cassandra).
- Múltiplos canais (push, e-mail, SMS) com filas e workers separados por canal.
- Retries com backoff e idempotência para não notificar duas vezes.
- Preferências e opt-out do usuário, rate limit por usuário, templates.
- Integração com provedores externos (APNs, FCM, SES, Twilio) e o que fazer quando eles falham.
- URL frontier: fila com prioridade e particionada por domínio.
- Politeness: respeitar
robots.txte limitar requisições por domínio. - Deduplicação: Bloom filter para URLs já vistas, hash de conteúdo para páginas duplicadas.
- Cache de DNS, armazenamento distribuído das páginas, detecção de spider traps.
- Atualizações frequentes de localização (a cada poucos segundos) — sistema write-heavy.
- Índice geoespacial: geohash, quadtree ou H3 para "motoristas próximos".
- Matching com garantia de que um motorista não seja atribuído a duas corridas (lock/consistência forte nesse ponto).
- Preço dinâmico, ETA e particionamento por região/cidade.
- Particionamento com consistent hashing e virtual nodes.
- Replicação com quorum configurável (N, R, W) e o tradeoff consistência × latência.
- Detecção de falhas (gossip), hinted handoff e anti-entropy (Merkle trees).
- Resolução de conflitos: vector clocks ou last-write-wins.
Conexão com a Disciplina
Onde cada conceito de entrevista aparece nas aulas e nos hands-ons de 2026.2.
Os hands-ons constroem, peça por peça, a mesma arquitetura do diagrama: gateway → load balancer → serviços stateless → cache → banco → fila → observabilidade. Use o seu projeto como exemplo concreto na entrevista: "no meu projeto, coloquei Redis compartilhado atrás do gateway porque cada réplica tinha um cache local inconsistente..."
Distributed Systems
CAP theorem, consistência e algoritmos de consenso.
Gateway
Ponto único de entrada, roteamento e o lugar natural do rate limiting.
Load Balancer
Nginx distribuindo tráfego entre réplicas — escala horizontal na prática.
In-Memory Cache
Redis compartilhado entre réplicas, cache-aside e TTL.
Kafka Messaging
Processamento assíncrono orientado a eventos e desacoplamento entre serviços.
Reliability
SLI/SLO/SLA, resilience patterns e chaos engineering.
Observability
Métricas com Prometheus e dashboards no Grafana.
Distributed Tracing
OpenTelemetry + Jaeger: onde o tempo de uma requisição foi gasto.
Bottlenecks
Identificar e mitigar gargalos — o passo 4 do roteiro de design.
Quiz
Teste seus conhecimentos com questões no estilo de entrevista real.
Checklist de Entrevista
Use para avaliar sua preparação antes de entrar numa entrevista de System Design. Marque os itens conforme pratica — o progresso fica salvo neste navegador.
Referências
Recursos fundamentais para aprofundamento — organizados por tipo e relevância para entrevistas técnicas.