Web performance budget: como definir e respeitar limites de tamanho

1. O que é um Performance Budget e por que ele é crítico hoje

Um performance budget (orçamento de performance) é um conjunto de limites estabelecidos para métricas que afetam a experiência do usuário e o desempenho de uma aplicação web. O conceito surgiu da necessidade de tratar performance como um requisito não funcional mensurável, assim como tratamos orçamentos financeiros em projetos.

O impacto real no negócio é inegável: estudos do Google mostram que 53% dos usuários abandonam sites que demoram mais de 3 segundos para carregar. Para cada segundo adicional de carregamento, as taxas de conversão podem cair até 20%. Além disso, desde 2021, o Google utiliza as Core Web Vitals como fator de ranqueamento, tornando a performance um requisito de SEO.

Existem duas abordagens principais para definir um performance budget:

  • Quantitativo: foca em limites de peso total (ex: 500KB de JavaScript, 1MB de imagens) e tempo de carregamento (ex: TTI < 3s em 3G).
  • Qualitativo: foca em métricas de experiência como Largest Contentful Paint (LCP < 2.5s), First Input Delay (FID < 100ms) e Cumulative Layout Shift (CLS < 0.1).

2. Métricas essenciais para definir seu orçamento

Para criar um orçamento eficaz, é preciso monitorar métricas específicas:

Time to Interactive (TTI) e First Contentful Paint (FCP) são métricas que se relacionam diretamente com o tamanho dos recursos. Um bundle de JavaScript de 500KB pode levar 2 segundos para ser processado em dispositivos móveis médios.

Peso máximo por tipo de recurso:

JavaScript: 300KB (inicial) - 500KB (máximo)
CSS: 50KB (inicial) - 100KB (máximo)
Imagens: 200KB (por imagem) - 1MB (total na viewport)
Fontes: 50KB (total)

Lighthouse Score mínimo: defina uma meta de pelo menos 90 em performance para dispositivos móveis, 90 em acessibilidade e 90 em boas práticas.

3. Como estabelecer limites realistas para cada recurso

O primeiro passo é realizar um benchmark contra concorrentes e médias do setor. O HTTP Archive fornece dados atualizados sobre o peso médio de páginas web:

Média global (2024):
- Peso total da página: 2.5MB
- JavaScript: 500KB
- Imagens: 1.2MB
- CSS: 80KB

Para calcular o budget baseado na conexão real do usuário, considere:

Conexão 3G lenta: 400Kbps
Conexão 4G média: 5Mbps
Conexão 5G: 50Mbps

Budget calculado para 3G (carregamento em 5 segundos):
- Peso máximo: 400Kbps * 5s = 2Mbps = 250KB

Adote uma estratégia de orçamento progressivo:

  • Budget inicial: 500KB JS, 200KB CSS, 1MB imagens (para MVP)
  • Budget intermediário: 300KB JS, 100KB CSS, 500KB imagens (após otimizações)
  • Budget ideal: 200KB JS, 50KB CSS, 300KB imagens (para máxima performance)

4. Ferramentas para monitorar e validar o orçamento

WebPageTest e Lighthouse CI permitem automatizar a validação em pipelines de CI/CD:

# Exemplo de configuração no Lighthouse CI
{
  "ci": {
    "assert": {
      "assertions": {
        "categories:performance": ["error", {"minScore": 0.9}],
        "resource-summary:script:size": ["error", {"maxNumericValue": 300000}],
        "resource-summary:stylesheet:size": ["error", {"maxNumericValue": 50000}]
      }
    }
  }
}

Bundlers e plugins:
- webpack-bundle-analyzer: visualiza o tamanho de cada módulo
- size-limit: verifica o tamanho do bundle em PRs
- bundlesize: compara o tamanho atual com o orçamento definido

Budgets nativos no Chrome DevTools e no Next.js:

// next.config.js
module.exports = {
  experimental: {
    performanceBudget: {
      totalSize: 500000, // 500KB
      imageSize: 200000, // 200KB
      fontSize: 50000    // 50KB
    }
  }
}

