Quando começamos a desenvolver uma aplicação, muitas vezes a arquitetura parece simples:
Usuário → Aplicação → Banco de Dados
E, para muitos sistemas, isso é suficiente.
Mas imagine que essa aplicação cresce.
Primeiro chegam 100 usuários.
Depois 10 mil.
Depois 1 milhão.
O banco começa a receber milhares de consultas por segundo. Algumas operações ficam lentas. Um servidor já não consegue processar todo o tráfego. Um serviço externo fica indisponível e começa a derrubar requisições.
Agora surgem novas perguntas:
- Precisamos de mais servidores?
- Como distribuir as requisições entre eles?
- Devemos adicionar cache?
- Precisamos processar algumas operações de forma assíncrona?
- O que acontece se o banco ficar indisponível?
- Podemos perder uma mensagem?
- Quanto tempo uma resposta pode levar?
- Precisamos armazenar todos esses dados?
- O sistema precisa continuar funcionando durante uma falha?
Responder perguntas como essas faz parte de System Design.
O que é System Design?
System Design é o processo de definir como os diferentes componentes de um sistema de software trabalham juntos para atender determinados requisitos.
Isso inclui decisões sobre:
- Aplicações e serviços;
- APIs;
- Bancos de dados;
- Cache;
- Filas e eventos;
- Armazenamento;
- Balanceamento de carga (Load Balancer);
- Comunicação entre serviços;
- Escalabilidade;
- Disponibilidade;
- Consistência;
- Segurança;
- Observabilidade.
Mas System Design não significa simplesmente adicionar todas essas tecnologias a uma arquitetura.
Na verdade, uma das habilidades mais importantes em System Design é saber quando não adicionar complexidade.
Não comece pela tecnologia
Um erro comum ao aprender System Design é pensar primeiro nas ferramentas:
"Vou usar Kubernetes, Kafka, Redis, PostgreSQL e Elasticsearch."
Mas ainda não sabemos qual problema estamos tentando resolver.
Imagine que precisamos construir um sistema que recebe pedidos de uma pequena loja online.
Talvez isso seja suficiente:
┌─────────────┐
Usuário ──→ │ API │
└──────┬──────┘
│
▼
┌─────────────┐
│ PostgreSQL │
└─────────────┘
Não existe motivo para adicionar Kafka, Redis ou dezenas de microservices apenas porque essas tecnologias aparecem em arquiteturas de grandes empresas.
Agora imagine que os requisitos mudam.
A plataforma passa a processar milhões de pedidos e, depois que um pedido é criado, diferentes operações precisam acontecer:
Criar pedido
│
├── Reservar estoque
├── Processar pagamento
├── Enviar e-mail
└── Atualizar analytics
Talvez algumas dessas operações não precisem acontecer durante a requisição do usuário.
Nesse cenário, processamento assíncrono pode começar a fazer sentido:
┌──────────────┐
Usuário ──→ API ──→ │ Pedidos │
└──────┬───────┘
│
▼
┌──────────┐
│ Eventos │
└────┬─────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
Estoque Pagamento Notificação
Perceba a diferença.
Não começamos dizendo:
"Precisamos de Kafka."
Começamos dizendo:
"Temos operações que podem acontecer de forma assíncrona, queremos desacoplar os consumidores e precisamos absorver picos de processamento."
Agora existe um problema que pode justificar o uso de uma fila ou plataforma de eventos.
Esse raciocínio é a essência de System Design.
Requisitos funcionais e não funcionais
Antes de desenhar uma arquitetura, precisamos entender o que estamos construindo.
Normalmente podemos dividir os requisitos em dois grupos.
Requisitos funcionais
Descrevem o que o sistema deve fazer.
Em um encurtador de URLs, por exemplo:
- Criar uma URL curta;
- Redirecionar uma URL curta para a URL original;
- Permitir que um link expire.
Em um aplicativo de mensagens:
- Enviar mensagens;
- Receber mensagens;
- Mostrar conversas;
- Indicar mensagens lidas.
Requisitos não funcionais
Descrevem como o sistema deve se comportar.
Por exemplo:
- Suportar 100 mil requisições por segundo;
- Responder em menos de 200 ms;
- Permanecer disponível mesmo se um servidor falhar;
- Não perder mensagens;
- Permitir consistência eventual em determinadas operações.
Esses requisitos frequentemente têm impacto muito maior na arquitetura do que parece.
Considere dois sistemas que fazem exatamente a mesma coisa:
Sistema A
10.000 usuários
100 req/s
Sistema B
100 milhões de usuários
500.000 req/s
Os requisitos funcionais podem ser idênticos.
A arquitetura provavelmente não será.
Escala muda a arquitetura
Vamos voltar ao nosso sistema simples:

