Blog

Como eu enxergo a arquitetura do Twitter (X).

Este texto abre uma série em que eu leio a arquitetura de seis produtos que todo mundo usa. Vale dizer de onde vem o material: blog de engenharia, palestra, paper publicado e o que os times contaram em conferência. Não é conhecimento interno, e sistema dessa idade muda por baixo o tempo todo. O que eu trago é a leitura, não a planta.

E é isso que me interessa: não o inventário de tecnologia, e sim a decisão que organiza o resto.

A pergunta que define tudo

Quando você abre o Twitter, precisa ver os tweets de quem você segue, em ordem, agora.

A forma óbvia de resolver isso é uma consulta:

select * from tweets
where author_id in (select followed_id from follows where follower_id = ?)
order by created_at desc
limit 50;

Funciona no seu projeto. Não funciona aqui, e a razão não é o tamanho da tabela: é a assimetria entre leitura e escrita.

Gente escreve pouco e lê muito. A proporção entre abrir a timeline e publicar algo é de ordens de grandeza. Uma arquitetura que faz trabalho pesado na leitura está fazendo trabalho pesado no lado que acontece mil vezes mais.

A inversão é o que eu considero a decisão central do produto: calcule na escrita, não na leitura.

Fan-out na escrita

Quando alguém publica, o sistema não guarda o tweet e vai embora. Ele empurra a referência daquele tweet pra dentro da timeline pré-montada de cada seguidor.

Publicar POST /tweet Serviço de fan-out lê o grafo de seguidores escreve N vezes timeline:user_A [id, id, id, …] · ~800 timeline:user_B [id, id, id, …] timeline:user_C [id, id, id, …] timeline:user_N cache em memória leitura: uma busca por chave
Fan-out na escrita — o trabalho acontece uma vez, na publicação

A timeline vira uma lista de identificadores num cache em memória, com algumas centenas de posições. Ler passa a ser buscar uma chave e hidratar os tweets. É a operação mais barata que existe.

O que eu acho elegante nisso é o reconhecimento de que a timeline não é uma consulta. É um resultado materializado, com todas as consequências que isso carrega: ela pode estar desatualizada por um instante, ela ocupa espaço multiplicado pelo número de usuários, e ela precisa ser reconstruível quando o cache se perde.

Que é exatamente a mesma discussão de saldo calculado ou materializado, num contexto onde o custo de errar é bem menor.

Onde o modelo quebra

Alguém com trinta milhões de seguidores publica. O fan-out precisa escrever trinta milhões de vezes.

Isso não é um pico: é um pico correlacionado, porque contas grandes publicam em horário de audiência, e várias fazem isso ao mesmo tempo. A fila explode, e a timeline de todo mundo atrasa por causa de um punhado de contas.

A saída, que virou material de estudo em system design, é híbrida: contas acima de um certo número de seguidores são excluídas do fan-out. Os tweets delas ficam só na linha do autor, e são buscados e mesclados na hora da leitura.

Timeline pré-montada contas comuns · cache Contas grandes buscadas na leitura Merge + ranking ordena, filtra, pontua Timeline entregue alguns milissegundos
O modelo híbrido — o caso raro e caro sai do caminho comum

Essa é a decisão que eu mais admiro nesse desenho, e é a mais transferível. Não é uma solução geral bonita: é o reconhecimento de que a distribuição é desigual e a arquitetura deveria refletir isso. A cauda longa vai pro caminho barato, o punhado extremo vai pro caminho caro, e ninguém paga o custo do outro.

Quase todo sistema que eu vi sofrer com performance tinha o problema oposto: um caminho único, dimensionado pelo caso médio, sendo destruído pelo caso extremo. Foi exatamente isso no cenário do índice faltando.

O identificador que carrega o tempo

Um detalhe pequeno com efeito enorme: o identificador do tweet não é aleatório nem sequencial global. É um número de 64 bits que embute o instante da criação, o identificador da máquina que gerou e uma sequência.

Isso resolve três coisas de uma vez. Dá pra gerar em paralelo, em várias máquinas, sem coordenação. É ordenável por tempo, então ordenar a timeline é ordenar números. E cabe num inteiro, que indexa e trafega barato.

Escrevi sobre essa família de identificadores em UUIDv7, ULID ou Snowflake. Ver o padrão nascer de uma necessidade concreta é bem mais instrutivo que ler a comparação.

O que a timeline algorítmica muda

Quando a ordem era estritamente cronológica, a timeline pré-montada era quase o produto final.

Com ranking, ela vira candidata. O sistema monta um conjunto de algumas centenas de itens possíveis e depois pontua cada um com um modelo, considerando afinidade, engajamento provável, recência e uma pilha de sinais.

Isso divide a arquitetura em duas etapas que passaram a aparecer em todo produto com feed: geração de candidatos, que é barata e ampla, e ranking, que é caro e estreito. Você nunca pontua tudo. Você reduz primeiro, pontua depois.

Vou voltar nesse padrão no texto sobre o YouTube, onde ele é ainda mais explícito.

O que eu levo pra sistema pequeno

Três coisas, e nenhuma delas exige escala.

Descubra a assimetria. Em quase todo sistema existe uma operação que acontece mil vezes mais que outra. Se o trabalho pesado está no lado frequente, mova pro lado raro. Materializar o resultado da consulta cara no momento da escrita é uma técnica disponível pra qualquer um.

Trate o caso extremo por fora. Se 0,1% dos registros tem mil vezes mais volume que a mediana, eles não deveriam passar pelo mesmo caminho. Um if explícito pra esse grupo é mais honesto que uma solução genérica que sofre nos dois casos.

Assuma que o cache vai sumir. Timeline materializada é cache, e a pergunta que decide a qualidade do desenho é como reconstruir. Se a resposta for “não sei”, você não tem cache: tem uma fonte de verdade frágil.

O que me impressiona nesse sistema não é a escala. É que a decisão central — inverter o momento do trabalho — foi tomada cedo, e o resto é consequência.


A série tem seis textos: Twitter, Instagram, YouTube, WhatsApp, Spotify e LinkedIn. Se preferir ler tudo de uma vez, com os diagramas, baixe o PDF.

← Voltar pro blog