Entrevistas Técnicas: System Design

O Que System Design Interviews Avaliam

System design é a entrevista que mais diferencia profundidade técnica. Em coding, candidatos de diferentes níveis podem ter performance similar. Em system design, a profundidade de raciocínio, a capacidade de fazer trade-offs e a visão sistêmica revelam claramente a experiência do candidato.

O que o entrevistador avalia:
┌────────────────────────────────────────────────────────┐
│ 1. Estruturação: segue um framework ou divaga?         │
│ 2. Escopo: define limites antes de desenhar?           │
│ 3. Estimativas: faz cálculos para justificar decisões? │
│ 4. Profundidade: vai além do diagrama de caixas?       │
│ 5. Trade-offs: explicita o que sacrificou e por quê?   │
│ 6. Comunicação: explica decisões com clareza?          │
│ 7. Experiência: traz exemplos de sistemas reais?       │
└────────────────────────────────────────────────────────┘

O Framework: 5 Fases em 40-45 Minutos

Fase 1: Requirements (5 minutos)

Nunca comece desenhando. Defina o escopo primeiro.

REQUISITOS FUNCIONAIS (o que o sistema faz):
  → Quais são as operações principais? (CRUD? Read-heavy? Write-heavy?)
  → Quem são os usuários? (B2C? B2B? Internos?)
  → Quais features são core vs nice-to-have?
  → Há requisitos de tempo real? (chat, notificações, streaming)

REQUISITOS NÃO-FUNCIONAIS (como o sistema se comporta):
  → Quantos usuários? (DAU — Daily Active Users)
  → Qual latência aceitável? (P50, P99)
  → Qual disponibilidade necessária? (99.9%? 99.99%?)
  → Qual consistência? (strong? eventual?)
  → Há requisitos regulatórios? (LGPD, GDPR, PCI-DSS)

EXEMPLOS DE PERGUNTAS QUE IMPRESSIONAM:
  "O sistema precisa funcionar globalmente ou em uma região?"
  "Qual é o ratio de leitura vs escrita esperado?"
  "Precisamos de consistência forte ou eventual é aceitável?"
  "Há picos sazonais? (Black Friday, eventos)"
  "Qual é o budget de infraestrutura?"

Fase 2: Back-of-the-Envelope Estimation (5 minutos)

Estimativas justificam decisões de arquitetura. Se você não calcula, está chutando.

NÚMEROS QUE VOCÊ DEVE MEMORIZAR:

Latências:
  L1 cache reference:                 0.5 ns
  RAM reference:                      100 ns
  SSD random read:                    150 μs
  HDD random read:                    10 ms
  Rede dentro do datacenter:          0.5 ms
  Rede entre regiões:                 50-150 ms

Throughput:
  SSD sequential read:                1 GB/s
  Network (1 Gbps):                   125 MB/s
  Disco HDD sequential:               100 MB/s

Escala:
  1 servidor web:                     ~1K-10K RPS (depende do workload)
  MySQL/PostgreSQL:                   ~5K-10K QPS (depende da query)
  Redis:                              ~100K-500K ops/s
  Kafka partition:                    ~10K-100K msgs/s
  1 GB = ~1 bilhão de caracteres ASCII
  1 dia = 86.400 segundos ≈ ~10^5 segundos

EXEMPLO DE CÁLCULO — URL Shortener:

  Premissas:
  - 100M URLs criadas por mês
  - Ratio leitura/escrita: 100:1
  - Reter por 5 anos

  QPS (Queries Per Second):
  - Escrita: 100M / (30 dias × 86400s) ≈ ~40 writes/s
  - Leitura: 40 × 100 = ~4.000 reads/s
  - Pico (3x): ~12.000 reads/s

  Storage:
  - Cada URL: ~500 bytes (URL original + hash + metadata)
  - 5 anos: 100M × 12 × 5 = 6 bilhões de URLs
  - Total: 6B × 500 bytes = 3 TB

  Bandwidth:
  - Escrita: 40 × 500 bytes = 20 KB/s (desprezível)
  - Leitura: 4.000 × 500 bytes = 2 MB/s

  Cache (regra 80/20):
  - 20% das URLs geram 80% do tráfego
  - Cache diário: 4.000 × 86.400 × 500 bytes × 0.2 ≈ ~35 GB
  - Cabe em um Redis single-node

