Projetando o X.com (Twitter) em Escala: Um Guia Completo de System Design

Resumo: Um guia completo de system design para construir uma plataforma social como o Twitter, lidando com bilhões de tweets, timelines em tempo real e assimetria massiva de leitura/escrita.

Publicado: Fevereiro 2026

Tempo de leitura: 55 minutos

Palavras-chave: #SystemDesign #Twitter #XDotCom #SistemasDistribuidos #Timeline #FanOut #Escalabilidade #ArquiteturaDeSoftware #EntrevistaTecnica

São 3 da manhã de um domingo. Beyoncé acabou de lançar um álbum surpresa e tuitou sobre isso. Nos próximos 60 segundos, 300.000 pessoas retuitam o anúncio. Cada um desses retweets precisa aparecer nas timelines de todos os seguidores -- algumas contas têm 50 milhões de seguidores. Isso são potencialmente 15 trilhões de inserções em timelines disparadas por um único tweet.

Bem-vindo ao desafio mais fascinante de sistemas distribuídos nas redes sociais: projetar o Twitter em escala.

Isso não é mais um overview superficial que te diz "use um load balancer." Isso é uma exploração aprofundada e testada em batalha de cada decisão arquitetural que torna uma plataforma como o X.com possível. Vamos cobrir os trade-offs exatos que o time de engenharia do Twitter enfrentou, as soluções que escolheram e o porquê de cada uma.

Se você está se preparando para uma entrevista de system design em uma grande empresa de tecnologia, arquitetando sua própria plataforma social, ou simplesmente curioso sobre como 500 milhões de tweets por dia chegam a bilhões de timelines em milissegundos -- este guia tem tudo que você precisa.

Vamos construir o Twitter do zero.

Sumário

  • Análise de Requisitos
  • Cálculos de Envelope
  • Arquitetura de Alto Nível
  • Design da API
  • Modelagem de Dados
  • Geração da Timeline: O Problema Central
  • Pipeline de Ingestão de Tweets
  • Busca e Trending Topics
  • Sistema de Notificações
  • Mensagens Diretas
  • Armazenamento de Mídia e CDN
  • Estratégia de Cache
  • Arquitetura de Banco de Dados e Sharding
  • Confiabilidade e Tolerância a Falhas
  • Monitoramento e Observabilidade
  • Dicas e Estratégia para Entrevistas
  • Anti-Patterns a Evitar
  • Conclusão
  • Referências
  • Referência Rápida

Análise de Requisitos

Antes de escrever uma única linha de código ou desenhar qualquer diagrama de arquitetura, precisamos entender profundamente o que estamos construindo. Em uma entrevista de system design, gastar de 5 a 8 minutos em requisitos não é tempo desperdiçado -- é a base que impede você de construir o sistema errado.

Requisitos Funcionais

Funcionalidades Essenciais (Must Have):

  1. Postar tweets -- Usuários podem criar posts curtos de texto (até 280 caracteres para usuários gratuitos, 25.000 para premium)
  2. Timeline inicial -- Usuários veem um feed cronológico ou classificado por algoritmo de tweets das pessoas que seguem
  3. Seguir/Deixar de seguir -- Usuários podem seguir outros usuários para ver seus tweets
  4. Retweet e Quote Tweet -- Usuários podem amplificar conteúdo para seus seguidores
  5. Curtir tweets -- Usuários podem expressar apreciação por conteúdo
  6. Responder a tweets -- Conversas encadeadas abaixo dos tweets
  7. Busca -- Busca textual completa em todos os tweets públicos
  8. Trending topics -- Identificação em tempo real de hashtags e tópicos em alta
  9. Notificações -- Alertas para menções, curtidas, retweets, seguidores e respostas
  10. Mensagens Diretas -- Mensagens privadas individuais e em grupo

Funcionalidades Estendidas (Nice to Have):

  • Anexos de mídia (imagens, vídeos, GIFs)
  • Enquetes
  • Spaces (salas de áudio ao vivo)
  • Listas e favoritos
  • Analíticos para criadores de conteúdo
  • Anúncios e conteúdo promovido
  • Moderação de conteúdo e denúncia

