Como acelerar builds no Node.js

1. Diagnóstico do gargalo no build

Antes de otimizar, é essencial entender onde o tempo está sendo perdido. Ferramentas de profiling ajudam a identificar os pontos críticos:

# Listar dependências com tamanhos
npm ls --depth=0

# Analisar tempo de execução de scripts
npm run build -- --timing

# Profiling detalhado com webpack
webpack --profile --json > stats.json

Para builds com webpack, utilize o webpack-bundle-analyzer para visualizar o tamanho de cada módulo:

npm install --save-dev webpack-bundle-analyzer
npx webpack-bundle-analyzer stats.json

Logs detalhados no webpack.config.js:

module.exports = {
  stats: {
    timings: true,
    builtAt: true,
    performance: true,
  },
};

2. Otimização do gerenciador de pacotes

A escolha do gerenciador impacta diretamente a velocidade de instalação e cache:

# Substituir npm por pnpm (instalação paralela)
npm install -g pnpm
pnpm install

# Usar yarn com cache eficiente
yarn install --prefer-offline

# Configurar cache local no npm
npm config set cache /path/to/cache
npm install --prefer-offline

Lockfiles são cruciais para evitar redownloads:

# pnpm-lock.yaml garante instalações determinísticas
pnpm install --frozen-lockfile

# yarn.lock com yarn install --frozen-lockfile
yarn install --frozen-lockfile

3. Paralelização e concorrência no processo de build

Execute tarefas independentes simultaneamente com concurrently:

npm install --save-dev concurrently

Configuração no package.json:

{
  "scripts": {
    "lint": "eslint src/",
    "test": "jest",
    "build": "webpack --mode production",
    "dev": "concurrently \"npm run lint\" \"npm run test\" \"npm run build\""
  }
}

Para webpack, utilize thread-loader para paralelizar a transpilação:

npm install --save-dev thread-loader

Configuração no webpack.config.js:

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          'thread-loader',
          'babel-loader'
        ]
      }
    ]
  }
};

Com parallel-webpack, é possível executar múltiplas configurações em paralelo:

npm install --save-dev parallel-webpack
npx parallel-webpack --config webpack.config.js

4. Redução do escopo e do tamanho do bundle

Tree shaking elimina código morto automaticamente quando usado com módulos ES:

// webpack.config.js
module.exports = {
  mode: 'production',
  optimization: {
    usedExports: true,
    sideEffects: false,
  },
};

Code splitting com importações dinâmicas:

// Em vez de importação estática
// import { heavyModule } from './heavy';

// Use importação dinâmica
const heavyModule = await import('./heavy');

Configuração de divisão de código no webpack:

module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 20000,
      maxSize: 70000,
    },
  },
};

5. Aproveitamento de ferramentas modernas e nativas

Substitua webpack por esbuild para compilação extremamente rápida:

npm install --save-dev esbuild

Script de build com esbuild:

{
  "scripts": {
    "build": "esbuild src/index.js --bundle --outfile=dist/bundle.js --minify"
  }
}

Para desenvolvimento, Vite oferece HMR quase instantâneo:

npm create vite@latest my-app -- --template react
cd my-app
npm install
npm run dev

Para projetos Next.js, ative o turbopack:

# next.config.js
module.exports = {
  experimental: {
    turbo: true,
  },
};

6. Configuração de cache inteligente e incremental

Ative cache persistente no webpack:

module.exports = {
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename],
    },
  },
};

Para monorepos, Turborepo oferece cache de tarefas:

npm install -g turbo

Configuração no turbo.json:

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"],
      "cache": true
    }
  }
}

Build incremental no TypeScript:

{
  "compilerOptions": {
    "incremental": true,
    "tsBuildInfoFile": ".tsbuildinfo"
  }
}

7. Boas práticas de dependências e ambiente

Remova dependências não utilizadas com depcheck:

npm install -g depcheck
depcheck --ignores=eslint,jest

No CI, instale apenas dependências de produção:

npm ci --production
# ou
npm install --production

Configure variáveis de ambiente para evitar recompilações:

# .env
NODE_ENV=production
API_URL=https://api.exemplo.com

# webpack.config.js
const webpack = require('webpack');
require('dotenv').config();

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
    }),
  ],
};

Use --watch para builds incrementais durante desenvolvimento:

# webpack --watch
webpack --watch --mode development

# esbuild --watch
esbuild src/index.js --bundle --outfile=dist/bundle.js --watch

Conclusão

Acelerar builds no Node.js envolve uma combinação de diagnóstico preciso, escolha de ferramentas modernas e configurações inteligentes de cache e paralelização. Comece identificando os gargalos com profiling, substitua gerenciadores lentos por alternativas como pnpm, utilize ferramentas nativas como esbuild ou Vite, e configure caching incremental. Monorepos se beneficiam enormemente de ferramentas como Turborepo, enquanto projetos menores podem ver ganhos significativos apenas com tree shaking e code splitting.

Lembre-se de que cada projeto tem necessidades específicas; teste cada otimização em seu contexto antes de adotá-la em produção. A combinação correta pode reduzir o tempo de build de minutos para segundos.

Referências