Em 2011, o Instagram tinha 14 milhões de usuários e rodava em menos de 10 servidores EC2. Em 2012, quando foi adquirido pelo Facebook por 1 bilhão de dólares, tinha 30 milhões de usuários e ainda rodava em menos de 10 servidores. O que permitia isso não era magia — era que cada decisão de arquitetura havia sido calibrada para a escala real, não para a escala imaginária. Cache agressivo onde fazia sentido, banco de dados simples onde o volume não justificava sharding, CDN para o que realmente precisava de CDN. Os fundadores sabiam os números do sistema e tomavam decisões baseadas neles.
Capacity estimation é a prática de derivar números concretos — requisições por segundo, storage por ano, banda de rede por hora — a partir dos requisitos de escala do sistema. O objetivo não é precisão: uma estimativa que erra por 2x ainda é útil para decidir se você precisa de cache ou não, se o banco vai saturar em 6 meses ou 6 anos, se CDN é sobre latência ou sobre capacidade. O objetivo é que os números informem decisões arquiteturais — e que as decisões erradas sejam eliminadas antes de virar código.
Os números de referência que você precisa internalizar
Estimation em tempo real requer que certos números estejam na memória — não em uma tabela que você consulta, mas internalizados ao ponto de poder fazer cálculos mentalmente enquanto conversa sobre arquitetura. Esses são os números que aparecem repetidamente:
Latências de I/O
Operação Latência típica
─────────────────────────────────────────────────────
L1 cache (CPU) ~1 ns
L2 cache (CPU) ~4 ns
RAM (leitura sequencial) ~100 ns
SSD NVMe (leitura aleatória) ~100 µs (100.000 ns)
HDD (seek + leitura) ~10 ms (10.000.000 ns)
Redis (rede intra-DC) ~0.5 ms
PostgreSQL query simples (com cache) ~1–5 ms
PostgreSQL query sem cache ~10–50 ms
Rede intra datacenter ~0.5 ms
Rede inter-região (EUA → Europa) ~150 ms
Rede intra-região AWS (AZ→AZ) ~1–2 ms
O que esses números revelam sobre design:
- RAM é 100.000x mais rápida que SSD para leitura aleatória — justifica manter hot data em memória
- Redis é ~10x mais rápido que PostgreSQL para leituras simples por chave — justifica cache para leituras frequentes
- 150ms de latência intercontinental é irredutível por física — justifica CDN edge para conteúdo estático e regiões distribuídas para sistemas globais com SLO de latência < 100ms
Throughput de sistemas comuns
Sistema Throughput típico
────────────────────────────────────────────────────────────
Servidor web (Nginx, serving estático) ~50.000 req/s por core
Servidor de aplicação (Go/Java) ~5.000–20.000 req/s
Servidor de aplicação (Python/Ruby) ~500–2.000 req/s
PostgreSQL (writes simples) ~5.000–10.000 writes/s
PostgreSQL (reads com índice) ~50.000–100.000 reads/s
PostgreSQL com read replicas (×3) ~150.000–300.000 reads/s
Redis (operações simples, single thread) ~100.000 ops/s
Redis Cluster (6 shards) ~600.000 ops/s
Kafka (throughput de ingestão) ~1.000.000 msgs/s por partição
S3 (PUT requests) ~3.500 PUT/s por prefix
S3 (GET requests) ~5.500 GET/s por prefix
A implicação prática mais importante: PostgreSQL com read replicas aguenta ~300k reads/s. Para a maioria dos sistemas, isso é suficiente sem sharding. A pergunta "precisamos de sharding?" deve ser respondida por estimation, não por instinto.
Tamanhos de objetos típicos
Objeto Tamanho típico
────────────────────────────────────────────────────
Tweet (texto + metadados) ~500 bytes
Registro de usuário ~1 KB
Mensagem de chat (texto) ~200 bytes
Imagem de perfil (JPEG comprimida) ~100 KB
Post de blog (texto, sem imagens) ~10 KB
Foto de alta resolução (JPEG) ~3–5 MB
Vídeo de 1 minuto (1080p, H.264) ~50–150 MB
Log estruturado por linha ~500 bytes
O processo de estimation em quatro derivações
A estimation parte sempre de uma âncora — o número de usuários ativos diários (DAU) ou mensais (MAU). Dali, quatro derivações informam as decisões mais importantes de arquitetura:
Derivação 1: QPS (queries per second)
# Fórmula base:
# QPS = DAU × operações_por_usuário_por_dia ÷ 86.400 segundos
# Exemplo: sistema de microblog tipo Twitter
# DAU: 300M usuários
# Comportamento médio: 5 leituras de timeline, 0,1 tweets por usuário/dia
# Razão leitura:escrita = 50:1
QPS_write = 300M × 0,1 tweets ÷ 86.400s
= 30M ÷ 86.400
≈ 347 writes/s
→ pico (3× média): ~1.000 writes/s
QPS_read = 347 × 50
≈ 17.350 reads/s
→ pico: ~52.000 reads/s
# Decisão que essa estimation informa:
# 1.000 writes/s → PostgreSQL aguenta sem sharding
# 52.000 reads/s → PostgreSQL sem cache não aguenta (~50k é o limite)
# Redis cache com 95% hit rate → banco recebe 2.600 reads/s ✓
Derivação 2: Storage em N anos
# Fórmula:
# Storage = objetos_criados_por_dia × tamanho_por_objeto × dias × fator_replicação
# Continuando o exemplo do microblog:
# Writes/dia = 1.000/s × 86.400s = ~86M tweets/dia
# Tamanho por tweet: texto(280 chars) + metadados + índices ≈ 500 bytes
Storage_raw_por_dia = 86M × 500 bytes = 43 GB/dia
Storage_raw_5_anos = 43 GB × 365 × 5 = ~78 TB
# Com replicação 3×: ~234 TB
# Com índices e metadados adicionais (~2×): ~470 TB
# Decisão que essa estimation informa:
# 470 TB em 5 anos com crescimento uniforme → particionamento por data é
# suficiente; sharding por user_id ainda não obrigatório no horizonte de 5 anos.
# Mas se o crescimento for 2× ao ano: 78TB × 2^5 = 2,5 PB → sharding necessário
# antes do ano 3. Vale modelar os dois cenários.
Derivação 3: Bandwidth de rede
# Fórmula:
# Bandwidth = QPS_read × tamanho_médio_resposta
# Sistema de foto (tipo Instagram):
# QPS_read: 100.000 reads/s no pico (feeds de foto)
# Tamanho médio da resposta: thumbnail (50KB) + metadata (1KB) ≈ 51KB
# (foto full-resolution servida pelo CDN, não pelo app server)
Bandwidth_inbound = 1.000 writes/s × 3MB foto original = 3 GB/s → S3 direct upload
Bandwidth_outbound = 100.000 reads/s × 51KB = 5,1 TB/s
# 5,1 TB/s de outbound é impossível de servir da origem.
# → CDN não é opcional. É a diferença entre "funciona" e "não funciona".
# → Mesmo com 99% de cache hit no CDN, a origem recebe 51 GB/s — ainda precisa
# de múltiplas regiões e object storage distribuído (S3 com multi-region).
# Decisão que essa estimation informa:
# CDN é infraestrutura crítica, não otimização. S3 direct upload com
# CloudFront (ou equivalente) não é uma escolha — é requisito.
Derivação 4: Memória de cache necessária
# Princípio de Pareto: 80% do tráfego acessa 20% dos dados
# Se você cacheia o "top 20%", atende ~80% das leituras sem ir ao banco
# Microblog: 86M tweets/dia, os 20% mais acessados são ~17M tweets
# Tamanho por tweet em cache: 500 bytes (só o hot path, sem índices)
# Memória necessária: 17M × 500 bytes = 8,5 GB
# Redis node com 32GB de RAM → cabe com folga
# Redis Cluster não necessário nessa escala → single node com sentinel para HA
# Decisão que essa estimation informa:
# Cache fit em memória de um único nó → Redis single node com AOF
# é suficiente. Kafka não é necessário para invalidação de cache nessa escala.
Como estimation muda o design: dois exemplos contrastantes
A mesma categoria de sistema com escalas diferentes requer arquiteturas radicalmente diferentes. Isso ilustra por que estimation precede o design — não o contrário.
Sistema de URL shortener: 1k vs 100k redirects/segundo
Cenário A: startup pequena, 1k redirects/s
─────────────────────────────────────────
QPS_read: 1.000 redirects/s
QPS_write: 10 URLs/s (razão 100:1)
Storage: 10 writes/s × 200B × 86400s × 365 dias × 5 anos = ~316 GB
Arquitetura:
- 1 servidor de aplicação (Go) → aguenta 1k req/s com folga
- 1 PostgreSQL primary + 1 read replica → aguenta 1k reads/s sem cache
- Redis opcional → adiciona se latência P99 for problema
- Sem CDN (latência não é requisito) → origem aguenta o volume
Cenário B: escala real (bit.ly, tinyurl), 100k redirects/s
───────────────────────────────────────────────────────────
QPS_read: 100.000 redirects/s
QPS_write: 1.000 URLs/s
Storage: 1000 writes/s × 200B × 86400s × 365 × 5 anos = ~31.5 TB
Arquitetura:
- 5–10 servidores de aplicação (load balanced)
- Redis Cluster para cache de redirects (99% hit rate → banco recebe 1k/s)
- PostgreSQL com 3 read replicas → banco recebe 1k reads/s (com 99% cache hit)
- CDN global (latência P99 < 50ms em qualquer região)
- Particionamento de analytics por data (analytics > redirect data em volume)
- Geração de short code: precisa de coordination para evitar colisões (ver C03)
A diferença não é "otimizar" o Cenário A para virar o Cenário B — são soluções fundamentalmente diferentes para o mesmo problema em escalas diferentes. Construir o Cenário B para o Cenário A é over-engineering com custo real de complexidade e operação.
Erros comuns em estimation
Confundir média com pico: a maioria do tráfego web tem picos de 5–10x a média. Um sistema dimensionado para a média vai cair durante pico. A regra prática: dimensionar para 3× a média como steady state, com autoscaling para absorver picos de até 10×. Um banco de dados dimensionado para a média com headroom de 30% vai saturar na primeira Black Friday.
Ignorar replicação no cálculo de storage: 1 TB de dados brutos com replicação fator 3 e índices que tipicamente adicionam 30–50% ao tamanho resulta em ~4 TB de storage real. Storage de produção geralmente é 3–5× o tamanho dos dados brutos quando você inclui replicação, índices, logs de replicação, e backups.
Não separar leitura de escrita: leitura e escrita têm características de carga completamente diferentes. Um sistema read-heavy (razão 100:1) tem solução diferente de um sistema write-heavy (razão 1:10) mesmo com o mesmo número total de operações. PostgreSQL com read replicas escala leitura horizontalmente mas não escrita — se o gargalo é escrita, read replicas não ajudam.
Esquecer de onde vêm os bytes: a banda de rede do sistema de fotos não é determinada pelo número de uploads (inbound) — é determinada pelo número de downloads (outbound), que pode ser 100x maior. Sistemas de mídia têm assimetria radical entre inbound e outbound que muda completamente a estratégia de infraestrutura.
Assumir que crescimento é linear: storage cresce linearmente com o tempo (dados acumulam), mas QPS pode crescer exponencialmente em lançamentos virais. O capacity plan precisa modelar os dois cenários: crescimento orgânico esperado e crescimento explosivo não-esperado (o que acontece se você aparecer no ProductHunt amanhã?).
Da estimation à decisão arquitetural
Estimation só tem valor se informa decisões. A conexão explícita entre número e decisão é o que separa estimation útil de exercício matemático:
Número derivado → Decisão arquitetural
QPS_read > 50k → Cache obrigatório (Redis na frente do banco)
QPS_read > 300k → Read replicas + cache + considerar sharding
QPS_write > 10k → Sharding ou banco NoSQL para writes
Storage > 10 TB em 5 anos → Particionamento por data desde o início
Storage > 100 TB em 5 anos → Sharding, tiered storage (hot/warm/cold)
Bandwidth_out > 10 GB/s → CDN obrigatório
Objetos > 1 MB → Object storage (S3), nunca blob em banco relacional
Latência P99 < 50ms global → CDN edge + regiões distribuídas
DAU > 10M → Considerar separação de banco por domínio
Mensagens > 100k/s → Kafka; abaixo disso SQS/RabbitMQ é suficiente
Esses thresholds não são regras absolutas — são pontos de inflexão onde as alternativas mais simples deixam de ser viáveis. Abaixo de 50k reads/s, PostgreSQL com boas queries e índices geralmente aguenta sem cache. Acima disso, a probabilidade de precisar de cache cresce rapidamente.
Estimation como ferramenta contínua, não ritual de design
Estimation não é só para novos sistemas. Em sistemas existentes, estimation retroativa revela problemas antes que se tornem crises:
- Crescimento de storage: se o banco cresce 50 GB/mês e o disco primário tem 2 TB, você tem ~3 meses antes de saturar — não 3 anos. Essa conta simples, feita regularmente, previne o incidente de disco cheio que derruba produção.
- Aproximação do limite de conexões: PostgreSQL tem um limite de conexões simultâneas (geralmente 100–200 em instâncias menores). Se cada deploy adiciona 2 conexões e você faz 3 deploys por semana, em quanto tempo você chega no limite? Essa conta revela quando connection pooling (PgBouncer) precisa ser introduzido — antes do problema, não durante.
- Cache hit rate declinante: se o cache tem 95% de hit rate com 1M usuários ativos mas apenas 10% dos dados cabe em memória, o hit rate vai cair à medida que a base de usuários cresce e o conjunto de dados quentes se expande. Modelar como o hit rate evolui com o crescimento permite planejar quando aumentar a capacidade de cache ou mudar a estratégia de caching.
Quando um time fala sobre escala, os números precisam ser compartilhados e entendidos da mesma forma. "Vai escalar" é uma afirmação sem significado — "vai aguentar 50k reads/s com uma read replica e cache com 90% de hit rate" é uma afirmação verificável. Estimation cria uma linguagem compartilhada de números que permite que discussões sobre arquitetura sejam concretas em vez de abstratas. O engenheiro sênior que entra numa reunião com os números calculados transforma uma discussão de opiniões numa discussão de trade-offs verificáveis.
Decisões de engenharia
Estimativas back-of-envelope (cálculo mental ou papel) são adequadas para decisões arquiteturais rápidas: "precisamos de cache?" pode ser respondido em 2 minutos. Modelos em planilha são necessários para capacity planning formal: "quando precisamos provisionar mais storage?" requer modelagem de crescimento com cenários múltiplos.
Regra prática: back-of-envelope para eliminar alternativas arquiteturais claramente erradas e para sessões de design. Planilha para capacity planning que vai informar decisões de compra de infraestrutura ou que precisa ser auditável.
A estimation inicial é baseada em suposições sobre comportamento de usuário que frequentemente estão erradas. Em produção, você tem dados reais: o QPS medido pode ser 3x o estimado (subestimou o engajamento) ou 0.3x (superestimou adoção). Refazer a estimation com dados reais periodicamente — mensal nos primeiros 6 meses, trimestral depois — calibra o capacity plan e revela problemas antes que se tornem crises.
Regra prática: refazer a estimation sempre que o tráfego real divergir mais de 2× da estimativa inicial, ou quando um novo comportamento de usuário emergir que não estava modelado.
Dimensionar exatamente para a carga esperada não deixa margem para picos, bugs de performance inesperados, ou crescimento acima do projetado. Mas over-provisionar demais desperdiça dinheiro e pode esconder ineficiências (um serviço que consome 5% da CPU nunca vai revelar um memory leak). A margem depende do tipo de componente.
Regra prática: banco de dados: nunca acima de 70% de capacidade de escrita em steady state (30% de margem). Servidores de aplicação: autoscaling com target de 60-70% de CPU. Storage: alertar em 75%, provisionar mais em 80%. Cache: manter hit rate monitorado; se cair abaixo de 85%, revisar strategy.
Estimation responde "o design vai funcionar?" antes de implementar. Load testing responde "a implementação funciona?" depois de implementar. Não são substitutos — são complementares em momentos diferentes. Uma estimation que indica "PostgreSQL aguenta o volume" pode estar errada se as queries forem ineficientes; o load test vai descobrir isso. Uma estimation incorreta pode levar a um load test no sistema errado.
Regra prática: estimation antes do design para eliminar arquiteturas claramente erradas; load test antes de ir para produção para validar que a arquitetura escolhida realmente aguenta o volume estimado com as queries reais.
Perguntas de entrevista
Como você estimaria o número de servidores necessários para um sistema com 1M de usuários ativos diários?
A derivação parte do comportamento de uso, não do número de usuários:
1. Modelar o comportamento: 1M DAU com comportamento médio de "10 ações por dia por usuário" (leitura de feed, alguns cliques) → 10M operações/dia. Assumindo distribuição uniforme: 10M ÷ 86.400 ≈ 116 ops/s em média. Pico (3× média): ~350 ops/s.
2. Modelar o servidor: depende do tipo de operação. Para serving de API típica (lógica moderada, calls para banco): um servidor Go/Java aguenta 5.000–10.000 req/s. Um servidor Python/Node.js com I/O async: 1.000–3.000 req/s.
3. Calcular: 350 ops/s ÷ 5.000 ops/s por servidor = 0,07 servidores → 1 servidor aguenta com enorme folga. Mesmo no pico de 10× (viral): 3.500 ops/s ÷ 5.000 = 0,7 servidores. Ainda 1 servidor.
Conclusão: 1M DAU com comportamento moderado não precisa de múltiplos servidores de aplicação no MVP. O gargalo quase certamente não é o servidor de aplicação — é o banco de dados e a latência de I/O. Dimensionar o banco corretamente é mais importante do que escalar horizontalmente os servidores.
Se a pergunta for sobre um sistema de alta frequência (streaming, gaming), o comportamento por usuário muda radicalmente — pode ser 1.000 ops/min em vez de 10/dia, o que muda toda a estimation.
Como você determina se um sistema precisa de cache — e quanto de memória o cache vai precisar?
A decisão de cache começa com a estimation de QPS de leitura e a comparação com a capacidade do banco:
Passo 1 — estimation de QPS de leitura: derivar o número de reads/s no pico como mostrado acima. Se o QPS de leitura fica abaixo de ~50k, PostgreSQL com índices bons provavelmente aguenta sem cache.
Passo 2 — verificar a razão leitura:escrita: sistemas com razão alta (100:1 ou mais) se beneficiam muito de cache porque a mesma escrita é lida muitas vezes. Sistemas com razão baixa (1:1) têm menos benefício porque dados ficam stale rapidamente.
Passo 3 — estimar o hot set: quais dados são acessados com maior frequência? O princípio de Pareto (80/20) é um ponto de partida: 20% dos dados respondem por 80% das leituras. Calcular o tamanho do hot set e verificar se cabe em memória de um único nó Redis ou se precisa de cluster.
Exemplo: sistema com 100M registros de usuário, cada um com 1KB, e hot set de 20%: 20M × 1KB = 20GB. Cabe num Redis node de 32GB com folga → sem cluster. Se o hot set fosse 50%: 50GB → Redis Cluster com 3 shards de 32GB cada.
A memória de cache necessária não é "o tamanho total dos dados" — é o tamanho do hot set. A diferença frequentemente é uma ordem de magnitude.
Qual é o custo de replicar dados em múltiplas regiões e quando ele se justifica?
O custo de replicação multi-região tem três componentes:
1. Custo de egress: toda transferência de dados entre regiões AWS custa ~$0.02/GB. Para um banco de dados que recebe 100 GB/dia de writes e replica para 2 regiões adicionais: 100GB × 2 × $0.02 = $4/dia = ~$120/mês só de egress. Para 1 TB/dia de writes: $1.200/mês de egress. Esse custo cresce linearmente com volume de escrita.
2. Custo de infraestrutura duplicada: cada região adicional precisa da stack completa — banco, cache, servidores de aplicação. Na prática, 2x ou 3x o custo de infraestrutura de uma região.
3. Custo de complexidade: consistência multi-região exige consenso distribuído ou aceitação de eventual consistency. Cada operação que requer consistência forte adiciona latência de round-trip inter-regional (150ms para EUA→Europa). Isso se manifesta como complexidade de código e de operação.
A justificativa é igualmente concreta: se o SLO de latência é < 100ms globalmente, e a latência intercontinental é 150ms, não há solução sem regiões distribuídas. Se o SLO de disponibilidade é 99.99%, uma única região tem risco de outage regional que viola o SLO; duas regiões em active-passive reduzem esse risco. Se há requisitos regulatórios de data residency (GDPR), a replicação não é uma escolha — é um requisito.
Como a estimation muda quando o sistema tem comportamento de acesso altamente desigual (distribuição de Pareto extrema)?
Sistemas com distribuição de Pareto extrema — onde 1% dos objetos recebe 99% do tráfego — têm características de estimation muito diferentes de sistemas com distribuição uniforme.
O fenômeno mais importante é o hot key problem: um único objeto (uma postagem viral, um produto em promoção relâmpago, um usuário com 100M seguidores) pode receber mais tráfego do que todos os outros objetos combinados. Em Redis, um hot key pode saturar uma única instância — porque Redis é single-threaded por chave, todas as requisições para aquela chave vão para o mesmo core de CPU de uma única shard.
A estimation padrão ("20% dos dados, 80% do tráfego") não captura esse comportamento. A estimation correta para distribuição extrema precisa modelar:
- QPS máximo esperado para um único objeto (não a média)
- Se esse QPS ultrapassa a capacidade de um único nó Redis (100k ops/s), é necessário local caching (cache em memória no próprio servidor de aplicação) para os hot keys
- Fan-out de writes: um tweet de celebridade sendo escrito para 100M timelines não pode ser tratado da mesma forma que um tweet comum
Twitter resolveu isso com tratamento híbrido: usuários com > X seguidores (celebrities) têm fan-out on read; usuários comuns têm fan-out on write. A estimation que revelou a necessidade desse tratamento híbrido foi exatamente calcular o custo de fan-out on write para um usuário com 100M seguidores: 1 tweet × 100M writes = 100M operações de cache update por tweet publicado.
Como você faz estimation para um sistema cujo padrão de uso você não conhece bem?
Quando o padrão de uso é incerto, a estimation precisa trabalhar com intervalos e cenários em vez de números pontuais:
1. Definir casos: conservador, esperado, e agressivo. Conservador: 10% da adoção projetada, comportamento mínimo por usuário. Esperado: projeção do produto. Agressivo: 10× o esperado (viral, promoção inesperada). O design precisa funcionar no caso agressivo sem ser over-engineered para ele no dia 1.
2. Identificar os pontos de inflexão: em que ponto cada componente do sistema precisaria mudar? "O banco aguenta até X QPS sem cache; com cache aguenta até Y; com read replicas aguenta até Z." Isso cria um mapa de quando cada upgrade seria necessário.
3. Projetar para a arquitetura que permite evolução: se não sei se vou precisar de sharding em 6 meses ou em 3 anos, a decisão de design correta é construir de forma que o sharding possa ser adicionado sem reescrita completa — mesmo que não seja adicionado agora. Isso é design para evolução, não over-engineering.
4. Medir rapidamente em produção: a incerteza de estimation diminui drasticamente com dados reais. Se você consegue lançar uma versão simplificada que gere dados de comportamento, a estimation do sistema completo pode ser feita com muito mais precisão — mesmo com uma amostra pequena de usuários.
Exercícios práticos
Sem consultar referências, estime de cabeça: (a) latência de leitura de RAM vs SSD NVMe vs HDD; (b) throughput de writes do PostgreSQL numa instância típica; (c) throughput de Redis para operações simples; (d) tamanho de um registro de usuário típico; (e) latência de rede entre duas regiões AWS em continentes diferentes. Depois consulte os números reais apresentados neste conceito e calcule sua margem de erro para cada um. Repita mensalmente até errar consistentemente por menos de 5× — não precisa ser exato, mas ordem de magnitude certa é obrigatório.
Critério: você consegue fazer a estimation de QPS, storage e banda de um sistema simples (pastebin ou URL shortener) completamente de cabeça em menos de 5 minutos, chegando a conclusões corretas sobre se cache é necessário e se sharding é necessário no horizonte de 5 anos.
Escolha um serviço que você mantém. Sem olhar métricas, estime: QPS de leitura e escrita no pico, storage total e crescimento mensal, hit rate do cache se houver, e capacidade utilizada do banco de dados principal. Depois, consulte as métricas reais (dashboards, CloudWatch, Datadog). Compare: onde sua intuição estava certa? Onde estava errada por quanto? Identifique qual componente está mais próximo do limite de capacidade e estime em quantos meses ele vai saturar no crescimento atual.
Critério: você identificou o componente com menor headroom no sistema, calculou quando ele vai saturar, e propôs uma ação preventiva com data de quando precisa ser executada para não entrar em crise.
Faça a estimation completa (QPS de leitura e escrita, storage em 5 anos, bandwidth, memória de cache) para três sistemas com escalas diferentes: (a) Instagram em 2011, com 1M DAU e foco em fotos; (b) Twitter atual, com 300M DAU e feed de texto; (c) WhatsApp, com 2B usuários e 100B mensagens/dia. Para cada um, indique quais decisões arquiteturais os números justificam: precisa de CDN? de sharding? de cache? de múltiplas regiões? Ao terminar, compare com o que você sabe sobre como esses sistemas foram de fato construídos.
Critério: para cada sistema, os números derivados levam às mesmas conclusões arquiteturais que a arquitetura real usa — não necessariamente o mesmo número exato, mas a mesma classe de solução (ex: "CDN obrigatório", "sharding por data", "cache essencial com hot key problem").
Construa uma planilha de capacity planning para o serviço mais crítico que você opera. Inputs: DAU atual, taxa de crescimento mensal, operações por usuário por dia, tamanho médio por objeto. Outputs calculados: QPS atual e em 6/12/18 meses, storage atual e projetado, bandwidth atual e projetado. Adicione linhas de threshold: em qual data o banco vai atingir 70% de capacidade de escrita? Em qual data o storage vai atingir 80%? Em qual data o cache vai precisar ser expandido? Compartilhe com o time e use para informar o roadmap de infraestrutura.
Critério: a planilha identifica pelo menos um componente que vai atingir seu threshold em menos de 12 meses no crescimento atual, e esse dado informa uma decisão concreta de infraestrutura (provisionar mais storage, adicionar read replica, expandir cluster de Redis).
Para o serviço do Exercício 2, valide a estimation com um load test real. Use k6, Locust, ou Artillery para simular o QPS estimado de pico. Meça: latência P50/P95/P99 sob carga, CPU e memória dos servidores de aplicação, QPS real do banco (com e sem cache), hit rate do cache. Compare os resultados com a estimation. Se divergirem significativamente (>2×), identifique a causa: a estimation estava errada (hipótese incorreta sobre comportamento de usuário) ou a implementação está ineficiente (queries lentas, cache miss alto)?
Critério: o load test foi executado até o ponto de saturação de pelo menos um componente — você sabe empiricamente qual componente falha primeiro e em que QPS. Esse dado atualiza o capacity plan com números reais em vez de estimados.
Referências
- article Jeff Dean & Peter Norvig — Latency Numbers Every Programmer Should Know
- article Colin Scott — Interactive Latencies
- book Alex Xu — System Design Interview vol. 1 — Chapter 2: Back-of-the-envelope Estimation
- book Martin Kleppmann — Designing Data-Intensive Applications — Chapter 1
- article Instagram Engineering — Billions of Messages
- article Discord Engineering — How Discord Scaled Elixir to 5,000,000 Concurrent Users
- article Cloudflare Blog — Counting Things at Scale
- docs AWS — Capacity Planning Best Practices
- docs PostgreSQL — Performance Tuning
- article Uber Engineering — Handling Millions of Rider Location Updates
- docs k6 — Load Testing Guide
- article ByteByteGo Newsletter — System Design Cheat Sheet