Requisitos Não-Funcionais

Requisito

Meta

Justificativa

Disponibilidade

99,99% (52 min de downtime/ano)

Plataforma social; usuários esperam disponibilidade constante

Latência da Timeline

< 200ms p99

Usuários rolam rapidamente; deve parecer instantâneo

Latência de Postagem

< 500ms p99

Usuários esperam publicação quase instantânea

Modelo de Consistência

Consistência eventual (timeline), Forte (follows, DMs)

Timeline pode ter leve atraso; follows devem ser precisos

Durabilidade

Nenhum tweet perdido após confirmação

Cada tweet é um registro permanente

Proporção Leitura:Escrita

1000:1

Amplificação massiva de leitura devido às timelines

Tolerância a Partições

Obrigatória

Sistema global deve tolerar partições de rede

Estimativas de Escala (Baseado em Dados Reais do Twitter)

  • Usuários Ativos Diários (DAU): 250 milhões
  • Usuários Ativos Mensais (MAU): 550 milhões
  • Tweets por dia: 500 milhões (~6.000 tweets/segundo em média, 12.000/seg no pico)
  • Leituras de timeline por dia: 250 bilhões (cada usuário lê a timeline ~1.000 vezes/dia em média)
  • Média de seguidores por usuário: 200 (mas a distribuição é extremamente enviesada -- lei de potência)
  • Contas de celebridades: ~100.000 contas com mais de 1 milhão de seguidores
  • Tamanho médio de um tweet: 300 bytes (texto + metadados)
  • Anexos de mídia: 30% dos tweets contêm imagens/vídeo

Cálculos de Envelope

Esses cálculos são críticos em entrevistas. Eles demonstram maturidade de engenharia e ajudam a direcionar decisões arquiteturais.

Armazenamento

Tweets por dia: 500MTamanho médio do tweet (texto + metadados): ~300 bytesArmazenamento diário de tweets: 500M x 300B = 150 GB/diaArmazenamento anual de tweets: 150 GB x 365 = ~55 TB/anoArmazenamento em 5 anos: ~275 TB (apenas texto)Mídia (imagens/vídeo):30% dos tweets têm mídiaTamanho médio de mídia: 500 KB (imagens), 5 MB (vídeo)Assumindo 80% imagens, 20% vídeo:Mídia diária: 500M x 0.3 x (0.8 x 500KB + 0.2 x 5MB) = 150M x (400KB + 1MB) = 150M x 1.4MB = 210 TB/diaMídia anual: 210 TB x 365 = ~76 PB/ano

Largura de Banda

Largura de banda de leitura (timeline):250B leituras de timeline/dia x 10 tweets por página x 300 bytes= 750 TB/dia = ~8,7 GB/segLargura de banda de escrita (novos tweets):6.000 tweets/seg x 300 bytes = 1,8 MB/seg (trivial)Largura de banda de mídia é dominante:Assumindo que 50% das visualizações de timeline incluem carregamento de mídia= ~4 TB/seg no pico (servido primariamente via CDN)

QPS (Consultas Por Segundo)

Criação de tweets: 6.000/seg (média), 12.000/seg (pico)Leituras de timeline: 250B/86400 ~ 3M/seg (média), 6M/seg (pico)Consultas de busca: ~100K/segCurtida/Retweet: ~50K/segSeguir/Deixar de seguir: ~5K/seg

Insight Principal: A proporção leitura-para-escrita é de aproximadamente 500:1 para operações de timeline. Essa assimetria extrema é o fator mais importante que direciona toda a nossa arquitetura.

Arquitetura de Alto Nível

