Como Estruturar um System Design
Depois de estudar CDN, load balancer, cache, filas, réplicas e todos os outros componentes individualmente, falta a peça final: uma ordem de raciocínio para juntar tudo isso na hora de projetar um sistema do zero, seja numa entrevista, seja num projeto real. Esta nota reúne essa metodologia, em nove etapas, e um checklist de cenários de falha para testar o design no final. Para um mapa de todos os tópicos que costumam aparecer num design, veja o Roadmap de System Design.
Etapa 1: Requisitos
Seção intitulada “Etapa 1: Requisitos”Antes de desenhar qualquer componente, alinhe o que o sistema precisa fazer e sob quais condições (veja System Design: Fundamentos):
- Requisitos funcionais: o que o sistema faz, do ponto de vista de quem usa
- Requisitos não funcionais: escala esperada, disponibilidade exigida, latência aceitável
Pular essa etapa é o erro mais comum em entrevistas de system design: desenhar uma arquitetura elaborada para um problema que na verdade tinha requisitos simples, ou o oposto, subestimar a escala e propor algo que não aguentaria o tráfego real.
Etapa 2: Capacity Planning
Seção intitulada “Etapa 2: Capacity Planning”Transforme os requisitos não funcionais em números concretos, usando o método de Capacity Planning: DAU, requests por usuário, QPS médio e de pico, estimativa de storage e de bandwidth. Esses números são o que justifica cada decisão das próximas etapas, “precisa escalar” não diz nada, “precisa aguentar 20 mil QPS de pico” diz tudo.
Etapa 3: Arquitetura inicial
Seção intitulada “Etapa 3: Arquitetura inicial”Desenhe o esqueleto: cliente, CDN, load balancer, API Gateway, os serviços que compõem o sistema, cache, filas e banco de dados. A nota de Arquitetura de uma Aplicação Distribuída mostra esse esqueleto de referência, componente por componente.
Nem todo sistema precisa de todas as peças desse esqueleto desde o início, um sistema pequeno pode começar só com cliente, um serviço e um banco. A etapa 2 (capacity planning) é o que diz quais peças já se justificam desde a primeira versão.
Defina as APIs antes de escolher tecnologia
Seção intitulada “Defina as APIs antes de escolher tecnologia”Antes de decidir banco, cache ou fila, liste as operações centrais que o sistema oferece. Num encurtador de URL, são duas: criar uma URL curta (POST /urls) e redirecionar (GET /{shortCode}). Só de escrever isso já aparece informação útil: uma operação é escrita e a outra é leitura, e a segunda vai ser chamada muito mais vezes que a primeira. A API mostra o que o sistema realmente precisa suportar, e evita infraestrutura para algo que ninguém vai chamar. Como definir contratos, códigos de resposta e versionamento está em APIs.
Com a API na mão, resista à vontade de desenhar tudo de uma vez. Comece pela arquitetura mais simples que atende aos requisitos (cliente, load balancer, servidor de aplicação e banco) e faça uma pergunta: onde isso quebra quando o tráfego crescer? É dessa resposta que saem as decisões de escalabilidade da etapa 4, uma de cada vez, cada uma com um motivo. O contrário (começar por Redis, Kafka e Kubernetes) tende a produzir um desenho cheio de peças que ninguém sabe justificar.
Um exemplo completo desse raciocínio, com números, API, modelo de dados e evolução da arquitetura, está no estudo de caso Encurtador de URL.
Etapa 4: Escalabilidade
Seção intitulada “Etapa 4: Escalabilidade”Com o esqueleto no lugar e os números da etapa 2 em mãos, decida como cada parte escala: horizontal scaling atrás do load balancer, replicação e sharding no banco, cache para tirar carga de leitura do banco, filas para absorver picos de escrita. Toda essa camada está detalhada em Escalabilidade e Infraestrutura.
Etapa 5: Confiabilidade
Seção intitulada “Etapa 5: Confiabilidade”Um sistema que escala mas cai na primeira falha de rede não está pronto. Aplique os padrões de Resiliência: retry com backoff, timeout em toda chamada entre serviços, circuit breaker nas dependências instáveis, idempotência em qualquer operação que pode ser reenviada, e dead letter queue para mensagens que falham repetidamente.
Etapa 6: Dados
Seção intitulada “Etapa 6: Dados”Volte para a camada de dados com mais detalhe: SQL ou NoSQL (veja NoSQL), como modelar as entidades, quais índices o padrão de consulta exige, como a replicação está configurada e qual nível de consistência cada tipo de dado exige (veja Consistência e Replicação).
Etapa 7: Performance
Seção intitulada “Etapa 7: Performance”Revise o orçamento de latência do caminho crítico (veja Latência, Throughput e Performance): qual o p99 esperado, onde o cache reduz esse número, o que pode sair do caminho síncrono e virar processamento assíncrono via fila.
Etapa 8: Observabilidade
Seção intitulada “Etapa 8: Observabilidade”Um sistema em produção sem observabilidade é uma caixa preta. Defina que logs, métricas e traces (veja Observabilidade) o sistema vai expor, quais alertas disparam em cima dessas métricas, e quais SLOs (veja Disponibilidade) definem “o sistema está saudável”.
Etapa 9: Failure Scenarios
Seção intitulada “Etapa 9: Failure Scenarios”A etapa final é atacar o próprio design com perguntas de “e se”. Um design só está completo depois de responder, para cada componente crítico, o que acontece quando ele falha:
- E se o banco cair?
- E se o Redis cair?
- E se o Kafka ficar indisponível?
- E se uma instância morrer?
- E se houver um pico de 10x no tráfego?
- E se houver uma partição de rede entre dois serviços?
- E se uma mensagem for processada duas vezes?
- E se uma requisição ficar lenta, sem chegar a falhar de vez?
- E se uma região inteira cair?
Não existe sistema imune a todos esses cenários ao mesmo tempo, o objetivo não é ter resposta perfeita para cada um, é mostrar que cada cenário foi considerado conscientemente, e que a resposta (“o sistema degrada graciosamente”, “perdemos disponibilidade mas não dados”, “esse cenário é aceitável dado o SLA”) foi uma escolha, não um ponto cego.
Catálogo de padrões para sistemas distribuídos
Seção intitulada “Catálogo de padrões para sistemas distribuídos”Ao longo de todas as notas de system design deste lab, um conjunto recorrente de padrões aparece repetidas vezes, atacando os mesmos problemas de formas diferentes. Em vez de repetir a explicação de cada um aqui, esta lista serve como índice de onde cada padrão é coberto em profundidade:
| Padrão | Onde é coberto |
|---|---|
| Retry, Timeout, Circuit Breaker, Bulkhead | Timeout, Retry, Circuit Breaker e Bulkhead |
| Idempotency | Idempotência |
| Rate Limiting, Backpressure | Rate Limiting |
| Cache | Cache e Redis |
| Queue, Pub/Sub | Filas e Mensageria |
| Replication | Replicação e Escalabilidade do Banco de Dados e Consistência e Replicação |
| Sharding | Stateless, Particionamento e Sharding |
| Load Balancing | Load Balancer |
| Health Check | Load Balancer (seção Health Checks) |
| Failover | Load Balancer e Disponibilidade |
Uma boa resposta de system design, na prática, é reconhecer qual desses padrões resolve qual dos “e se” da etapa anterior, e justificar por que ele se encaixa (ou não vale a complexidade) para o problema específico em mãos.