5. Técnicas para respeitar o orçamento de JavaScript

Code splitting por rota e por componente:

// Em vez de importar tudo de uma vez
import { Chart } from 'heavy-chart-library'

// Use dynamic imports
const Chart = dynamic(() => import('heavy-chart-library'), {
  loading: () => <Skeleton />
})

Tree shaking eficiente e remoção de dependências desnecessárias:

// Verifique o que está sendo importado
import { map } from 'lodash'  // Evite importar toda a biblioteca
// Prefira:
const map = (arr, fn) => arr.map(fn)

Substituição de bibliotecas pesadas:

Bibliotecas pesadas → Alternativas leves
jQuery (87KB) → Vanilla JS ou Alpine.js (7KB)
Moment.js (230KB) → date-fns (17KB) ou Day.js (2KB)
Lodash (71KB) → Funções nativas ou micro-libraries

6. Otimização de assets visuais para manter o budget

Imagens responsivas com srcset e formatos modernos:

<img 
  src="foto-800.webp" 
  srcset="foto-400.webp 400w, foto-800.webp 800w, foto-1200.webp 1200w"
  sizes="(max-width: 600px) 100vw, 800px"
  alt="Descrição"
  loading="lazy"
/>

Fontes sob medida:

/* Subsetting: remova caracteres desnecessários da fonte */
/* font-display: swap evita FOIT (Flash of Invisible Text) */
@font-face {
  font-family: 'MinhaFonte';
  src: url('/fonts/minhafonte-subset.woff2') format('woff2');
  font-display: swap;
  unicode-range: U+0000-00FF; /* Apenas caracteres latinos básicos */
}

CSS crítico inline e lazy loading de estilos não essenciais:

<!-- CSS crítico inline no <head> -->
<style>
  /* Estilos para o conteúdo acima da dobra */
  header, nav, .hero { display: block; }
</style>

<!-- CSS não crítico carregado assíncronamente -->
<link rel="preload" href="/styles/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">

7. Automatizando a governança do orçamento no fluxo de desenvolvimento

Integração contínua com falha automática:

# .github/workflows/performance-budget.yml
name: Performance Budget Check
on: [pull_request]
jobs:
  budget:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm run build
      - run: npx size-limit
      - run: npx lighthouse-ci https://staging.example.com

Alertas e dashboards para equipes de frontend podem ser configurados com ferramentas como Datadog, Grafana ou até mesmo planilhas compartilhadas que monitoram a evolução do peso do bundle a cada deploy.

Revisão periódica do orçamento: a cada trimestre, reavalie os limites com base na evolução da aplicação, nos novos recursos e no perfil dos usuários (que podem estar migrando para conexões mais rápidas).

8. Casos reais e armadilhas comuns ao definir budgets

Exemplo para um portal editorial vs. uma SPA pesada:

Portal editorial (notícias):
- Budget: 200KB JS, 50KB CSS, 500KB imagens
- Prioridade: FCP < 1.5s, LCP < 2s

SPA de dashboard (gráficos interativos):
- Budget: 500KB JS, 100KB CSS, 200KB imagens
- Prioridade: TTI < 3s, interatividade rápida

O erro de focar apenas no peso total: um site com 1MB de JS bem dividido em chunks pode carregar mais rápido que um com 300KB de JS monolítico. A latência da rede e a forma como os recursos são entregues importam tanto quanto o peso.

Como lidar com exceções justificadas: animações complexas, gráficos interativos ou bibliotecas de terceiros (como players de vídeo) podem estourar o budget. Nesses casos, documente a exceção, estabeleça um prazo para revisão e considere carregar esses recursos apenas sob demanda.

// Exemplo de justificativa documentada
/*
 * Exceção aprovada em 15/03/2024
 * Biblioteca de gráficos: 150KB (orçamento: 50KB)
 * Motivo: visualização de dados complexa para o módulo de analytics
 * Revisão agendada: 15/06/2024
 * Responsável: João Silva
 */

Referências