Técnicas de Performance que as Big Techs Usam (e você pode aplicar hoje)
Speculation Rules API, Optimistic UI, Streaming SSR, Priority Hints, Virtual Scrolling, Web Workers, SWR e INP — o que Google, Shopify, Netflix, Meta e Figma fazem de diferente pra ter apps que parecem instantâneos.
Você abre o Linear, clica num card, a navegação é instantânea. Abre o Figma, arrasta um elemento com centenas de layers, sem travar. Usa o Shopify, clica num produto, a página já tá lá.
Isso não é mágica. É um conjunto de técnicas de performance que as big techs refinaram ao longo de anos — e que estão disponíveis pra qualquer dev usar.
Vou mostrar 8 técnicas, com os contextos reais de quem usa e exemplos práticos de como aplicar.
🔮 1. Speculation Rules API
A técnica mais relevante do momento. O browser prediz qual página o usuário vai acessar e faz prefetch ou prerender antes da navegação acontecer.
Tem dois modos:
- prefetch → paga o custo de TTFB antecipado (só baixa o HTML)
- prerender → renderiza a página inteira em background (navegação parece instantânea)
O Shopify configurou com eagerness: conservative — o browser faz prefetch no momento em que o usuário pressiona o botão do mouse, antes mesmo de soltar.
<script type="speculationrules">
{
"prefetch": [{ "where": { "href_matches": "/*" }, "eagerness": "immediate" }],
"prerender": [{ "where": { "href_matches": "/*" }, "eagerness": "moderate" }]
}
</script>
⚠️ Cuidados importantes
- Não especule rotas destrutivas como
/logoutou/delete - Prerender executa todos os scripts — pode causar overreporting em analytics (a página é "visitada" antes do usuário de fato navegar)
- Use
eagerness: conservativeoumoderatepra não desperdiçar banda
Quem usa: Google, Shopify, Cloudflare
⚡ 2. Optimistic UI
Atualiza a UI antes da confirmação do servidor. Se falhar, faz rollback. É o que faz o Linear parecer que tudo acontece instantaneamente.
O fluxo é simples:
- Usuário faz uma ação
- UI atualiza imediatamente com um estado temporário
- Requisição vai pro servidor em paralelo
- Se der certo: substitui o temporário pelo dado real
- Se der errado: reverte e mostra o erro
// Svelte store com optimistic update
function createEntry(data) {
const tempId = crypto.randomUUID()
entries.update(list => [{ ...data, id: tempId, pending: true }, ...list])
api.post('/entries', data)
.then(real => entries.update(list =>
list.map(e => e.id === tempId ? real : e)
))
.catch(() => entries.update(list =>
list.filter(e => e.id !== tempId)
))
}
O estado pending: true serve pra mostrar um indicador visual sutil (loading spinner no item, opacity reduzida) enquanto confirma no servidor.
Quem usa: Meta, Linear, Vercel
🌊 3. Streaming SSR + Suspense
Netflix reduziu o TTI em 50% controlando exatamente o que é renderizado no servidor vs. cliente. A ideia: não bloquear a renderização esperando dados secundários.
No SvelteKit isso é nativo via load() com streaming — dados sem await são enviados de forma incremental:
// +page.server.js
export async function load() {
return {
// dados críticos: aguarda antes de enviar o HTML
user: await getUser(),
// dados secundários: streaming (não bloqueia a renderização)
entries: getEntries(), // sem await — chega depois
}
}
No React/Next.js, o equivalente é <Suspense> com Server Components. O shell da página chega imediatamente, os dados lentos chegam depois e hidratam o componente no lugar certo.
Quem usa: Netflix, Vercel
🎯 4. Priority Hints
Diz pro browser o que é importante agora e o que pode esperar. Simples, mas poucos usam.
<!-- Imagem principal acima do fold: alta prioridade -->
<img fetchpriority="high" src="/hero.webp">
<!-- Imagens abaixo do fold: baixa prioridade -->
<img fetchpriority="low" loading="lazy" src="/cover.webp">
// Fetch da IA com alta prioridade — usuário está esperando
fetch('/api/generate', { priority: 'high' })
// Analytics: baixa prioridade, não bloqueia nada
fetch('/api/track', { priority: 'low' })
O impacto no LCP pode ser significativo. Se o browser trata sua imagem hero com a mesma prioridade que um pixel de rastreamento, você tá perdendo performance de graça.
Quem usa: Google
📜 5. Virtual Scrolling
Timeline com centenas (ou milhares) de registros — o DOM renderiza só o que está visível na tela. O resto é calculado matematicamente mas não existe no DOM.
O Google Sheets faz isso com suas células. O Linear faz com issues. O Notion faz com blocos longos.
// Com svelte-virtual (equivalente ao react-window no React)
import { VirtualList } from 'svelte-virtual'
<VirtualList items={entries} itemHeight={80} let:item>
<EntryCard {item} />
</VirtualList>
Sem virtual scrolling, uma lista de 5000 itens cria 5000 nós no DOM. Com virtual scrolling, você cria só os ~15 que aparecem na tela, independente do tamanho da lista.
Quem usa: Google Sheets, Linear, Notion
🧵 6. Web Workers para Operações Pesadas
Geração de PDF, cálculo de embeddings, parsing de arquivos grandes — tudo isso bloqueia a main thread se rodar no componente. A solução: mover pra um Web Worker.
O Figma roda praticamente toda a lógica de renderização em Workers, mantendo a UI fluida mesmo com operações pesadas.
// worker.js
self.onmessage = async ({ data }) => {
const pdf = await generatePDF(data.entries)
self.postMessage({ pdf })
}
// componente
const worker = new Worker('/workers/pdf.js')
worker.postMessage({ entries })
worker.onmessage = ({ data }) => downloadPDF(data.pdf)
A regra geral: qualquer operação que leva mais de 50ms é candidata a ir pra um Worker.
Quem usa: Figma, Google Docs
🔄 7. SWR / Stale-While-Revalidate
Mostra o dado em cache imediatamente, revalida em background. O usuário vê algo útil na hora, e os dados ficam frescos em seguida.
No SvelteKit, você pode configurar isso diretamente nos headers de resposta:
// +page.server.js
export async function load({ fetch, setHeaders }) {
setHeaders({
'cache-control': 'max-age=60, stale-while-revalidate=300'
})
return {
entries: await fetch('/api/entries').then(r => r.json())
}
}
Com essa config: dados com menos de 60s são servidos do cache direto. Entre 60s e 5min, o cache velho é servido enquanto a revalidação acontece em background.
Quem usa: Vercel (criou o padrão com a lib SWR), GitHub
📊 8. INP — A Nova Métrica Core Web Vitals
Em março de 2024, o INP (Interaction to Next Paint) substituiu o FID como Core Web Vital. A diferença é sutil mas importante:
- FID mede só a primeira interação do usuário
- INP mede todas as interações — cliques, toques, teclas — e retorna a pior
Isso significa que uma página pode ter FID excelente mas INP ruim se tiver alguma interação lenta no meio da sessão.
Como otimizar INP
scheduler.yield() — cede controle ao browser entre tarefas longas:
async function handleGenerateAI() {
updateUI('loading')
await scheduler.yield() // browser pinta o loading antes de continuar
const result = await callClaude()
updateUI('done', result)
}
Outras técnicas:
- Debounce em inputs que disparam cálculos pesados
- Evitar layout thrashing — ler e escrever DOM separadamente
- Quebrar tarefas longas com
setTimeout(fn, 0)ouscheduler.postTask()
Quem criou e monitora: Google
📋 Resumo por Empresa
| Técnica | Quem usa |
|---|---|
| Speculation Rules API | Google, Shopify, Cloudflare |
| Optimistic UI | Meta, Linear, Vercel |
| Streaming SSR | Netflix, Vercel |
| Priority Hints | |
| Virtual Scrolling | Google Sheets, Linear, Notion |
| Web Workers | Figma, Google Docs |
| SWR | Vercel, GitHub |
| INP optimization | Google (própria métrica) |
Nenhuma dessas técnicas exige um framework específico ou uma infraestrutura especial. Speculation Rules é um <script> no HTML. Priority Hints é um atributo. SWR é um header HTTP.
O que separa apps lentos de apps rápidos geralmente não é uma grande reescrita — é a soma de pequenas decisões bem tomadas.
Para discussões técnicas ou dúvidas sobre implementação, me encontre no LinkedIn ou GitHub.
Acompanhe mais conteúdo em eliseu.dev e na MFE Brasil Newsletter.