Figma: Sharding Horizontal de Postgres Sem Parar de Crescer
Ouvir estudo
gerado ao ouvirGerado apenas no primeiro play
Com tecnologia Amazon Polly + OmniVoice
Em 2022, o Figma enfrentou limites físicos do seu banco de dados Postgres monolítico e executou uma migração de sharding horizontal com particionamento por chave, roteamento dinâmico e migração incremental — tudo isso sem downtime e sem travar o crescimento do produto. Este teardown reconstrói a arquitetura, analisa as decisões técnicas e aponta o que eu faria diferente.
Sharding de banco de dados é uma das operações mais arriscadas que uma engenharia pode executar em produção. O Figma fez isso com Postgres, em escala de produto SaaS global, sem downtime e sem interromper o crescimento — e depois contou como. Este teardown desmonta a arquitetura, avalia as decisões e diz o que eu mudaria.
Ficha Técnica
- Empresa
- Figma
- Domínio
- Plataforma de design colaborativo SaaS
- Período da migração
- 2021–2022 (publicado em 2022)
- Banco de dados original
- PostgreSQL monolítico (instância única de escrita)
- Banco de dados alvo
- PostgreSQL com sharding horizontal por chave de tenant/objeto
- Stack principal
- PostgreSQL, PgBouncer, Ruby on Rails, infraestrutura AWS
- Estratégia de roteamento
- Camada de roteamento customizada na aplicação (shard map)
- Downtime
- Zero downtime para usuários finais
- Impacto do problema
- CPU, WAL lag e capacidade de conexões próximos do limite físico da instância primária
O Problema: Um Postgres Monolítico Engolindo Uma Empresa em Crescimento
O Figma cresceu rápido demais para a sua própria infraestrutura de dados. Durante anos, a arquitetura de banco de dados seguiu o padrão clássico de startups bem-sucedidas: um Postgres primário com réplicas de leitura, escalando verticalmente sempre que os sinais de alerta apareciam. Essa abordagem funcionou — até o momento em que deixou de funcionar.
O problema não foi um único evento catastrófico. Foi uma convergência de pressões: a instância primária acumulando CPU elevada de forma crônica, o WAL (Write-Ahead Log) apresentando lag crescente nas réplicas, e o pool de conexões operando perto do limite físico do PgBouncer. Em sistemas transacionais de alta frequência como o Figma — onde múltiplos usuários editam o mesmo documento em tempo real — qualquer degradação no banco de dados primário se propaga diretamente para a experiência do usuário.
A escala vertical havia chegado ao seu teto prático. Mover para uma instância maior resolveria o problema por meses, não por anos. O time precisava de uma solução estrutural, e a única saída era o sharding horizontal — distribuir os dados entre múltiplos nós Postgres independentes, cada um responsável por um subconjunto das entidades do sistema.
O que torna esse problema especialmente difícil é que o Figma não tinha o luxo de pausar o produto para fazer a migração. O crescimento do negócio continuava, novos usuários chegavam, novos arquivos eram criados. A migração precisava acontecer debaixo de um sistema em plena operação, sem que os usuários percebessem.
A Arquitetura Reconstruída: Como o Sistema Funciona
A solução do Figma pode ser decomposta em três camadas interdependentes: particionamento lógico, roteamento dinâmico e migração incremental com dupla escrita.
Particionamento lógico por chave de entidade
O esquema de sharding escolhido foi baseado em chave — especificamente, no identificador da entidade raiz (tipicamente o file_id ou equivalente de tenant). Isso significa que todos os dados relacionados a um determinado arquivo ou usuário são colocalizados no mesmo shard físico. A colocação é crítica: queries que atravessam shards (cross-shard joins) são extremamente custosas e, se mal planejadas, eliminam qualquer ganho de escala. O Figma optou por uma abordagem de colocação agressiva para evitar esse antipadrão.
O número de shards lógicos foi definido como um múltiplo intencional dos shards físicos. Isso é uma decisão de engenharia sofisticada: ao separar shards lógicos de shards físicos, o sistema pode rebalancear dados movendo shards lógicos entre nós físicos sem precisar rehashear todas as chaves. É o mesmo princípio do consistent hashing, mas implementado de forma mais explícita com um mapa de shards controlado pela aplicação.
Roteamento dinâmico na camada de aplicação
Em vez de adotar um proxy de banco de dados externo (como Vitess ou Citus), o Figma implementou o roteamento dentro da própria aplicação Rails. Existe um componente central — o shard map — que mantém o mapeamento de shard_id lógico → instância Postgres física. Antes de qualquer query, a aplicação consulta esse mapa para determinar para qual conexão de banco de dados rotear a operação.
Essa escolha tem implicações profundas. Por um lado, elimina um hop de rede e uma camada de infraestrutura adicional para operar. Por outro, acopla a lógica de roteamento ao código da aplicação, o que significa que toda mudança no mapa de shards precisa ser coordenada com o deploy da aplicação — ou gerenciada via feature flags e configuração dinâmica.
Migração incremental com dupla escrita e verificação
A parte mais delicada de toda a operação foi a migração dos dados existentes sem downtime. O processo seguiu um padrão clássico de migração online: primeiro, ativar a dupla escrita (dual-write) para as entidades sendo migradas, onde cada escrita vai tanto para o shard de origem quanto para o shard de destino; segundo, executar o backfill dos dados históricos em background, com controle de taxa para não sobrecarregar o primário; terceiro, verificar a consistência entre origem e destino antes de cortar o tráfego de leitura; quarto, promover o shard de destino como autoritativo e remover a escrita duplicada.
Cada fase dessa migração foi executada de forma incremental, tabela por tabela, entidade por entidade. Não foi uma big-bang migration — foi uma série de migrações cirúrgicas, cada uma com rollback definido.
Arquitetura de Sharding Horizontal do Figma
Fluxo de leitura/escrita com roteamento por shard map, dupla escrita durante migração e réplicas por shard físico.
- Figma Client · (Browser / Desktop)
- Rails App · (API Servers)
- Shard Map · (logical→physical)
- PgBouncer · (connection pool)
- Postgres Primary · Shard 0
- Postgres Replica · Shard 0
- Postgres Primary · Shard 1
- Postgres Replica · Shard 1
- Postgres Primary · Shard N
- Postgres Replica · Shard N
- Backfill Worker · (rate-limited)
- Consistency Verifier · (diff checker)
A Complexidade Oculta: O Que Não Aparece no Diagrama
Qualquer diagrama de sharding parece razoável no papel. A brutalidade está nos detalhes operacionais que o diagrama não captura.
O problema das transações distribuídas
O Postgres não tem suporte nativo a transações distribuídas entre instâncias independentes. Isso significa que qualquer operação que precise de atomicidade entre dois shards diferentes precisa ser redesenhada. O Figma resolveu isso de duas formas: primeiro, através da colocação agressiva de entidades relacionadas no mesmo shard (evitando o problema na origem); segundo, aceitando consistência eventual em casos onde a atomicidade cross-shard era inviável e redesenhando o modelo de dados para eliminar dependências entre shards sempre que possível.
Essa é uma decisão de design que tem impacto direto no produto. Alguns padrões de query que eram triviais no monolito — um JOIN entre tabelas de entidades diferentes — se tornam operações proibidas ou muito custosas no mundo shardado. O time de engenharia precisou auditar todas as queries existentes e classificá-las por padrão de acesso antes de definir a estratégia de particionamento.
O custo humano da dupla escrita
A fase de dual-write é conceitualmente simples, mas operacionalmente cara. Durante o período de migração, cada escrita relevante precisa ir para dois destinos. Isso aumenta a latência de escrita, dobra a pressão sobre o pool de conexões, e introduz um novo vetor de falha: o que acontece se a escrita no shard de destino falhar? O Figma precisou definir políticas explícitas de tratamento de falha para cada fase da migração — e essas políticas precisavam ser conservadoras o suficiente para não corromper dados, mas agressivas o suficiente para não travar o produto.
Gerenciamento do shard map como infraestrutura crítica
O shard map não é um arquivo de configuração estático. É um componente de infraestrutura que precisa ser lido em alta frequência (toda query passa por ele), ter baixíssima latência, e ser atualizado de forma atômica durante rebalanceamentos. O Figma precisou tratar o shard map como um serviço de primeira classe — com cache na aplicação, invalidação controlada e monitoramento dedicado. Uma falha no shard map é uma falha global: se a aplicação não consegue resolver para qual shard rotear uma query, ela não consegue servir nenhuma requisição.
Schema migrations em um mundo shardado
No monolito, uma migration de schema é um evento único e controlado. Com N shards físicos, cada migration precisa ser executada N vezes, de forma coordenada, sem causar inconsistência entre shards durante o período de transição. O Figma adotou a prática de migrations compatíveis com versões anteriores (backward-compatible schema changes) — adicionando colunas como nullable antes de torná-las obrigatórias, mantendo colunas antigas durante períodos de transição — para garantir que diferentes shards pudessem estar em estados de schema ligeiramente diferentes durante o rollout.
Matriz de Decisões: Opções de Sharding Consideradas
Roteamento na aplicação (escolha do Figma)
- Sem hop adicional de rede
- Controle total sobre lógica de roteamento e failover
- Sem dependência de produto externo para o caminho crítico
- Integração nativa com feature flags e rollout incremental
- Lógica de sharding acoplada ao código da aplicação
- Cada linguagem/serviço precisa implementar o roteamento
- Shard map vira infraestrutura crítica gerenciada internamente
Correto para o contexto: stack Rails homogênea, equipe com controle total do código
Vitess (proxy MySQL-compatible)
- Sharding transparente para a aplicação
- Gerenciamento de conexões centralizado
- Amplamente testado em produção (YouTube, PlanetScale)
- Incompatível com Postgres — requereria migração de banco de dados também
- Adiciona camada de infraestrutura complexa para operar
- Latência adicional de proxy no caminho crítico
Descartado: incompatível com Postgres e introduz risco duplo
Citus (extensão Postgres para sharding)
- Sharding nativo dentro do ecossistema Postgres
- SQL distribuído com suporte a joins cross-shard
- Compatível com ferramentas existentes do ecossistema
- Adiciona complexidade operacional de extensão crítica
- Migração de schema e dados para modelo Citus seria big-bang
- Vendor lock-in para uma extensão específica
Viável, mas o custo de migração e o lock-in foram desfavoráveis no contexto
Escala vertical contínua
- Zero mudança no código da aplicação
- Operacionalmente simples
- Teto físico de hardware já próximo
- Custo exponencial por ganho linear de capacidade
- Não resolve o problema de conexões (PgBouncer tem limite)
Solução paliativa — compra tempo mas não resolve o problema estrutural
Leitura pelo AWS Well-Architected Framework
Segurança
Adequado para o contexto, mas não destacado. O post não detalha a postura de segurança, mas a arquitetura implica múltiplos endpoints de banco de dados, cada um precisando de controle de acesso independente. Em ambientes regulados (o Figma não é financeiro, mas serve empresas enterprise), a proliferação de endpoints de banco de dados aumenta a superfície de ataque e a complexidade de auditoria. Credenciais rotacionadas por shard, network policies por instância, e auditoria de acesso por shard são requisitos que escalam linearmente com o número de shards.
Confiabilidade
Forte. A migração incremental com dual-write e verificação de consistência é um padrão de alta confiabilidade. Cada fase tem rollback definido. A separação entre shards lógicos e físicos permite rebalanceamento sem rehashing completo. O risco principal é o shard map como single point of failure lógico — se não for tratado com redundância e cache adequados, uma falha nele é uma falha global. O Figma demonstra consciência disso ao tratar o shard map como infraestrutura crítica.
Eficiência de performance
Forte. O objetivo central do projeto era performance, e a arquitetura endereça isso diretamente: distribuição de carga de escrita entre múltiplos primários, redução de contenção de locks, e pool de conexões por shard em vez de um pool global. A colocação de entidades relacionadas no mesmo shard minimiza o custo de queries. O risco de degradação de performance está nas queries cross-shard não antecipadas — que, sem auditoria rigorosa, podem aparecer em produção.
Sustentabilidade
Neutro. Mais instâncias significa mais consumo de energia. Não há informação pública sobre estratégias de eficiência energética específicas para essa arquitetura. A consolidação de shards lógicos em menos shards físicos quando o crescimento estabilizar seria a alavanca de sustentabilidade mais direta.
A execução do Figma é tecnicamente sólida e o post é um dos mais honestos sobre sharding que já li. Mas há três pontos onde eu tomaria decisões diferentes — ou pelo menos questionaria mais cedo. 1. Shard map como serviço explícito, não como biblioteca embutida. Implementar o roteamento dentro da aplicação Rails é pragmático para uma stack homogênea, mas cria um problema de longo prazo: quando o Figma adicionar serviços em outras linguagens (Go, Python, o que for), cada um precisará reimplementar o cliente do shard map. Eu teria extraído o shard map como um serviço leve (um endpoint HTTP/gRPC com cache agressivo no cliente), garantindo que a lógica de roteamento fosse centralizada e versionada de forma independente do código da aplicação. O custo de latência de um cache local com TTL curto é negligenciável; o custo de manter N implementações do mesmo roteamento em N linguagens é alto. 2. Auditoria de queries cross-shard como gate obrigatório antes do sharding. O maior risco em uma migração de sharding é descobrir, em produção, que uma query crítica faz join entre entidades que foram parar em shards diferentes. Eu teria investido mais tempo — antes de qualquer linha de código de migração — em instrumentar o banco de dados monolítico para identificar todos os padrões de join, classificar entidades por afinidade de acesso, e só então definir a estratégia de particionamento. Isso é trabalho chato e lento, mas é o que separa uma migração de sharding bem-sucedida de um incidente de produção seis meses depois. **3.
Veredicto
O Figma executou uma das migrações de banco de dados mais complexas do ecossistema SaaS moderno e fez isso de forma incremental, reversível e sem downtime para o usuário final. A escolha de roteamento na aplicação em vez de um proxy externo é defensável e provavelmente correta para o contexto — stack homogênea, equipe com controle total do código, e necessidade de integração nativa com o ciclo de deploy. O que mais me impressiona nesse caso não é a tecnologia — sharding por chave é um padrão conhecido. O que impressiona é a disciplina de execução: a separação entre shards lógicos e físicos para facilitar rebalanceamento futuro, o processo de verificação de consistência antes de cada cutover, e a abordagem incremental que permitiu rollback em qualquer ponto. O post original do Figma é uma leitura obrigatória para qualquer engenheiro que trabalha com dados em escala. Ele não vende uma solução mágica — documenta o trabalho duro, os trade-offs reais, e os problemas que apareceram no caminho. Isso é raro e valioso.
Post-mortems, ADRs e deep dives de arquitetura no seu email — do jeito que um arquiteto lê.
Sem spam · cancele quando quiser
Pergunte ao Fernando sobre isto
Receba uma resposta focada sobre este estudo do meu assistente de IA, baseada no meu trabalho.
Participe da conversa
Entre para comentar
Confirme seu e-mail para participar — você também recebe a newsletter. Sem senha.