0Pricing
DSA Interview Prep · Aula

A estrutura da entrevista de projeto de sistemas

Percorra a estrutura RADIO em cinco etapas (Requisitos, API, Dados, Infraestrutura e Otimização) e pratique sua aplicação a um encurtador de URLs.

A estrutura da entrevista de projeto de sistemas é uma aula grátis de DSA Interview Prep no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de DSA Interview Prep, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de DSA Interview Prep inclui 4 aulas no total.

Por que a Arquitetura de Sistemas é Importante em Entrevistas

Entrevistas de arquitetura de sistemas avaliam sua capacidade de pensar em grande escala — como você projetaria o Twitter, o YouTube ou um encurtador de URLs para bilhões de usuários? Ao contrário dos problemas de programação, que têm uma única resposta correta, a arquitetura de sistemas é aberta: você precisa fazer escolhas e justificar as compensações. Cargos sênior e de liderança técnica dedicam exclusivamente 30–45 minutos a essa etapa.

Os entrevistadores avaliam se você consegue esclarecer requisitos, estimar a carga, propor uma arquitetura de alto nível, aprofundar-se nos componentes principais e discutir compensações — tudo isso enquanto se comunica com clareza. Uma estrutura organizada evita que você divague e garante que todas as dimensões críticas sejam abordadas.

# System design is not about a single correct answer.
# Interviewers look for:
eval_criteria = [
    'Ability to clarify requirements before designing',
    'Back-of-envelope capacity estimation',
    'High-level architecture with clear components',
    'Data modelling and storage choice',
    'Handling scalability (10x, 100x load)',
    'Trade-off discussion (consistency vs availability, etc.)',
    'Communication: talking through decisions as you make them',
]
for c in eval_criteria:
    print('-', c)

Visão Geral da Estrutura RADIO

A estrutura RADIO oferece uma sequência repetível de cinco etapas para qualquer entrevista de arquitetura de sistemas:

  • R — Requisitos: funcionais e não funcionais
  • A — Projeto da API: quais operações o sistema disponibiliza?
  • D — Modelo de dados: quais dados são armazenados e como?
  • I — Infraestrutura: componentes de alto nível (servidores, filas, sistemas de cache)
  • O — Otimizar: gargalos, armazenamento em cache, fragmentação e replicação

Sempre percorra essas etapas na ordem, mas retorne às etapas anteriores e refine-as quando surgirem novas informações. Dedique aproximadamente o mesmo tempo a cada fase. Nunca comece desenhando caixas sem antes esclarecer os requisitos.

RADIO = {
    'R': 'Requirements — What must the system do? What scale?',
    'A': 'API         — Define endpoints/operations the system exposes',
    'D': 'Data Model  — Entities, schemas, storage types',
    'I': 'Infra       — High-level architecture: servers, queues, caches, CDN',
    'O': 'Optimise    — Identify and address bottlenecks, trade-offs',
}
for step, desc in RADIO.items():
    print(f'[{step}] {desc}')

print('\nTiming guide for a 45-min interview:')
print('  R: 5 min | A: 5 min | D: 10 min | I: 15 min | O: 10 min')

Etapa R: Esclarecer os Requisitos

Nunca comece a projetar sem esclarecer os requisitos. Pergunte sobre requisitos funcionais (o que o sistema faz) e requisitos não funcionais (escala, latência e disponibilidade). Para um encurtador de URLs:

  • Funcionais: encurtar uma URL, redirecionar para a URL original e, opcionalmente, permitir apelidos personalizados e expiração
  • Não funcionais: quantas URLs por dia? O sistema terá predominância de leitura ou de gravação? Qual é o requisito de disponibilidade (99,9% ou 99,99%)? Qual é a latência aceitável?

Declarar explicitamente as suposições demonstra maturidade. Os entrevistadores frequentemente fornecem especificações deliberadamente vagas para verificar se você faz as perguntas certas. Dois minutos de esclarecimentos evitam que você projete o sistema errado.

