Por que otimizar performance é crucial?

Performance não é apenas uma questão técnica - é um fator crítico de negócio. Aplicações lentas resultam em:

  • Perda de usuários (53% abandonam sites que levam mais de 3 segundos)
  • Redução de conversões
  • Maior custo de infraestrutura
  • Experiência de usuário ruim

1. Implementar Cache Estratégico

Cache é uma das técnicas mais efetivas para melhorar performance. Node.js oferece várias opções:

Cache em Memória (Redis)

Use Redis para cache de dados frequentemente acessados:

const redis = require('redis');
const client = redis.createClient();

async function getCachedData(key) {
    const cached = await client.get(key);
    if (cached) return JSON.parse(cached);
    
    const data = await fetchFromDatabase();
    await client.setex(key, 3600, JSON.stringify(data));
    return data;
}

Cache HTTP

Implemente headers de cache para recursos estáticos:

app.use(express.static('public', {
    maxAge: '1y',
    etag: true
}));

2. Otimizar Processamento Assíncrono

Node.js é single-threaded, mas excelente em I/O assíncrono. Aproveite isso:

Use Async/Await corretamente

// ❌ Ruim - operações sequenciais desnecessárias
const user = await getUser(id);
const profile = await getProfile(user.id);
const settings = await getSettings(user.id);

// ✅ Bom - operações paralelas
const [user, profile, settings] = await Promise.all([
    getUser(id),
    getProfile(id),
    getSettings(id)
]);

Streams para grandes volumes de dados

const fs = require('fs');
const readStream = fs.createReadStream('large-file.json');
const writeStream = fs.createWriteStream('output.json');

readStream.pipe(writeStream);

3. Clustering para Aproveitar Múltiplos Cores

Node.js por padrão usa apenas um core da CPU. Use clustering para aproveitar todos:

const cluster = require('cluster');
const os = require('os');

if (cluster.isMaster) {
    const numCPUs = os.cpus().length;
    for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
    }
} else {
    // Código da aplicação
    require('./app');
}

4. Otimizar Queries de Banco de Dados

Banco de dados é frequentemente o gargalo de performance:

Índices estratégicos

Crie índices em colunas frequentemente consultadas:

// MongoDB
db.users.createIndex({ email: 1 });

// PostgreSQL
CREATE INDEX idx_user_email ON users(email);

Connection Pooling

const pool = new Pool({
    max: 20,
    idleTimeoutMillis: 30000,
    connectionTimeoutMillis: 2000,
});

Evite N+1 Queries

// ❌ Ruim
users.forEach(user => {
    user.profile = await getProfile(user.id);
});

// ✅ Bom
const userIds = users.map(u => u.id);
const profiles = await getProfiles(userIds);
users.forEach(user => {
    user.profile = profiles.find(p => p.userId === user.id);
});

5. Monitoramento e Profiling

Você não pode otimizar o que não mede. Use ferramentas de monitoramento:

APM Tools

  • New Relic
  • Datadog
  • AppDynamics
  • PM2 Monitoring

Node.js Profiler

node --prof app.js
node --prof-process isolate-*.log > processed.txt

Bônus: Dicas Avançadas

Compressão Gzip

const compression = require('compression');
app.use(compression());

Lazy Loading de Módulos

// Carregue módulos pesados apenas quando necessário
const heavyModule = require('./heavy-module'); // ❌
const heavyModule = () => require('./heavy-module'); // ✅

Rate Limiting

const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
    windowMs: 15 * 60 * 1000,
    max: 100
});
app.use('/api/', limiter);

Medindo Resultados

Após implementar otimizações, meça:

  • Time to First Byte (TTFB)
  • Throughput (requests/segundo)
  • Uso de memória
  • Uso de CPU
  • Tempo de resposta de queries

Lembre-se: Otimização prematura é a raiz de todo mal. Meça primeiro, otimize depois, e sempre teste as mudanças.