graph TB subgraph "Client Layer" WEB[Web Client<br/>React/Next.js] IOS[iOS App<br/>Swift] AND[Android App<br/>Kotlin] end subgraph "Edge Layer" CDN[CDN<br/>CloudFront/Akamai] LB[Load Balancer<br/>L7 - HAProxy/Nginx] end subgraph "API Layer" GW[API Gateway<br/>Rate Limiting, Auth, Routing] GQL[GraphQL Federation<br/>Gateway] end subgraph "Core Services" TS[Tweet Service] TLS[Timeline Service] US[User Service] FS[Follow Service/Social Graph] SS[Search Service] NS[Notification Service] DMS[DM Service] MS[Media Service] TRS[Trending Service] end subgraph "Async Processing" MQ[Message Queue<br/>Apache Kafka] FO[Fan-Out Service] IDX[Indexing Service] AN[Analytics Pipeline] end subgraph "Data Layer" TC[(Tweet Store<br/>Manhattan/Cassandra)] UC[(User Store<br/>PostgreSQL)] GC[(Social Graph<br/>FlockDB/Neo4j)] SC[(Search Index<br/>Elasticsearch)] CACHE[(Cache Layer<br/>Redis Cluster)] TLC[(Timeline Cache<br/>Redis)] BLOB[(Media Store<br/>S3/HDFS)] end WEB & IOS & AND --> CDN CDN --> LB LB --> GW GW --> GQL GQL --> TS & TLS & US & FS & SS & NS & DMS & MS & TRS TS --> MQ MQ --> FO & IDX & AN FO --> TLC TS --> TC US --> UC FS --> GC SS --> SC TLS --> TLC & CACHE MS --> BLOB IDX --> SC style GW fill:#e1f5fe style FO fill:#fff3e0 style TLC fill:#fce4ec style MQ fill:#f3e5f5

Decisões Arquiteturais

Por que Microsserviços? O Twitter começou como um monolito em Ruby on Rails. Em 2012, eles migraram para uma arquitetura de microsserviços porque:

  1. Escalabilidade independente -- Leituras de timeline escalam de forma diferente das escritas de tweets
  2. Autonomia dos times -- Mais de 100 times de engenharia podem fazer deploy de forma independente
  3. Diversidade tecnológica -- Busca usa Java, timeline usa Scala, alguns serviços usam Go
  4. Isolamento de falhas -- Um bug na busca não deveria derrubar a postagem de tweets

Por que GraphQL Federation? Em vez de cada cliente conversar com dezenas de serviços, uma camada de federação GraphQL oferece:

  • Endpoint único para clientes
  • Agregação de dados entre serviços
  • Busca de dados dirigida pelo cliente (mobile recebe menos dados que web)
  • Governança de schema entre times

Design da API

Endpoints Core REST/GraphQL

// ===== Tweet Service API =====interface CreateTweetRequest { text: string; // max 280 chars (free) ou 25,000 (premium) media_ids?: string[]; // referências de mídia pré-carregadas reply_to?: string; // tweet ID se for uma resposta quote_tweet_id?: string; // tweet ID se for um quote tweet poll?: PollOptions; // enquete opcional conversation_settings?: 'everyone' | 'following' | 'mentioned';}interface Tweet { id: string; // Snowflake ID user_id: string; text: string; created_at: string; // ISO 8601 media: MediaAttachment[]; metrics: { retweet_count: number; like_count: number; reply_count: number; view_count: number; }; reply_to?: string; conversation_id: string; language: string; source: string; // "Twitter for iPhone", etc.}// POST /api/v2/tweets// GET /api/v2/tweets/:id// DELETE /api/v2/tweets/:id// ===== Timeline Service API =====interface TimelineRequest { cursor?: string; // cursor de paginação (string opaca) count?: number; // padrão 20, máximo 200 algorithm?: 'reverse_chronological' | 'ranked';}interface TimelineResponse { tweets: Tweet[]; cursor_top: string; // para tweets mais recentes (pull to refresh) cursor_bottom: string; // para tweets mais antigos (scroll infinito)}// GET /api/v2/timeline/home?cursor=xxx&count=20// GET /api/v2/timeline/user/:userId?cursor=xxx// ===== Social Graph API =====interface FollowRequest { target_user_id: string;}// POST /api/v2/users/:id/follow// DELETE /api/v2/users/:id/follow// GET /api/v2/users/:id/followers?cursor=xxx// GET /api/v2/users/:id/following?cursor=xxx// ===== Search API =====interface SearchRequest { query: string; type: 'tweets' | 'users' | 'hashtags'; since?: string; // filtro de data until?: string; from?: string; // usuário específico lang?: string; cursor?: string; count?: number;}// GET /api/v2/search?q=hello&type=tweets&cursor=xxx// ===== Engagement API =====// POST /api/v2/tweets/:id/like// DELETE /api/v2/tweets/:id/like// POST /api/v2/tweets/:id/retweet// DELETE /api/v2/tweets/:id/retweet