# Requirements questions for URL Shortener:
functional = [
    'Shorten a given URL to a 7-character alias',
    'Redirect short URL to original URL',
    'Allow custom aliases (optional)',
    'URL expiry (optional)',
    'Analytics: click count per URL (optional)',
]
non_functional = [
    '100 million new URLs per day (write: ~1160/sec)',
    '10:1 read:write ratio => 11,600 redirects/sec',
    'Redirects must be < 100ms p99 latency',
    '99.99% availability (< 1 hr downtime/year)',
    'URLs must be globally accessible',
]
print('Functional:')
for f in functional: print(' -', f)
print('\nNon-functional:')
for nf in non_functional: print(' -', nf)

Etapa R: Estimativa de Capacidade

Depois de esclarecer os requisitos, estime a capacidade. Isso demonstra que você consegue raciocinar sobre a escala antes de propor soluções. Os principais números a obter são: solicitações por segundo (RPS), armazenamento necessário por dia e por ano, largura de banda e memória para armazenamento em cache.

Use números arredondados e faça aproximações livremente. Os entrevistadores se importam com a ordem de grandeza, não com valores exatos. Exemplo para um encurtador de URLs: 100M gravações/dia ÷ 86400 ≈ 1160 gravações/s. 10B leituras/dia ÷ 86400 ≈ 115K leituras/s. Cada registro de URL tem aproximadamente 500 bytes: 100M × 500B = 50 GB/dia, 18 TB/ano.

# Back-of-envelope for URL Shortener
writes_per_day = 100_000_000        # 100 million URLs/day
read_write_ratio = 100              # 100:1 read/write
reads_per_day = writes_per_day * read_write_ratio
bytes_per_url = 500                 # url string + metadata
years_to_store = 5

print('=== Capacity Estimation ===')
print(f'Writes/sec:  {writes_per_day / 86400:.0f}')
print(f'Reads/sec:   {reads_per_day / 86400:,.0f}')
print(f'Storage/day: {writes_per_day * bytes_per_url / 1e9:.1f} GB')
print(f'Storage total ({years_to_store}y): {writes_per_day * bytes_per_url * 365 * years_to_store / 1e12:.1f} TB')

cache_hit_rate = 0.80
hot_urls = reads_per_day * (1 - cache_hit_rate)
print(f'\n80% cache hit rate: {cache_hit_rate*100}% of reads from cache')
print(f'DB reads/sec: {hot_urls / 86400:,.0f}')

Etapa A: Projeto da API

Defina o conjunto de operações da API — as operações que o sistema disponibiliza para clientes e serviços internos. Especifique claramente o método HTTP, o caminho do ponto de acesso, os parâmetros da solicitação e o formato da resposta. Isso serve de base para o restante do projeto: todo o resto existe para implementar essas APIs.

Para um encurtador de URLs, as duas APIs principais são: (1) POST /shorten para criar uma URL curta, (2) GET /{alias} para redirecionar. Opcionalmente: DELETE /{alias} para excluir e GET /{alias}/stats para análises. Especifique os códigos de resposta (201 Criado, 301 Redirecionado, 404 Não Encontrado).

# API Design for URL Shortener
apis = [
    {
        'method': 'POST',
        'path': '/api/v1/shorten',
        'request': '{"long_url": "https://...", "alias": "optional", "expires_at": "optional"}',
        'response': '201 Created: {"short_url": "https://short.ly/abc1234", "alias": "abc1234"}',
    },
    {
        'method': 'GET',
        'path': '/{alias}',
        'request': 'No body',
        'response': '301 Redirect to long_url (or 404 Not Found)',
    },
    {
        'method': 'GET',
        'path': '/api/v1/{alias}/stats',
        'request': 'Optional: date range query params',
        'response': '200 OK: {"clicks": 42000, "unique_visitors": 15000}',
    },
]
for api in apis:
    print(f'{api["method"]} {api["path"]}')
    print(f'  Request:  {api["request"]}')
    print(f'  Response: {api["response"]}')
    print()