Fase 3: High-Level Design (10 minutos)

Desenhe os componentes principais e as conexões entre eles.

Componentes típicos de um sistema web:

  ┌──────────┐     ┌──────────┐     ┌──────────────┐
  │  Client  │────▶│   CDN    │     │ Load Balancer │
  │(Web/App) │     │(estático)│     │  (L7 / ALB)   │
  └──────────┘     └──────────┘     └──────┬───────┘

                    ┌──────────────────────┼───────────────────┐
                    │                      │                   │
              ┌─────▼─────┐         ┌─────▼─────┐      ┌─────▼─────┐
              │ API Server│         │ API Server│      │ API Server│
              │  (pod 1)  │         │  (pod 2)  │      │  (pod N)  │
              └─────┬─────┘         └─────┬─────┘      └─────┬─────┘
                    │                      │                   │
         ┌──────────┼──────────────────────┼───────────────────┤
         │          │                      │                   │
   ┌─────▼─────┐ ┌─▼────────┐    ┌───────▼──────┐    ┌──────▼─────┐
   │   Cache   │ │ Database  │    │ Message Queue│    │ Object     │
   │  (Redis)  │ │(Postgres) │    │  (RabbitMQ)  │    │ Storage    │
   └───────────┘ └───────────┘    └──────┬───────┘    │  (S3)      │
                                         │            └────────────┘
                                  ┌──────▼───────┐
                                  │   Workers    │
                                  │ (consumers)  │
                                  └──────────────┘

DICAS PARA O DIAGRAMA:
  → Mostre a direção do fluxo de dados (setas)
  → Indique protocolo (HTTP, gRPC, WebSocket, AMQP)
  → Marque se é síncrono ou assíncrono
  → Nomeie os componentes com tecnologias específicas
  → Separe read path e write path se forem diferentes

Fase 4: Deep Dive (15 minutos)

Escolha 2-3 componentes para aprofundar. O entrevistador geralmente indica.

TÓPICOS COMUNS DE DEEP DIVE:

1. Database Design
   → Schema (tabelas, índices, constraints)
   → Partitioning/Sharding strategy
   → Replication (sync vs async)
   → SQL vs NoSQL (justifique com o workload)

2. API Design
   → Endpoints REST ou gRPC
   → Pagination (cursor-based vs offset)
   → Rate limiting strategy
   → Idempotência

3. Caching Strategy
   → O que cachear (hot data, computed results)
   → Onde cachear (CDN, Redis, application-level)
   → Invalidação (TTL, write-through, write-behind)
   → Cache stampede prevention (locking, probabilistic early expiry)

4. Escalabilidade
   → Horizontal vs vertical scaling
   → Stateless servers (session no Redis)
   → Database read replicas
   → Connection pooling
   → Auto-scaling triggers

5. Resiliência
   → Circuit breaker para dependências externas
   → Retry com exponential backoff
   → Bulkhead pattern (isolamento de falhas)
   → Graceful degradation
   → Health checks e readiness probes

Fase 5: Trade-offs e Bottlenecks (5 minutos)

SEMPRE termine com trade-offs explícitos:

"Neste design, priorizei [disponibilidade] sobre [consistência]
porque [a maioria das operações tolera eventual consistency, e
downtime custa $X/minuto]."

"O bottleneck principal é [o banco de dados em writes].
Com mais tempo, eu adicionaria [write-behind cache + async
processing] para reduzir a carga."

"Se o orçamento fosse ilimitado, eu faria [multi-region active-active
com CockroachDB], mas para o custo atual, [single-region com
read replicas] é suficiente."

Conceitos de Sistemas Distribuídos

CAP Theorem na Prática

CAP: em uma partição de rede, você deve escolher entre
Consistência (C) e Disponibilidade (A).

              Consistência
              (todos veem o mesmo dado)
                   /\
                  /  \
                 /    \
                / CP   \
               / Redis  \
              / MongoDB  \
             / (config   \
            /  primário)  \
           /               \
          /       CA        \
         / PostgreSQL single \
        /   (sem partição)   \
       /                      \
      /_________________________\
   Disponibilidade      Tolerância a
   (sempre responde)    Partição
                        (funciona com rede falhando)