Estratégia de Rate Limiting

interface RateLimitConfig { // Limites por usuário (janela deslizante) tweet_create: { requests: 300, window: '3h' }; timeline_read: { requests: 1500, window: '15m' }; search: { requests: 450, window: '15m' }; follow: { requests: 400, window: '24h' }; like: { requests: 1000, window: '24h' }; dm_send: { requests: 500, window: '24h' }; // Limites por aplicação (para consumidores da API) app_tweet_read: { requests: 300000, window: '15m' }; app_tweet_create: { requests: 200, window: '15m' };}

WebSocket para Atualizações em Tempo Real

// Conexão WebSocket para funcionalidades em tempo real// wss://stream.x.com/v2/streaminterface StreamEvent { type: 'new_tweet' | 'like' | 'retweet' | 'reply' | 'follow' | 'dm' | 'notification' | 'typing'; data: Record<string, unknown>; timestamp: string;}// O cliente se inscreve em streams específicos:// - atualizações da timeline do usuário// - stream de notificações// - atualizações de conversas de DM// - indicadores de digitação

Modelagem de Dados

Diagrama Entidade-Relacionamento

erDiagram USER { bigint id PK "Snowflake ID" varchar username "unique, indexed" varchar display_name text bio varchar profile_image_url varchar banner_image_url boolean verified boolean is_premium timestamp created_at int followers_count int following_count int tweet_count } TWEET { bigint id PK "Snowflake ID" bigint user_id FK text content bigint reply_to_tweet_id FK bigint conversation_id bigint quote_tweet_id FK varchar language varchar source jsonb media_keys timestamp created_at int retweet_count int like_count int reply_count int view_count } FOLLOW { bigint follower_id FK bigint following_id FK timestamp created_at } LIKE { bigint user_id FK bigint tweet_id FK timestamp created_at } RETWEET { bigint user_id FK bigint tweet_id FK timestamp created_at } MEDIA { bigint id PK bigint tweet_id FK varchar type "image|video|gif" varchar url varchar thumbnail_url int width int height int duration_ms varchar alt_text } NOTIFICATION { bigint id PK bigint user_id FK varchar type "like|retweet|reply|follow|mention" bigint actor_id FK bigint tweet_id FK boolean read timestamp created_at } USER ||--o{ TWEET : "posts" USER ||--o{ FOLLOW : "follows" USER ||--o{ LIKE : "likes" USER ||--o{ RETWEET : "retweets" TWEET ||--o{ MEDIA : "contains" TWEET ||--o{ LIKE : "receives" TWEET ||--o{ RETWEET : "receives" USER ||--o{ NOTIFICATION : "receives"

Schema SQL (Tabelas Principais)

-- Tabela de usuários (PostgreSQL, sharded por user_id)CREATE TABLE users ( id BIGINT PRIMARY KEY, -- Snowflake ID username VARCHAR(15) UNIQUE NOT NULL, display_name VARCHAR(50), bio TEXT, profile_image VARCHAR(255), banner_image VARCHAR(255), verified BOOLEAN DEFAULT FALSE, is_premium BOOLEAN DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), followers_count INT DEFAULT 0, following_count INT DEFAULT 0, tweet_count INT DEFAULT 0, location VARCHAR(100), website VARCHAR(200));CREATE INDEX idx_users_username ON users(username);CREATE INDEX idx_users_created_at ON users(created_at);-- Tabela de tweets (Cassandra / Manhattan, particionada por user_id)CREATE TABLE tweets ( id BIGINT PRIMARY KEY, -- Snowflake ID (contém timestamp) user_id BIGINT NOT NULL, content TEXT, reply_to_tweet_id BIGINT, conversation_id BIGINT, quote_tweet_id BIGINT, language VARCHAR(5), source VARCHAR(50), media_keys TEXT[], -- array de IDs de mídia created_at TIMESTAMPTZ NOT NULL, retweet_count INT DEFAULT 0, like_count INT DEFAULT 0, reply_count INT DEFAULT 0, view_count BIGINT DEFAULT 0, is_sensitive BOOLEAN DEFAULT FALSE);CREATE INDEX idx_tweets_user_id ON tweets(user_id, created_at DESC);CREATE INDEX idx_tweets_conversation ON tweets(conversation_id, created_at);-- Relacionamentos de follow (Grafo Social - FlockDB / Cassandra)CREATE TABLE follows ( follower_id BIGINT NOT NULL, following_id BIGINT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), PRIMARY KEY (follower_id, following_id));-- Índice reverso para "quem me segue"CREATE INDEX idx_follows_following ON follows(following_id, follower_id);-- Curtidas (Cassandra, alto throughput de escrita)CREATE TABLE likes ( user_id BIGINT NOT NULL, tweet_id BIGINT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), PRIMARY KEY (user_id, tweet_id));CREATE INDEX idx_likes_tweet ON likes(tweet_id, created_at DESC);

Geração de Snowflake IDs

O Twitter inventou o sistema de Snowflake IDs, que agora é usado em toda a indústria. Entender isso é essencial.

/** * Twitter Snowflake ID Generator * * Estrutura do ID de 64 bits: * [1 bit não usado][41 bits timestamp][5 bits datacenter][5 bits worker][12 bits sequência] * * - Timestamp: milissegundos desde uma época customizada (Twitter: 4 Nov, 2010) * - Suporta ~69 anos de timestamps * - 4096 IDs únicos por milissegundo por worker * - Ordenável por tempo: IDs mais novos são sempre maiores */class SnowflakeGenerator { private static EPOCH = 1288834974657n; // Época do Twitter: 4 Nov, 2010 private static DATACENTER_BITS = 5n; private static WORKER_BITS = 5n; private static SEQUENCE_BITS = 12n; private static MAX_SEQUENCE = (1n << this.SEQUENCE_BITS) - 1n; // 4095 private static WORKER_SHIFT = this.SEQUENCE_BITS; private static DATACENTER_SHIFT = this.SEQUENCE_BITS + this.WORKER_BITS; private static TIMESTAMP_SHIFT = this.SEQUENCE_BITS + this.WORKER_BITS + this.DATACENTER_BITS; private sequence = 0n; private lastTimestamp = -1n; constructor( private datacenterId: bigint, private workerId: bigint ) {} generate(): bigint { let timestamp = BigInt(Date.now()); if (timestamp === this.lastTimestamp) { this.sequence = (this.sequence + 1n) & SnowflakeGenerator.MAX_SEQUENCE; if (this.sequence === 0n) { // Espera pelo próximo milissegundo while (timestamp <= this.lastTimestamp) { timestamp = BigInt(Date.now()); } } } else { this.sequence = 0n; } this.lastTimestamp = timestamp; return ( ((timestamp - SnowflakeGenerator.EPOCH) << SnowflakeGenerator.TIMESTAMP_SHIFT) | (this.datacenterId << SnowflakeGenerator.DATACENTER_SHIFT) | (this.workerId << SnowflakeGenerator.WORKER_SHIFT) | this.sequence ); } // Extrai timestamp de um Snowflake ID static extractTimestamp(id: bigint): Date { const timestamp = (id >> this.TIMESTAMP_SHIFT) + this.EPOCH; return new Date(Number(timestamp)); }}

Source

1 share