Etapa D: Modelo de Dados

O modelo de dados define o que é armazenado e como. Identifique as entidades principais e seus atributos. Para um encurtador de URLs: uma tabela urls com alias (chave primária), long_url, created_at, expires_at e user_id. Uma tabela clicks opcional para análises.

Escolher o tipo certo de armazenamento é fundamental: um banco de dados relacional para dados estruturados com consultas complexas; um armazenamento de chave-valor (Redis, DynamoDB) para consultas de apelidos em O(1) em grande escala; um armazenamento de objetos (S3) para grandes dados binários. Para um encurtador de URLs, um armazenamento de chave-valor indexado pelo apelido é ideal para leituras, com um banco de dados relacional para gravações e gerenciamento.

# Data model for URL Shortener

# Core table (PostgreSQL)
urls_schema = '''
CREATE TABLE urls (
    alias       VARCHAR(16) PRIMARY KEY,   -- e.g., 'abc1234'
    long_url    TEXT NOT NULL,
    user_id     UUID REFERENCES users(id),
    created_at  TIMESTAMP DEFAULT NOW(),
    expires_at  TIMESTAMP,
    click_count BIGINT DEFAULT 0
);
CREATE INDEX ON urls(user_id);
'''

# Cache layer (Redis) for hot reads
redis_schema = '''
alias  =>  long_url       # O(1) GET on cache hit
TTL = 24 hours (or until expiry)
Cache eviction: LRU
'''

print('PostgreSQL schema:')
print(urls_schema)
print('Redis cache:')
print(redis_schema)
print('Storage split: Redis for hot reads (~80%), PostgreSQL for writes and cold reads')

Etapa I: Infraestrutura de Alto Nível

Esboce a infraestrutura de alto nível: quais servidores lidam com quais responsabilidades, como os dados fluem entre os componentes e quais serviços externos são usados. Para um encurtador de URLs em grande escala:

  • Balanceador de carga: distribui o tráfego entre réplicas dos serviços de gravação e leitura
  • Serviço de gravação: gera o apelido, valida sua exclusividade, grava no banco de dados e invalida o cache
  • Serviço de leitura/redirecionamento: verifica primeiro o cache do Redis e recorre ao banco de dados quando não encontra o valor
  • Banco de dados relacional: fonte de verdade (com réplicas de leitura)
  • Agrupamento do Redis: armazena em cache os mapeamentos de URLs mais acessados para leituras inferiores a um milissegundo
# ASCII architecture sketch
architecture = '''
         Clients
            |
       [Load Balancer]
       /             \\
  [Write API]    [Read/Redirect API]
      |                  |  |
  [Alias Gen]       [Redis Cache]
      |                  |
  [PostgreSQL]---->[Read Replicas]

Alias Generation:
- Base62 encoding of auto-incrementing ID
- 62^7 = 3.5 trillion unique URLs (enough for 5 years at 100M/day)
- OR random 7-char Base62 with collision check
'''
print(architecture)
print('Key design decisions:')
print('  - Read/Write split: separate services for scalability')
print('  - Cache-aside pattern: read from Redis, fallback to DB')
print('  - 301 vs 302 redirect: 301 cached by browser (less load), 302 always hits server (analytics)')

Etapa O: Otimizar e Lidar com Gargalos

A fase de otimização trata dos gargalos e permite dimensionar o sistema. Para um encurtador de URLs, as principais preocupações são: latência do redirecionamento (colocar o Redis próximo aos usuários, com uma CDN ou caches de borda), exclusividade dos apelidos em grande escala (usar um servidor central de distribuição de identificadores ou um resumo com detecção de colisões) e gargalo de gravação no banco de dados (fazer gravações em lote ou assíncronas usando uma fila).

Discuta as compensações explicitamente: redirecionamentos 301 reduzem a carga do servidor, mas diminuem a precisão das análises; redirecionamentos 302 podem ser rastreados, mas acrescentam latência. O armazenamento em cache com expiração longa reduz a carga do banco de dados, mas pode deixar URLs desatualizadas. Demonstrar que você entende essas tensões indica pensamento de nível sênior.

