Em uma entrevista de design de sistemas ou em um documento de design, é no diagrama que o raciocínio fica visível. Uma caixa com o rótulo "cache" é fácil de desenhar; o que faz o diagrama valer a leitura é mostrar por que ela está ali, quanto tráfego absorve e o que acontece quando o cache está frio.
Este guia percorre um exercício clássico, um encurtador de URL, dos requisitos até um diagrama de design de sistemas pronto. O foco não é o encurtador em si, mas o método: primeiro os requisitos e os números, depois a API e os dados, depois as caixas, com cada decisão escrita onde todo mundo possa ver.
Passo 1: requisitos
Funcionais:
- Criar um link curto para uma URL longa, opcionalmente com um alias personalizado e uma data de expiração.
- Redirecionar um link curto para a URL longa correspondente.
- Contar cliques por link.
Não funcionais:
- Os redirecionamentos precisam ser rápidos e altamente disponíveis; a criação de links pode tolerar mais latência.
- O sistema é dominado por leituras: links são criados uma vez e clicados muitas vezes.
- Códigos curtos não devem ser fáceis de enumerar.
Passo 2: contas de guardanapo
Suponha 100 milhões de links novos por mês e 100 redirecionamentos por link criado:
| Grandeza | Estimativa |
|---|---|
| Escritas | 100M / 2,6M segundos por mês ≈ 40 por segundo em média |
| Leituras | 100 × 40 ≈ 4.000 redirecionamentos por segundo; dimensione para picos de 10×, 40.000 por segundo |
| Links após 5 anos | 100M × 12 × 5 = 6 bilhões |
| Armazenamento | 6 bilhões × ~500 bytes ≈ 3 TB, sem contar réplicas |
| Tamanho do código | 7 caracteres em base62 dão 62⁷ ≈ 3,5 trilhões de códigos |
Os números já moldam o design: as leituras dominam, então o caminho de leitura ganha um cache; 3 TB não vão caber para sempre em um único banco de dados pequeno, então o modelo de dados precisa permitir um sharding limpo; e 7 caracteres deixam bastante folga.
Passo 3: API
POST /api/links
{ "url": "https://example.com/a/very/long/path", "alias": "launch", "expiresAt": "2027-01-01" }
→ 201 { "code": "launch", "shortUrl": "https://s.example/launch" }
GET /{code}
→ 302 Location: https://example.com/a/very/long/path
Uma decisão merece uma linha no diagrama: 301 ou 302? Um 301 é permanente, então os navegadores o guardam em cache e as visitas repetidas nunca chegam até você. Isso economiza tráfego, mas você perde a contagem de cliques. Um 302 faz cada clique passar pelo serviço de redirecionamento, que é o que o analytics precisa. Este design usa 302.
Passo 4: modelo de dados e IDs
A tabela principal é simples: code (chave primária), long_url, owner_id, created_at, expires_at. Toda leitura é uma busca por code, então tanto um armazenamento chave-valor quanto um banco relacional com chave primária funcionam, e fazer sharding por um hash de code distribui a carga por igual.
Gerar os códigos é a parte interessante:
| Abordagem | Prós | Contras |
|---|---|---|
| Contador + base62 | Sem colisões | Códigos sequenciais são fáceis de enumerar; exige coordenação |
| 7 caracteres aleatórios | Difíceis de adivinhar, sem coordenação | É preciso checar colisões na inserção |
| Hash da URL | A mesma URL recebe o mesmo código | Colisões ao truncar; aliases exigem tratamento especial |
Aqui, a API de escrita reserva faixas de IDs em um alocador de faixas de IDs, por exemplo um milhão de cada vez, e embaralha cada ID com uma permutação reversível antes da codificação em base62. Assim, os códigos são únicos sem verificação de colisão e não parecem sequenciais para quem vê de fora.
Passo 5: o diagrama
Separe os caminhos que têm requisitos diferentes. O caminho de escrita tem pouco volume e precisa estar correto; o de leitura tem muito volume e precisa ser rápido; o analytics pode ficar para trás.
Conteúdo do diagrama
- Users
- Load balancer
- ID range allocator
- Write API
- Redirect service
- Cache: code → URL
- Links DB (sharded by code)
- click events
- Analytics workers
- Analytics store
- HTTPS
- POST · 40/s
- GET · 4k/s, peak 40k/s
- lease ID range
- INSERT link
- GET code
- on cache miss
- click event
- consume
- counts
Como ler:
- Caminho de escrita (em cima). O balanceador de carga encaminha
POST /api/linkspara a API de escrita, que pega um ID da sua faixa reservada, codifica esse ID e grava o link no banco de dados. - Caminho de leitura (no meio).
GET /{code}vai para o serviço de redirecionamento, que consulta o Redis primeiro. Se os dados mais acessados cabem na memória, a maioria dos redirecionamentos nunca toca o banco. Em um cache miss, o serviço lê o banco de dados e preenche o cache. - Analytics (embaixo). Cada redirecionamento emite um evento de clique para o Kafka, e workers de analytics agregam as contagens no armazenamento de analytics. O redirecionamento nunca espera pelo analytics.
Desenhe de um jeito que o raciocínio apareça
- Coloque números nas conexões.
40/sna conexão de escrita e4k/s, peak 40k/sna de leitura explicam o cache melhor do que qualquer rótulo. - Escreva as premissas ao lado das caixas. "Meta de taxa de acerto do cache: 90%", "302 para o analytics", "IDs reservados em faixas de 1M".
- Separe o síncrono do assíncrono. Conexões contínuas para o caminho da requisição, tracejadas para os eventos de clique.
- Mostre o que acontece quando algo falha. Se o Redis cair, os redirecionamentos recorrem ao banco de dados, com latência maior; escreva isso no diagrama.
- Mantenha as estimativas no canvas. Uma pequena tabela de números ao lado do diagrama transforma tudo em um documento de design.
Aprofundamentos que vale a pena preparar
- Cache. Os links mais acessados seguem uma distribuição de cauda longa, então um cache LRU com um TTL modesto cobre a maior parte do tráfego. Invalide na exclusão e na expiração.
- Escala do banco de dados. Réplicas de leitura ajudam menos aqui do que o cache; o sharding por código faz escritas e armazenamento crescerem de forma equilibrada.
- Expiração. Verifique
expires_atna leitura e faça a limpeza em segundo plano, em vez de rodar um job gigante de exclusão. - Abuso. Aplique rate limit à criação de links por usuário e verifique as URLs de destino em listas de malware.
Monte no Archboard
- Entre com Google ou GitHub e crie um diagrama.
- Adicione um nó Internet do pack Rede para os usuários e um Balanceador de carga na nuvem do pack Nuvem para o balanceamento de carga.
- Adicione a API de escrita, o serviço de redirecionamento, o alocador de IDs e os workers de analytics como nós Contêiner do pack Modelo C4, com a linguagem no campo Tecnologia.
- Adicione do pack Dados um Redis para o cache, um Postgres para o banco de links, um Tópico para os eventos de clique e um Armazém de dados para o analytics.
- Rotule as conexões com operações e taxas, deixe os eventos de clique tracejados e coloque as estimativas em um bloco Markdown do pack Básico.
O exemplo usa 11 nós de diagrama, incluindo o bloco Markdown com as estimativas, e fica dentro do plano Free. Para o método geral por trás de qualquer diagrama de arquitetura, veja como desenhar um diagrama de arquitetura de software.
Perguntas frequentes
Quão detalhado deve ser um diagrama de design de sistemas em uma entrevista? O suficiente para mostrar os caminhos de leitura e escrita, o armazenamento de dados, o cache e uma peça assíncrona. Só aprofunde onde o entrevistador pedir.
SQL ou NoSQL para a tabela de links? Os dois funcionam para uma busca por chave primária. Escolha com base em como você pretende fazer sharding e no que o seu time opera bem, e escreva o motivo no diagrama.
Devo desenhar uma CDN? Só se você fizer cache dos redirecionamentos na borda. Com 302 e contagem de cliques, o cache na borda é limitado, então muitos designs deixam a CDN de fora.
Por que não usar 301 em tudo? Um 301 economiza tráfego, mas os navegadores param de consultar você; assim, você perde dados de cliques e não consegue mudar o destino para quem volta.
Onde ficam os números? No canvas, ao lado do diagrama, para que qualquer pessoa que revise o design possa conferir as premissas por trás das caixas.