Performance
Escopo: JavaScript. Visão transversal: shared/platform/performance.md.
Estas diretrizes se aplicam a hot paths (caminhos quentes, fluxos executados em volume ou frequência alta): loops apertados, handlers de requisição, processamento de stream. Fora desse contexto, prefira legibilidade. Premature optimization (otimização prematura) custa clareza sem ganho real: meça antes de otimizar.
Conceitos fundamentais
| Conceito | O que é |
|---|---|
| hot path (caminho quente) | Trecho de código executado em volume ou frequência alta; otimizar aqui rende |
| cold path (caminho frio) | Trecho raro (inicialização, erro); otimizar aqui é desperdício de clareza |
| V8 (engine do Chrome/Node.js) | Motor JavaScript do Node.js e Chrome; otimiza quando o código é previsível e mantém formas estáveis de objeto |
| JIT (Just-In-Time, Compilação Sob Demanda) | Compilador que traduz JS para código de máquina em tempo de execução |
| allocation (alocação) | Criação de objeto na heap; pressão de alocação custa GC |
| GC (Garbage Collection, Coleta de Lixo) | Liberação automática de memória; pausas afetam latência |
| Big O (notação assintótica) | Custo do algoritmo em função do tamanho da entrada (O(n), O(n²), O(log n)) |
| profiling (perfilamento) | Medição empírica de onde tempo e memória são gastos; node --prof, clinic, Chrome DevTools |
| stream (fluxo) | Leitura/escrita em pedaços; evita carregar arquivo inteiro na memória |
for...of vs forEach
forEach executa um callback (função de retorno) por iteração: há custo de chamada de função e criação de contexto de
execução a cada item. Em hot paths, for...of itera diretamente sobre o iterável, sem callback.
❌ Ruim: callback alocado por iteração
function calculateTotalRevenue(orders) {
let total = 0;
orders.forEach((order) => {
total += order.amount;
});
return total;
}
✅ Bom: for...of sem overhead de callback
function calculateTotalRevenue(orders) {
let total = 0;
for (const order of orders) {
total += order.amount;
}
return total;
}
Set para membership
Array.includes() percorre o array do início ao fim: O(n) por verificação. Set.has() resolve em
O(1) via hash. Para listas fixas verificadas com frequência, defina o Set uma vez no módulo e
reutilize.
❌ Ruim: Array.includes percorre tudo a cada chamada
const PREMIUM_CATEGORIES = ["electronics", "jewelry", "watches"];
function filterPremiumProducts(products) {
const premiumProducts = products.filter((product) =>
PREMIUM_CATEGORIES.includes(product.category)
);
return premiumProducts;
}
✅ Bom: Set.has resolve em O(1)
const PREMIUM_CATEGORIES = new Set(["electronics", "jewelry", "watches"]);
function filterPremiumProducts(products) {
const premiumProducts = products.filter((product) =>
PREMIUM_CATEGORIES.has(product.category)
);
return premiumProducts;
}
ID: UUID v4 vs UUID v7
crypto.randomUUID() gera UUID (Universally Unique Identifier, Identificador Único Universal) v4, que é aleatório. Inserções aleatórias fragmentam o índice primário
progressivamente. UUID v7 é time-ordered: insere sempre próximo ao fim da B-tree, sem fragmentação.
Veja o impacto no banco em sql/conventions/advanced/performance.md.
❌ Ruim: crypto.randomUUID() é v4: random, fragmenta índice
function createOrder(request) {
const orderId = crypto.randomUUID(); // v4: random, page splits no banco
return saveOrder({ id: orderId, ...request });
}
✅ Bom: UUID v7: time-ordered, sequencial no índice
import { v7 as uuidv7 } from "uuid";
function createOrder(request) {
const orderId = uuidv7(); // time-ordered: sequencial no índice, sem fragmentação
return saveOrder({ id: orderId, ...request });
}
String building
Concatenação com + ou template literal dentro de um loop aloca uma nova string a cada iteração:
strings são imutáveis em JavaScript. Para construir strings dinamicamente, acumule em array e chame
join() no final. Uma alocação, resultado único.
❌ Ruim: nova string alocada por iteração
function buildOrderReport(orders) {
let report = "";
for (const order of orders) {
report += `#${order.id}: ${order.customer}, ${order.total}\n`;
}
return report;
}
✅ Bom: array + join, uma alocação no final
function buildOrderReport(orders) {
const lines = [];
for (const order of orders) {
lines.push(`#${order.id}: ${order.customer}, ${order.total}`);
}
const report = lines.join("\n");
return report;
}
Desenvolvido por @thiagocajadev · Fork baseado no repositório pmndrs/docs · Poimandres.