Na prática para entrevistas:

CP (Consistência + Partição): Quando dados incorretos são inaceitáveis
  → Sistemas financeiros, inventário, configuração
  → Exemplo: ZooKeeper, etcd, Redis Cluster (em modo strong consistency)

AP (Disponibilidade + Partição): Quando indisponibilidade é inaceitável
  → Social feeds, DNS, cache distribuído
  → Exemplo: Cassandra, DynamoDB, DNS

CA (Consistência + Disponibilidade): Quando não há partições de rede
  → Sistema single-node (PostgreSQL standalone)
  → Na prática, partições sempre são possíveis → CA é teórico

Modelos de Consistência

Strong Consistency:
  → Toda leitura retorna o dado mais recente
  → Custo: latência alta (replicação síncrona)
  → Quando usar: transações financeiras, inventário

Eventual Consistency:
  → Leituras podem retornar dados stale temporariamente
  → Custo: complexidade de resolução de conflitos
  → Quando usar: feeds, likes, analytics, DNS

Causal Consistency:
  → Operações causalmente relacionadas são vistas na ordem correta
  → Custo: tracking de dependências causais
  → Quando usar: mensagens em chat (ordem importa por conversa)

Read-your-writes:
  → Após escrever, o mesmo usuário sempre vê o dado atualizado
  → Outros usuários podem ver dado stale
  → Implementação: sticky sessions ou read from primary após write

Particionamento (Sharding)

Estratégias de sharding:

1. Range-based:
   → Shard por range de valores (users A-M → shard 1, N-Z → shard 2)
   → Prós: queries de range eficientes
   → Contras: hotspots (letra "S" tem mais nomes que "X")

2. Hash-based:
   → Shard por hash(key) % num_shards
   → Prós: distribuição uniforme
   → Contras: queries de range requerem scatter-gather

3. Consistent Hashing:
   → Ring hash com virtual nodes
   → Prós: mínima redistribuição ao adicionar/remover nós
   → Contras: mais complexo de implementar

4. Directory-based:
   → Lookup table que mapeia key → shard
   → Prós: flexível, pode rebalancear arbitrariamente
   → Contras: lookup table vira ponto de falha/bottleneck

Sistemas Comuns em Entrevistas

URL Shortener

Requisitos: criar short URLs, redirecionar, analytics
Escrita: POST /shorten → gerar hash base62 de 7 chars → 62^7 = 3.5T URLs
Leitura: GET /:hash → 301 redirect → read-heavy (100:1)

Decisões chave:
  → Geração de hash: MD5/SHA256 truncado ou counter + base62?
    Counter garante unicidade sem colisão. MD5 pode colidir.
    Counter precisa de serviço centralizado (Snowflake ID).
  → Storage: Key-value (DynamoDB) — não precisa de JOINs
  → Cache: Redis com TTL — URLs populares em memória
  → CDN: 301 é cacheável pelo browser e CDN
  → Analytics: Async — evento a cada redirect → Kafka → OLAP

Trade-off: 301 (permanent) vs 302 (temporary)?
  301: browser cacheia → menos load, mas analytics perde visibilidade
  302: não cacheia → mais load, analytics preciso

Chat System (WhatsApp/Slack)

Requisitos: mensagens 1:1, grupos, presença online, delivery status
Conexões: WebSocket para real-time, HTTP para historico

Decisões chave:
  → WebSocket Gateway: stateful → sticky sessions ou connection registry
  → Storage: mensagens em Cassandra (write-heavy, append-only)
  → Message ordering: por conversa (não global) → causal consistency
  → Delivery status: sent → delivered → read (state machine)
  → Presença: heartbeat a cada 30s → Redis com TTL
  → Push notification: fallback quando WebSocket desconecta
  → Grupos: fan-out on write (mensagem copiada para cada membro)
    vs fan-out on read (lê de uma fonte central)
    → Grupos pequenos (<100): fan-out on write
    → Canais grandes (>1000): fan-out on read

Trade-off: fan-out on write gasta mais storage mas leitura é O(1).
           fan-out on read economiza storage mas leitura é O(n_membros).