# Key optimisation discussion points
optimisations = {
    'Read latency': [
        'Redis cluster in multiple regions (CDN-edge caching)',
        '301 redirect for non-tracked URLs (client caches)',
        '302 redirect when click analytics are needed',
    ],
    'Write throughput': [
        'Async writes: accept request, enqueue to Kafka, batch-commit to DB',
        'Ticket server (centralised ID generator) to avoid UUID collision',
        'Or: hash(user_id + timestamp + random) with retry on collision',
    ],
    'Availability': [
        'Multi-AZ PostgreSQL with automatic failover',
        'Redis Sentinel or Redis Cluster for HA cache',
        'Health checks + circuit breaker on each service',
    ],
    'Storage': [
        'Partition urls table by hash(alias) for horizontal scaling',
        'Archive expired URLs to cold storage (S3)',
    ],
}
for area, points in optimisations.items():
    print(f'{area}:')
    for p in points: print(f'  - {p}')
    print()

Geração de Apelidos: Codificação Base62

Um detalhe técnico central dos encurtadores de URLs é como gerar apelidos curtos e exclusivos. A abordagem padrão é usar um ID inteiro autoincrementável do banco de dados e codificá-lo em Base62 (dígitos de 0 a 9, letras de a a z e de A a Z). Uma cadeia Base62 de 7 caracteres pode representar 62^7 ≈ 3,5 trilhões de URLs exclusivas — o suficiente para décadas à razão de 100M por dia.

Isso não gera colisões (cada ID é exclusivo) e produz cadeias curtas compatíveis com URLs. O mapeamento de ID para apelido é determinístico e reversível. A compensação é que IDs sequenciais criam apelidos previsíveis (uma preocupação de segurança). Embaralhe o alfabeto Base62 ou use um deslocamento do contador para reduzir a previsibilidade.

import string

BASE62_CHARS = string.digits + string.ascii_lowercase + string.ascii_uppercase
BASE = 62

def encode_base62(num):
    if num == 0:
        return BASE62_CHARS[0]
    result = ''
    while num:
        result = BASE62_CHARS[num % BASE] + result
        num //= BASE
    return result

def decode_base62(s):
    result = 0
    for c in s:
        result = result * BASE + BASE62_CHARS.index(c)
    return result

# Generate aliases for IDs 1, 100, 1000, 10^9
for id_ in [1, 100, 1000, 1_000_000, 10**9, 3_521_614_606207]:
    alias = encode_base62(id_)
    decoded = decode_base62(alias)
    print(f'ID {id_:20,} => alias "{alias}" (len={len(alias)}) => decoded={decoded}')

Aplicando RADIO ao Projeto de um Fluxo do Twitter

Vamos aplicar brevemente RADIO ao projeto de um fluxo de notícias semelhante ao do Twitter para mostrar que a estrutura é generalizável:

  • R: os usuários publicam mensagens, seguem outras pessoas e veem um fluxo de mensagens das pessoas que seguem. Escala: 300M usuários, 500M mensagens/dia; o fluxo deve carregar em <2s.
  • A: POST /tweets, GET /feed, GET /timeline/{user_id}
  • D: tabela tweets (id, user_id, content, created_at); tabela follows (follower_id, followee_id); cache do fluxo por usuário
  • I: o serviço de distribuição grava novas mensagens nos fluxos dos seguidores (pré-calculados); Cassandra para tabelas de seguidores e mensagens com predominância de gravações; Redis para os caches dos fluxos
