Método de System Design
Todo playbook de tópico aplica esta lente. Leia antes de qualquer design específico.
System design é uma cadeia de decisões sob restrição: o que ingerir, o que armazenar, o que computar, o que servir. Cada stage corta desperdício e preserva sinal. O melhor design escolhe bem o que fazer, e não faz mais do que isso.
O formato de qualquer sistema
Quase todo sistema não trivial tem quatro capacidades.
| Capacidade | Exemplo em search engine | Geral |
|---|---|---|
| Descobrir / ingerir | crawler | trazer dados para dentro (API, eventos, uploads, crawl) |
| Entender / modelar | parser, extrator | parsear, validar, enriquecer, normalizar |
| Organizar | inverted index | armazenar para query eficiente (index, schema, partição) |
| Servir | caminho de query | responder requisições dentro de um budget de latência |
Dois planos de apoio ficam ao lado delas. O plano de metadados e política guarda regras, agendamento, dedup, config e quotas. O plano de observabilidade e controle guarda métricas, debugging, replay, backfill e listas de permissão e bloqueio.
Os três pilares (Kleppmann, DDIA)
Avalie todo design contra reliability, scalability e maintainability. Eles são a espinha não funcional.
Reliability significa que o sistema funciona corretamente sob falha de hardware, bug de software e erro humano. Werner Vogels: "everything fails all the time". As ferramentas são os stability patterns de Nygard: timeouts, retries com backoff exponencial, circuit breakers, bulkheads, idempotência. Um sistema confiável assume que as dependências falham e degrada de propósito.
Scalability significa aguentar o crescimento da carga. Defina os parâmetros de carga primeiro (QPS, tamanho do payload, fan-out, razão leitura/escrita) e depois descreva a performance sob essa carga (p50, p95, p99, throughput). Prefira escala horizontal e particione por uma chave que evite hot spots. Uma afirmação de scalability é a resposta para "se a carga crescer X, qual é o plano".
Maintainability significa operável, simples e evolutivo. O A Philosophy of Software Design, de Ousterhout, pede módulos profundos: interfaces simples sobre implementação substancial. A complexidade se acumula em dependências e obscuridade. Observabilidade faz parte do design desde o início.
Números que todo engenheiro deveria saber (Jeff Dean)
Use estes números para contas de guardanapo. São ordens de grandeza.
| Operação | Tempo |
|---|---|
| Referência ao cache L1 | ~1 ns |
| Branch mispredict | ~5 ns |
| Referência à memória principal | ~100 ns |
| Comprimir 1 KB | ~2 us |
| Leitura aleatória em SSD | ~16 us |
| Ler 1 MB em sequência da memória | ~10 us |
| Ler 1 MB em sequência do SSD | ~50 us |
| Ida e volta dentro do mesmo datacenter | ~0.5 ms |
| Ler 1 MB em sequência do disco | ~5 ms |
| Seek de disco | ~2 ms |
| Ida e volta Califórnia para Holanda | ~150 ms |
Mostre sempre a conta de dimensionamento. Se você não consegue fazê-la, ainda não entende a escala.
QPS = DAU x actions/day / 86400
peak QPS = avg x (2 to 10)
storage = records x bytes/record x replication x retention
bandwidth = QPS x payload
O método de 13 stages
Um System Design Doc percorre estes stages, e o template espelha cada um.
| Stage | O que cobre |
|---|---|
| 1. Problema e contexto | Enquadramento em uma linha: quem, escala, interno ou web-scale. |
| 2. Requisitos | Funcionais, mais os não funcionais com números: SLO de latência, throughput, disponibilidade, modelo de consistência, teto de custo. |
| 3. Modelo mental | Fluxo de ponta a ponta como diagrama mermaid. Veja o caminho inteiro antes de qualquer componente. |
| 4. Arquitetura de alto nível | Componentes, mermaid e os dois planos de apoio. |
| 5. Deep dives | Os 2 ou 3 componentes que carregam o risco: estruturas de dados, algoritmo, trade-off difícil. É aqui que o trabalho de staff se separa do de senior. |
| 6. Dados e armazenamento | Separe stores por função e padrão de acesso: KV ou wide-column para updates aleatórios, object storage para blobs, índice de busca para texto, relacional para transações. Prefira imutabilidade (Helland: eventos em vez de estado mutável). |
| 7. Escala e particionamento | Sharding, replicação, rebalanceamento. Um dono por partição para coordenação. Workers sem estado fazem autoscale; os com estado precisam de liderança e réplicas. |
| 8. Consistência e falha | Escolha um modelo de consistência com honestidade (eventual serve se convergir). Liste os modos de falha, como o design resiste a cada um e o que quebra primeiro. |
| 9. Observabilidade e operação | Métricas por stage, mais as ferramentas de debugging que precisam existir: inspecionar o ciclo de vida de um registro, explicar o resultado de uma requisição. |
| 10. Segurança e compliance | Guarde o mínimo de dados, isole entrada não confiável em sandbox, sanitize o parsing, respeite políticas externas. |
| 11. Plano incremental | Fatia vertical primeiro, em escopo pequeno com engines comprovadas, depois eficiência e qualidade, depois escala real, depois o avançado. |
| 12. Trade-offs | Cobertura vs qualidade, frescor vs custo, recall vs latência, complexidade vs velocidade de entrega, centralizar vs particionar. |
| 13. Perguntas em aberto | O que um revisor de design deveria interrogar. |
Disciplina de trade-off
Nunca apresente uma opção como óbvia. Nomeie a alternativa, o eixo e a escolha:
Escolhi X em vez de Y porque {eixo} pesa mais aqui, dado {restrição}.
Um design sem trade-off declarado escondeu um.
Construa com pragmatismo
Use uma engine comprovada (Lucene, Postgres, Kafka, uma fila gerenciada) na camada cheia de detalhes traiçoeiros, e gaste seus trimestres no pipeline e na lógica que são o seu diferencial. Reinvente só a parte que é o produto.
Consistência, tempo e ordenação
O Time, Clocks, and the Ordering of Events in a Distributed System (1978), de Lamport, é a raiz do raciocínio distribuído. Um sistema distribuído não tem um "agora" global único, então você raciocina sobre ordenação causal em vez de ordenação por relógio de parede. Isso sustenta a consistência eventual e os vector clocks, e é por isso que confiar em timestamps é uma armadilha (clock skew).
O Life Beyond Distributed Transactions, de Helland, tira a conclusão prática. Em escala você abre mão de ACID entre entidades e projeta em torno de unidades independentes e idempotentes que se reconciliam com o tempo.
CAP e PACELC
Sob uma partição de rede (P) você escolhe disponibilidade (A) ou consistência (C): o CAP de Brewer. O PACELC acrescenta que, caso contrário (E), sem partição, você troca latência (L) por consistência (C). A maioria dos designs reais é AP sob partição e troca latência por consistência na operação normal. Diga em qual canto você está e por quê, por operação e não por sistema.
O cânone
Cite estes quando deixarem um ponto mais preciso.
| Pessoa | Ideia | Fonte |
|---|---|---|
| Martin Kleppmann | reliability/scalability/maintainability; parâmetros de carga antes de performance | DDIA (2017) |
| Jeff Dean, Sanjay Ghemawat | números que todo mundo sabe; batch no formato MapReduce | LADIS 2009; OSDI 2004 |
| Werner Vogels | projetar para a falha; consistência eventual em escala | Dynamo (SOSP 2007) |
| Pat Helland | imutabilidade; a vida além das transações distribuídas | CIDR 2007; 2015 |
| Michael Nygard | circuit breaker, bulkhead, timeout, backoff | Release It! (2018) |
| John Ousterhout | módulos profundos, interfaces simples | PoSD (2018) |
| Leslie Lamport | ordenação causal, sem relógio global | CACM 1978 |
| Eric Brewer | teorema CAP | PODC 2000 |
| Sam Newman | fronteiras de serviço seguindo a capacidade de negócio | Building Microservices |
| Gregor Hohpe | o elevador do arquiteto: ligar o trade-off da sala de máquinas ao interesse do negócio | 2020 |
A conexão com o harness
O agent system-architect é ele mesmo um harness (Böckeler/Fowler): guides são feedforward, sensors e evals são feedback, e os humanos ficam sobre o loop. Um design precisa da mesma divisão. Controles computacionais (testes, linters, checagem de schema) e controles inferenciais (revisão semântica) rodam juntos. Veja Harness Engineering e Evals.
Referências
| Autor | Obra | Publicação |
|---|---|---|
| Martin Kleppmann | Designing Data-Intensive Applications | O'Reilly, 2017 |
| Jeff Dean | Designs, Lessons and Advice from Building Large Distributed Systems | LADIS, 2009 |
| Dean, Ghemawat | MapReduce: Simplified Data Processing on Large Clusters | OSDI, 2004 |
| DeCandia et al. | Dynamo: Amazon's Highly Available Key-value Store | SOSP, 2007 |
| Pat Helland | Life Beyond Distributed Transactions | CIDR, 2007 |
| Pat Helland | Immutability Changes Everything | ACM Queue, 2015 |
| Michael Nygard | Release It!, 2nd ed. | Pragmatic Bookshelf, 2018 |
| John Ousterhout | A Philosophy of Software Design | 2018 |
| Leslie Lamport | Time, Clocks, and the Ordering of Events in a Distributed System | CACM, 1978 |
| Eric Brewer | Towards Robust Distributed Systems (keynote do CAP) | PODC, 2000 |
| Birgitta Böckeler | Harness engineering for coding agent users | martinfowler.com, 2026 |
Tradução manual de Método de System Design, a página original em inglês na wiki.