News Feed (Twitter/Instagram)

Requisitos: publicar post, timeline personalizada, likes/comments
Problema core: timeline assembly é O(followings × posts) se feito em read

Decisões chave:
  → Fan-out on write: ao publicar, push para timeline cache de cada follower
    Bom para usuários normais (~1K followers)
  → Fan-out on read: ao ler timeline, pull posts dos followings
    Necessário para celebridades (~10M followers)
  → Híbrido: fan-out on write para users normais + fan-out on read para
    celebridades → merge no momento da leitura

  → Ranking: chronological ou algorithmic?
    ML model scores cada post: engagement_score × recency × affinity
  → Storage: posts em MySQL/PostgreSQL, timeline cache em Redis
  → Media: S3 + CDN para imagens/vídeos
  → Notifications: async via event + push service

Rate Limiter

Requisitos: limitar requests por user/IP/API key, window fixo ou sliding
Algoritmos:

1. Fixed Window Counter:
   → Contador por janela de tempo (ex: 100 req/minuto)
   → Simples mas permite burst na fronteira (2x no boundary)
   → Redis: INCR key, EXPIRE key 60

2. Sliding Window Log:
   → Armazena timestamp de cada request
   → Preciso mas usa mais memória
   → Redis: ZADD (sorted set) + ZRANGEBYSCORE

3. Sliding Window Counter:
   → Combina fixed window com peso proporcional
   → Boa precisão com memória baixa
   → Fórmula: requests = prev_window × overlap% + current_window

4. Token Bucket:
   → Tokens adicionados a taxa fixa, consumidos por request
   → Permite burst controlado
   → Redis: HMSET (tokens, last_refill_time)

5. Leaky Bucket:
   → Queue com saída a taxa fixa
   → Suaviza o tráfego, sem burst
   → Implementação: queue com consumer a taxa constante

Onde colocar: API Gateway (antes de chegar na aplicação)
Storage: Redis (centralizado, atômico) ou in-memory (por instância)
Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset

Database Selection Guide

Workload                          | Banco recomendado
----------------------------------|-----------------------------------
OLTP relacional genérico          | PostgreSQL
Read-heavy com schema flexível    | MongoDB, DynamoDB
Write-heavy append-only           | Cassandra, ScyllaDB
Key-value cache                   | Redis, Memcached
Full-text search                  | Elasticsearch, PostgreSQL (FTS)
Time-series                       | TimescaleDB, InfluxDB
Graph relationships               | Neo4j, Amazon Neptune
Analytics/OLAP                    | ClickHouse, BigQuery, Redshift
Wide-column (petabytes)           | HBase, Cassandra
Multi-model                       | FaunaDB, CosmosDB

Expectativas por Nível

NÍVEL MID/PLENO (L4-L5):
  → Segue o framework básico
  → Define requisitos funcionais
  → Desenha diagrama high-level correto
  → Escolhe banco de dados com justificativa simples
  → Faz deep dive em 1 componente

NÍVEL SÊNIOR (L5-L6):
  → Tudo acima +
  → Back-of-the-envelope calculations
  → Deep dive em 2-3 componentes com detalhes técnicos
  → Discute trade-offs explicitamente
  → Traz experiências reais como referência
  → Considera failure modes e resiliência

NÍVEL STAFF+ (L6-L7):
  → Tudo acima +
  → Questiona premissas do entrevistador
  → Propõe design evolutivo (MVP → escala)
  → Discute impacto organizacional (team boundaries, ownership)
  → Considera custos operacionais e complexidade de manutenção
  → Aborda observabilidade (metrics, logging, tracing)
  → Discute estratégia de migração e rollback
  → Traz perspectiva de produto junto com a técnica

Referências

- "System Design Interview" Vol 1 e 2 — Alex Xu
- "Designing Data-Intensive Applications" — Martin Kleppmann
- "Building Microservices" — Sam Newman
- ByteByteGo: https://bytebytego.com
- Grokking System Design: https://www.designgurus.io
- "The System Design Primer": https://github.com/donnemartin/system-design-primer
- "Database Internals" — Alex Petrov
- Google SRE Book: https://sre.google/sre-book/