Usuário → API → Banco
Ele pode funcionar perfeitamente durante muito tempo.
Até que a aplicação cresça.
Problema 1: um servidor não suporta mais o tráfego
Podemos executar várias instâncias da aplicação e distribuir as requisições entre elas:
┌──→ API 1
Usuário → Load Balancer ─→ API 2
└──→ API 3
Agora conseguimos aumentar a capacidade horizontalmente.
Mas surge outra pergunta:
O banco consegue acompanhar esse crescimento?
Problema 2: muitas leituras no banco
Imagine que milhares de usuários consultem repetidamente os mesmos dados.
Talvez possamos armazenar temporariamente os dados mais acessados:
Usuário
│
▼
API
│
├────→ Cache
│ │
│ └── cache hit → resposta
│
└────→ Banco
cache miss
Isso pode diminuir a latência e reduzir a carga no banco.
Mas acabamos de criar outro problema:
Como manter o Cache atualizado?
Esse é um exemplo clássico de trade-off.
Toda decisão tem um custo
Não existe arquitetura perfeita.
Existem arquiteturas adequadas para determinados requisitos.
Adicionar cache pode reduzir latência e carga no banco, mas introduz problemas de invalidação e dados desatualizados.
Adicionar réplicas pode aumentar disponibilidade e capacidade de leitura, mas pode introduzir atraso de replicação.
Processamento assíncrono pode absorver picos e desacoplar serviços, mas introduz questões como retries, mensagens duplicadas e consistência eventual.
Particionar dados pode permitir que o sistema cresça além da capacidade de uma única máquina, mas aumenta significativamente a complexidade de consultas e operações.
Por isso, uma pergunta muito importante em System Design é:
O que estamos ganhando e o que estamos sacrificando com essa decisão?
Esse é o conceito de trade-off.
Alguns trade-offs aparecem o tempo todo
Durante o estudo de System Design você encontrará várias decisões recorrentes:
Consistência ↔ Disponibilidade
Latência ↔ Consistência
Performance ↔ Complexidade
Custo ↔ Redundância
Síncrono ↔ Assíncrono
SQL ↔ NoSQL
Escala vertical ↔ Escala horizontal
Nenhum lado é automaticamente melhor.
A escolha depende dos requisitos.
Um sistema financeiro pode exigir consistência forte para determinadas operações.
Um feed de rede social talvez aceite que uma publicação demore alguns segundos para aparecer para todos os usuários.
São problemas diferentes.
Logo, podem exigir decisões diferentes.
Os principais blocos de um sistema
Apesar de existirem milhares de tecnologias, grande parte das arquiteturas modernas é construída combinando alguns blocos fundamentais:
| Componente | Responsabilidade |
|---|---|
| Client | Aplicação web, mobile ou outro serviço |
| API | Interface de comunicação com o sistema |
| Load Balancer | Distribuir tráfego entre servidores |
| Application Server | Executar regras de negócio |
| Database | Persistir dados |
| Cache | Acelerar acessos frequentes |
| Message Queue / Event Stream | Processar trabalho de forma assíncrona |
| Object Storage | Armazenar arquivos e grandes objetos |
| CDN | Distribuir conteúdo próximo dos usuários |
| Observability | Logs, métricas e traces |
Aprender System Design envolve entender cada um desses componentes.
Mas existe uma pergunta ainda mais importante:
qual problema cada componente resolve?
Por exemplo:
"Redis deixa o sistema escalável."
não explica muita coisa.
Uma justificativa melhor seria:
Temos um endpoint com grande volume de leitura
e dados que mudam poucas vezes.
Adicionar cache pode reduzir a quantidade de consultas
ao banco e diminuir a latência desse endpoint.
Agora existe uma relação clara:
Requisito → Problema → Decisão → Trade-off
Esse modelo mental é extremamente útil para estudar System Design.
Como abordar um problema de System Design
Não existe um único processo obrigatório, mas uma sequência simples ajuda bastante.
1. Entenda o problema
Antes de desenhar qualquer arquitetura, faça perguntas.
Por exemplo, se pedirem:
"Projete um encurtador de URLs."
Ainda existem muitas coisas que precisamos saber.
-
Quantos usuários teremos?
-
Quantas URLs serão criadas por dia?
-
Quantos redirecionamentos acontecerão?
-
Links podem expirar?
-
Precisamos de analytics?
-
Qual latência esperamos no redirecionamento?
2. Faça estimativas
Não precisamos calcular tudo com precisão absoluta.
Queremos entender a ordem de grandeza.
Por exemplo:
100 milhões de redirecionamentos por dia
Dividindo aproximadamente pelo número de segundos de um dia:
100.000.000 / 86.400 ≈ 1.157 req/s
Mas tráfego raramente é uniforme.
Se considerarmos um pico de 5x:
≈ 5.800 req/s
Isso já nos ajuda a pensar sobre capacidade, cache, banco de dados e escalabilidade.
O objetivo não é acertar exatamente quantas requisições acontecerão às 14:37 de uma terça-feira.
É entender a dimensão do problema.
3. Desenhe primeiro a solução mais simples
Comece com:
Client
│
▼
API
│
▼
Database
Depois pergunte:
Onde essa arquitetura quebra considerando nossos requisitos?
Talvez o banco receba leituras demais.
Adicionamos cache.
Talvez uma instância da aplicação não seja suficiente.
Adicionamos múltiplas instâncias e um load balancer.
Talvez determinado processamento seja lento e não precise bloquear a requisição.
Adicionamos processamento assíncrono.
A arquitetura cresce porque os requisitos exigem — não porque queremos deixar o diagrama mais sofisticado.
4. Identifique gargalos e falhas
Depois de desenhar o fluxo principal, comece a quebrar mentalmente o sistema.
Pergunte:
- E se uma instância cair?
- E se o banco ficar indisponível?
- E se o cache estiver vazio?
- E se recebermos 10x mais tráfego?
- E se uma mensagem for processada duas vezes?
- E se um serviço externo levar 30 segundos para responder?
- E se um consumidor parar de processar eventos?
Sistemas reais falham.
Um bom design considera como o sistema se comporta quando isso acontece.
5. Discuta os trade-offs
Finalmente, explique suas decisões.
Em vez de:
"Vou usar PostgreSQL."
Explique:
"Os dados possuem relacionamentos claros e algumas operações exigem transações. Por isso, começaria com um banco relacional como PostgreSQL. Se o volume crescer além da capacidade de uma única instância, podemos avaliar replicação, particionamento ou outras estratégias."
A tecnologia aparece depois do raciocínio.
System Design não é apenas para sistemas gigantes
Existe uma ideia equivocada de que System Design só importa quando estamos construindo algo do tamanho de Netflix, Uber ou WhatsApp.
Não é verdade.
Mesmo sistemas relativamente pequenos precisam responder perguntas arquiteturais.
Por exemplo:
-
Precisamos realmente de microservices?
-
Vale adicionar Redis?
-
Essa operação deveria ser síncrona?
-
Como evitamos processar essa mensagem duas vezes?
-
O que acontece quando esse serviço externo falhar?
-
Precisamos de consistência forte aqui?
-
Como vamos descobrir por que uma requisição ficou lenta?
Tudo isso é System Design.
Na verdade, uma boa decisão arquitetural muitas vezes é não construir uma arquitetura distribuída quando ela ainda não é necessária.
Um monólito com PostgreSQL pode ser uma excelente arquitetura.
A pergunta não é:
"Essa arquitetura parece sofisticada?"
A pergunta é:
"Essa arquitetura atende aos requisitos com uma complexidade razoável?"
System Design em entrevistas
System Design também aparece com frequência em processos seletivos para engenheiros mais experientes.
Uma pergunta típica poderia ser:
"Desenhe um Encurtador de URLs"
ou:
"Desenhe o WhatsApp."
ou:
"Desenhe um Sistema de Notificação."
O objetivo normalmente não é descobrir se você memorizou a arquitetura usada por essas empresas.
O entrevistador quer observar como você pensa.
Você consegue:
- Descobrir requisitos?
- Fazer estimativas?
- Identificar gargalos?
- Decompor o problema?
- Justificar suas escolhas?
- Reconhecer riscos?
- Discutir alternativas?
- Explicar trade-offs?
- Adaptar a arquitetura quando os requisitos mudam?
Por isso, duas pessoas podem propor arquiteturas diferentes e ambas apresentarem boas soluções.
O importante é conseguir justificar as decisões.
System Design na vida real
Existe uma diferença importante entre entrevistas e sistemas reais.
Em entrevistas, normalmente começamos com uma folha em branco.
Na vida real, quase nunca.
Você encontrará:
Código legado
+
Banco existente
+
Sistemas externos
+
Restrições de orçamento
+
Times diferentes
+
Débito técnico
+
Requisitos de segurança
+
Prazos
Por isso, System Design não termina quando desenhamos um diagrama.
Sistemas evoluem.
Uma arquitetura pode funcionar perfeitamente para 10 mil usuários e precisar mudar quando chegar a 1 milhão.
O processo é iterativo:
Projetar
↓
Construir
↓
Medir
↓
Encontrar gargalos
↓
Evoluir
↓
Medir novamente
Esse ciclo faz parte da engenharia de software.
Então, o que precisamos aprender?
System Design pode parecer enorme quando encontramos termos como:
Load Balancer
Cache
Replicas(Replication)
Fragmentação (Sharding)
Particionamento (Partitioning)
CDN
Filas (Queues)
Streaming de eventos (Event Streaming)
Teorema CAP (CAP Theorem)
Hashing consistente (Consistent Hashing)
Rate Limiter
Circuit Breaker
Idempotência (Idempotency)
Consistência Eventual (Eventual Consistency)
Mas você não precisa aprender tudo de uma vez.
Grande parte de System Design consiste em aprender alguns blocos fundamentais e entender:
- Qual problema eles resolvem;
- Quando devemos utilizá-los;
- Quais problemas novos eles introduzem.
É exatamente essa abordagem que seguiremos nesta trilha.
O modelo mental mais importante
Se você lembrar de apenas uma coisa deste artigo, lembre-se desta sequência:
Requisitos
↓
Escala
↓
Problemas
↓
Decisões arquiteturais
↓
Trade-offs
Não comece perguntando:
"Qual tecnologia devo usar?"
Comece perguntando:
"Qual problema preciso resolver?"
Depois:
"Quais requisitos realmente importam?"
Depois:
"Onde a solução mais simples deixa de funcionar?"
E somente então:
"Qual componente ou tecnologia pode resolver esse problema?"
System Design, no fim das contas, é isso: transformar requisitos e restrições em decisões técnicas conscientes.
E aprender System Design significa desenvolver a capacidade de explicar não apenas como construir um sistema, mas principalmente por que ele deveria ser construído daquela maneira.