# Fan-out on Write vs Fan-out on Read trade-off
fan_out_strategies = {
    'Fan-out on Write (Push)': {
        'How': 'When user A posts, immediately write to all followers feeds',
        'Pro': 'O(1) feed read — feed is precomputed',
        'Con': 'Celebrities with 10M followers => 10M writes per post; very slow write',
        'Best for': 'Users with few followers (regular users)',
    },
    'Fan-out on Read (Pull)': {
        'How': 'When user reads feed, query followees tweets and merge',
        'Pro': 'Writes are fast (one DB write per tweet)',
        'Con': 'Feed read is slow: must query all N followees',
        'Best for': 'Celebrity accounts (few reads per post)',
    },
    'Hybrid': {
        'How': 'Push for regular users, pull for celebrities',
        'Pro': 'Balances read and write cost',
        'Con': 'More complex implementation',
        'Best for': 'Production systems like Twitter',
    },
}
for strategy, details in fan_out_strategies.items():
    print(f'{strategy}:')
    for k, v in details.items(): print(f'  {k}: {v}')
    print()

Erros comuns em entrevistas de design de sistemas

Evite estes erros frequentes que comprometem as entrevistas de design de sistemas:

  • Começar pelas soluções: desenhar caixas antes de esclarecer os requisitos indica práticas de engenharia deficientes
  • Não fazer estimativas: projetar sem conhecer a escala é trabalhar com suposições
  • Projetar além do necessário: projetar para 1 bilhão de usuários quando o enunciado informa 10.000 desperdiça o tempo da entrevista
  • Não discutir compromissos: toda escolha tem vantagens e desvantagens; não mencioná-las indica uma compreensão superficial
  • Silêncio: os entrevistadores precisam ouvir seu processo de raciocínio; narre suas decisões à medida que as tomar
# Checklist: before you stop talking, verify you covered:
checklist = [
    '[ ] Asked clarifying questions about scale and constraints',
    '[ ] Made capacity estimates (RPS, storage, bandwidth)',
    '[ ] Defined the API surface clearly',
    '[ ] Described the data model and storage choices',
    '[ ] Drew a high-level architecture with named components',
    '[ ] Identified the main bottleneck and proposed a solution',
    '[ ] Discussed at least one trade-off explicitly',
    '[ ] Verified the design meets the stated requirements',
]
for item in checklist:
    print(item)

Verificação rápida

Teste sua compreensão dos conceitos de Estruturas de Dados & Algoritmos — Preparação para Entrevistas de Programação abordados nesta lição.

Recapitulação da lição

Nesta lição, você aprendeu que: o framework RADIO estrutura as entrevistas de design de sistemas em Requisitos, API, Modelo de dados, Infraestrutura e Otimização, você deve sempre esclarecer os requisitos funcionais e não funcionais e fazer estimativas de capacidade antes de propor um projeto e discutir os compromissos explicitamente — toda decisão de projeto tem vantagens e desvantagens que os entrevistadores esperam que você articule. A seguir, exploraremos como escolher entre mecanismos de armazenamento SQL e NoSQL com base nos padrões de acesso, na consistência e na escala.

Perguntas Frequentes

A aula “A estrutura da entrevista de projeto de sistemas” é grátis?

Sim — o texto completo de “A estrutura da entrevista de projeto de sistemas” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de DSA Interview Prep, atualize para CoddyKit PRO. O curso de DSA Interview Prep inclui 4 aulas no total.

O que vou aprender em “A estrutura da entrevista de projeto de sistemas”?

Percorra a estrutura RADIO em cinco etapas (Requisitos, API, Dados, Infraestrutura e Otimização) e pratique sua aplicação a um encurtador de URLs. Você pratica DSA Interview Prep com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar DSA Interview Prep?

Nenhuma experiência prévia é necessária. DSA Interview Prep no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.

Quanto tempo leva a aula “A estrutura da entrevista de projeto de sistemas”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de DSA Interview Prep?

Sim. Cada aula de DSA Interview Prep inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. A estrutura da entrevista de projeto de sistemas
  2. Armazenamento de dados escalável: SQL vs NoSQL
  3. Cache, CDNs e balanceamento de carga
  4. Projetar um limitador de taxa e um feed do Twitter
← Voltar para DSA Interview Prep