We use cookies to enhance your experience on the site
CodeWorlds

Bundle optimization - analysis and package optimization

Bundle optimization is a comprehensive process of analyzing and optimizing JavaScript packages, aimed at maximizing size reduction, improving loading performance, and efficient use of browser resources. This is the last step in the web application optimization process - when we already have code splitting, tree shaking, and lazy loading, it's time for deep analysis and fine-tuning of the entire bundle.

Why is bundle optimization crucial?

Impact on Core Web Vitals

1// Performance metrics before optimization
2const beforeOptimization = {
3  bundleSize: '2.8MB',
4  firstContentfulPaint: '3.4s',
5  largestContentfulPaint: '5.1s',
6  timeToInteractive: '6.2s',
7  cumulativeLayoutShift: 0.25
8};
9
10// Performance metrics after optimization
11const afterOptimization = {
12  bundleSize: '420KB',
13  firstContentfulPaint: '0.9s',
14  largestContentfulPaint: '1.6s',
15  timeToInteractive: '2.1s',
16  cumulativeLayoutShift: 0.05
17};
18
19// Performance improvement
20const improvement = {
21  bundleSizeReduction: '85%',
22  fcpImprovement: '73%',
23  lcpImprovement: '69%',
24  ttiImprovement: '66%',
25  clsImprovement: '80%'
26};

Business costs of unoptimized bundles

1// Business impact cost analysis
2const businessImpact = {
3  conversionRate: {
4    before: '2.1%',
5    after: '3.8%',
6    improvement: '+81%'
7  },
8  bounceRate: {
9    before: '68%',
10    after: '42%',
11    improvement: '-38%'
12  },
13  mobileUsersLost: {
14    before: '35%', // Due to slow loading
15    after: '12%',
16    improvement: '-66%'
17  },
18  revenueImpact: {
19    monthlyLoss: '$24,000', // Due to slow bundles
20    monthlyGain: '$45,000'  // After optimization
21  }
22};

Bundle analysis tools

1. Webpack Bundle Analyzer

1# Installation and usage
2npm install --save-dev webpack-bundle-analyzer
3
4# Generate report
5npx webpack-bundle-analyzer dist/static/js/*.js
6
7# Integration with webpack config
8const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
9
10module.exports = {
11  plugins: [
12    new BundleAnalyzerPlugin({
13      analyzerMode: 'static',
14      openAnalyzer: false,
15      generateStatsFile: true,
16      reportFilename: 'bundle-report.html'
17    })
18  ]
19};

2. Source Map Explorer

1# Source map analysis
2npm install --global source-map-explorer
3
4# Analyze specific bundle
5source-map-explorer bundle.js bundle.js.map
6
7# Compare two versions
8source-map-explorer bundle.js --compare-to=bundle-old.js

Advanced optimization techniques

1. Dynamic chunking strategies

1// webpack.config.js - Advanced chunk configuration
2module.exports = {
3  optimization: {
4    splitChunks: {
5      chunks: 'all',
6      minSize: 20000,
7      maxSize: 244000,
8      cacheGroups: {
9        // Framework chunks (React, Angular, Vue)
10        framework: {
11          test: /[\/]node_modules[\/](react|react-dom|@angular|vue)[\/]/,
12          name: 'framework',
13          priority: 40,
14          enforce: true
15        },
16        
17        // UI Libraries chunk
18        ui: {
19          test: /[\/]node_modules[\/](@mui|antd|bootstrap|@chakra-ui)[\/]/,
20          name: 'ui-libs',
21          priority: 30,
22          enforce: true
23        },
24        
25        // Utility libraries chunk
26        utils: {
27          test: /[\/]node_modules[\/](lodash|ramda|date-fns|moment)[\/]/,
28          name: 'utils',
29          priority: 20,
30          enforce: true
31        },
32        
33        // Common application code
34        common: {
35          name: 'common',
36          minChunks: 2,
37          priority: 10,
38          reuseExistingChunk: true
39        },
40        
41        // Large libraries in separate chunks
42        largeDeps: {
43          test: /[\/]node_modules[\/](monaco-editor|three|d3)[\/]/,
44          name(module) {
45            const packageName = module.context.match(/[\/]node_modules[\/](.*?)([\/]|$)/)[1];
46            return `lib.${packageName.replace('@', '')}`;
47          },
48          priority: 35,
49          enforce: true
50        }
51      }
52    }
53  }
54};

2. Library-specific optimization

1// Date library comparison
2const dateLibraryComparison = {
3  'moment': { size: '230KB', gzipped: '67KB', treeshaking: false },
4  'date-fns': { size: '78KB', gzipped: '13KB', treeshaking: true },
5  'dayjs': { size: '9KB', gzipped: '3KB', treeshaking: true },
6  'luxon': { size: '67KB', gzipped: '19KB', treeshaking: true }
7};
8
9// Lodash optimization
10// Before (70KB)
11import _ from 'lodash';
12
13// After (2KB)
14import debounce from 'lodash/debounce';
15import throttle from 'lodash/throttle';

3. CSS framework optimization

1// PurgeCSS for removing unused styles
2const purgecss = require('@fullhuman/postcss-purgecss');
3
4module.exports = {
5  plugins: [
6    purgecss({
7      content: [
8        './src/**/*.html',
9        './src/**/*.js',
10        './src/**/*.tsx'
11      ],
12      safelist: [/^tooltip-/, /^modal-/, /^dropdown-/],
13      defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
14    })
15  ]
16};
17
18// Bundle size reduction examples
19const cssOptimizationResults = {
20  bootstrap: { before: '150KB', after: '23KB', reduction: '85%' },
21  tailwind: { before: '3.2MB', after: '12KB', reduction: '99.6%' },
22  materialUI: { before: '890KB', after: '145KB', reduction: '84%' }
23};

2. Bundle Size Analysis with CI/CD

1# .github/workflows/bundle-analysis.yml
2name: Bundle Analysis
3on: [push, pull_request]
4
5jobs:
6  analyze:
7    runs-on: ubuntu-latest
8    steps:
9      - uses: actions/checkout@v2
10
11      - name: Setup Node.js
12        uses: actions/setup-node@v2
13        with:
14          node-version: '18'
15
16      - name: Install dependencies
17        run: npm ci
18
19      - name: Build and analyze
20        run: |
21          npm run build
22          npx bundlesize
23
24      - name: Upload bundle stats
25        uses: actions/upload-artifact@v2
26        with:
27          name: bundle-stats
28          path: dist/bundle-stats.json

3. Source Map Explorer

1# Source map analysis
2npm install --global source-map-explorer
3
4# Analyze specific bundle
5source-map-explorer bundle.js bundle.js.map
6
7# Compare two versions
8source-map-explorer bundle.js --compare-to=bundle-old.js

4. Webpack Dashboard

1// Interactive dashboard during development
2const DashboardPlugin = require('webpack-dashboard/plugin');
3
4module.exports = {
5  plugins: [
6    new DashboardPlugin()
7  ]
8};

Advanced optimization techniques

1. Dynamic chunking strategies

1// webpack.config.js - Advanced chunk configuration
2module.exports = {
3  optimization: {
4    splitChunks: {
5      chunks: 'all',
6      minSize: 20000,
7      maxSize: 244000,
8      cacheGroups: {
9        // Framework chunks (React, Angular, Vue)
10        framework: {
11          test: /[\/]node_modules[\/](react|react-dom|@angular|vue)[\/]/,
12          name: 'framework',
13          priority: 40,
14          enforce: true
15        },
16
17        // UI Libraries chunk
18        ui: {
19          test: /[\/]node_modules[\/](@mui|antd|bootstrap|@chakra-ui)[\/]/,
20          name: 'ui-libs',
21          priority: 30,
22          enforce: true
23        },
24
25        // Utility libraries chunk
26        utils: {
27          test: /[\/]node_modules[\/](lodash|ramda|date-fns|moment)[\/]/,
28          name: 'utils',
29          priority: 20,
30          enforce: true
31        },
32
33        // Common application code
34        common: {
35          name: 'common',
36          minChunks: 2,
37          priority: 10,
38          reuseExistingChunk: true
39        },
40
41        // Large libraries in separate chunks
42        largeDeps: {
43          test: /[\/]node_modules[\/](monaco-editor|three|d3)[\/]/,
44          name(module) {
45            const packageName = module.context.match(/[\/]node_modules[\/](.*?)([\/]|$)/)[1];
46            return `lib.${packageName.replace('@', '')}`;
47          },
48          priority: 35,
49          enforce: true
50        }
51      }
52    }
53  }
54};

2. Module Federation for micro-frontends

1// webpack.config.js - Host application
2const ModuleFederationPlugin = require('@module-federation/webpack');
3
4module.exports = {
5  plugins: [
6    new ModuleFederationPlugin({
7      name: 'host',
8      filename: 'remoteEntry.js',
9      remotes: {
10        userDashboard: 'userDashboard@http://localhost:3001/remoteEntry.js',
11        productCatalog: 'productCatalog@http://localhost:3002/remoteEntry.js',
12        paymentSystem: 'paymentSystem@http://localhost:3003/remoteEntry.js'
13      },
14      shared: {
15        react: {
16          singleton: true,
17          requiredVersion: '^18.0.0',
18          eager: true
19        },
20        'react-dom': {
21          singleton: true,
22          requiredVersion: '^18.0.0',
23          eager: true
24        }
25      }
26    })
27  ]
28};
29
30// Micro-frontend - User Dashboard
31const ModuleFederationPlugin = require('@module-federation/webpack');
32
33module.exports = {
34  plugins: [
35    new ModuleFederationPlugin({
36      name: 'userDashboard',
37      filename: 'remoteEntry.js',
38      exposes: {
39        './UserProfile': './src/components/UserProfile',
40        './UserSettings': './src/components/UserSettings'
41      },
42      shared: {
43        react: { singleton: true },
44        'react-dom': { singleton: true }
45      }
46    })
47  ]
48};

3. Incremental bundling

1// Webpack cache configuration for faster rebuilds
2module.exports = {
3  cache: {
4    type: 'filesystem',
5    cacheDirectory: path.resolve(__dirname, '.webpack-cache'),
6    buildDependencies: {
7      config: [__filename],
8      tsconfig: [path.resolve(__dirname, 'tsconfig.json')],
9      packagejson: [path.resolve(__dirname, 'package.json')]
10    },
11    version: '1.0',
12    store: 'pack',
13    compression: 'gzip'
14  },
15
16  // Persistent cache for node_modules
17  snapshot: {
18    managedPaths: [path.resolve(__dirname, 'node_modules')],
19    buildDependencies: {
20      hash: true,
21      timestamp: true
22    },
23    module: {
24      timestamp: true
25    },
26    resolve: {
27      timestamp: true
28    }
29  }
30};

Performance budgets and monitoring

1. Performance budgets configuration

1// bundlesize configuration in package.json
2{
3  "bundlesize": [
4    {
5      "path": "./dist/js/main.*.js",
6      "maxSize": "170 kB",
7      "compression": "gzip"
8    },
9    {
10      "path": "./dist/js/vendor.*.js",
11      "maxSize": "300 kB",
12      "compression": "gzip"
13    },
14    {
15      "path": "./dist/css/*.css",
16      "maxSize": "50 kB",
17      "compression": "gzip"
18    },
19    {
20      "path": "./dist/js/runtime.*.js",
21      "maxSize": "10 kB",
22      "compression": "gzip"
23    }
24  ]
25}

2. Automated performance monitoring

1// Performance monitoring service
2class BundlePerformanceMonitor {
3  constructor() {
4    this.metrics = {
5      bundleSizes: new Map(),
6      loadTimes: new Map(),
7      cacheHitRates: new Map()
8    };
9
10    this.thresholds = {
11      maxBundleSize: 250000, // 250KB
12      maxLoadTime: 3000,     // 3s
13      minCacheHitRate: 80    // 80%
14    };
15  }
16
17  async measureBundlePerformance() {
18    const performanceData = {
19      timestamp: Date.now(),
20      metrics: await this.collectMetrics(),
21      violations: this.checkViolations(),
22      recommendations: this.generateRecommendations()
23    };
24
25    await this.sendToMonitoring(performanceData);
26    return performanceData;
27  }
28
29  async collectMetrics() {
30    const bundleEntries = performance.getEntriesByType('navigation');
31    const resourceEntries = performance.getEntriesByType('resource');
32
33    return {
34      pageLoadTime: bundleEntries[0]?.loadEventEnd - bundleEntries[0]?.fetchStart,
35      resourceLoadTimes: resourceEntries
36        .filter(entry => entry.name.includes('.js') || entry.name.includes('.css'))
37        .map(entry => ({
38          name: entry.name,
39          size: entry.transferSize,
40          loadTime: entry.responseEnd - entry.fetchStart,
41          cached: entry.transferSize === 0
42        })),
43      memoryUsage: performance.memory ? {
44        used: performance.memory.usedJSHeapSize,
45        total: performance.memory.totalJSHeapSize,
46        limit: performance.memory.jsHeapSizeLimit
47      } : null
48    };
49  }
50
51  checkViolations() {
52    const violations = [];
53
54    // Check bundle sizes
55    this.metrics.bundleSizes.forEach((size, bundleName) => {
56      if (size > this.thresholds.maxBundleSize) {
57        violations.push({
58          type: 'SIZE_VIOLATION',
59          bundle: bundleName,
60          current: size,
61          threshold: this.thresholds.maxBundleSize,
62          severity: 'HIGH'
63        });
64      }
65    });
66
67    return violations;
68  }
69
70  generateRecommendations() {
71    const recommendations = [];
72
73    // Analyze duplicates in bundles
74    const duplicatedModules = this.findDuplicatedModules();
75    if (duplicatedModules.length > 0) {
76      recommendations.push({
77        type: 'REMOVE_DUPLICATES',
78        modules: duplicatedModules,
79        estimatedSavings: this.calculateDuplicateSavings(duplicatedModules)
80      });
81    }
82
83    // Analyze unused dependencies
84    const unusedDeps = this.findUnusedDependencies();
85    if (unusedDeps.length > 0) {
86      recommendations.push({
87        type: 'REMOVE_UNUSED_DEPS',
88        dependencies: unusedDeps,
89        estimatedSavings: this.calculateUnusedDepsSavings(unusedDeps)
90      });
91    }
92
93    return recommendations;
94  }
95}
96
97// Automatic monitoring in production
98if (typeof window !== 'undefined' && process.env.NODE_ENV === 'production') {
99  const monitor = new BundlePerformanceMonitor();
100
101  // Measure after page load
102  window.addEventListener('load', () => {
103    setTimeout(() => {
104      monitor.measureBundlePerformance();
105    }, 2000);
106  });
107}

3. Real User Monitoring (RUM)

1// RUM for bundle performance
2class RealUserBundleMonitoring {
3  constructor() {
4    this.sessionId = this.generateSessionId();
5    this.startTime = performance.now();
6    this.setupObservers();
7  }
8
9  setupObservers() {
10    // Performance Observer for resource timing
11    if ('PerformanceObserver' in window) {
12      const observer = new PerformanceObserver((list) => {
13        list.getEntries().forEach(entry => {
14          if (entry.name.includes('.js') || entry.name.includes('.css')) {
15            this.trackResourceLoad(entry);
16          }
17        });
18      });
19
20      observer.observe({ entryTypes: ['resource'] });
21    }
22
23    // Long task observer
24    if ('PerformanceObserver' in window) {
25      const longTaskObserver = new PerformanceObserver((list) => {
26        list.getEntries().forEach(entry => {
27          this.trackLongTask(entry);
28        });
29      });
30
31      longTaskObserver.observe({ entryTypes: ['longtask'] });
32    }
33  }
34
35  trackResourceLoad(entry) {
36    const resourceData = {
37      sessionId: this.sessionId,
38      timestamp: Date.now(),
39      resourceType: entry.name.includes('.js') ? 'javascript' : 'css',
40      resourceName: entry.name,
41      loadTime: entry.responseEnd - entry.fetchStart,
42      transferSize: entry.transferSize,
43      cached: entry.transferSize === 0,
44      userAgent: navigator.userAgent,
45      connectionType: navigator.connection?.effectiveType
46    };
47
48    this.sendMetric('resource_load', resourceData);
49  }
50
51  trackLongTask(entry) {
52    const longTaskData = {
53      sessionId: this.sessionId,
54      timestamp: Date.now(),
55      duration: entry.duration,
56      startTime: entry.startTime,
57      attribution: entry.attribution
58    };
59
60    this.sendMetric('long_task', longTaskData);
61  }
62
63  async sendMetric(type, data) {
64    // Send to analytics system
65    if (navigator.sendBeacon) {
66      navigator.sendBeacon('/api/metrics', JSON.stringify({
67        type,
68        data,
69        metadata: {
70          url: window.location.href,
71          timestamp: Date.now(),
72          sessionId: this.sessionId
73        }
74      }));
75    }
76  }
77}

Library-specific optimization

1. Lodash optimization

1# Analyze Lodash usage
2npx lodash-cli modularize exports=node
3
4# Babel plugin for automatic optimization
5npm install --save-dev babel-plugin-lodash
1// babel.config.js
2module.exports = {
3  plugins: [
4    'lodash' // Automatically converts imports to cherry-picked
5  ]
6};
7
8// Before optimization (70KB)
9import _ from 'lodash';
10
11// After optimization (2KB)
12import debounce from 'lodash/debounce';
13import throttle from 'lodash/throttle';

2. Date library optimization

1// Date library size comparison
2const dateLibraryComparison = {
3  'moment': {
4    size: '230KB',
5    gzipped: '67KB',
6    treeshaking: false
7  },
8  'date-fns': {
9    size: '78KB',
10    gzipped: '13KB',
11    treeshaking: true
12  },
13  'dayjs': {
14    size: '9KB',
15    gzipped: '3KB',
16    treeshaking: true
17  },
18  'luxon': {
19    size: '67KB',
20    gzipped: '19KB',
21    treeshaking: true
22  }
23};
24
25// Migration guide from Moment.js to date-fns
26const migrationExamples = {
27  before: `
28    import moment from 'moment';
29
30    const formatted = moment().format('DD/MM/YYYY');
31    const isAfter = moment(date1).isAfter(date2);
32  `,
33  after: `
34    import { format, isAfter } from 'date-fns';
35    import { pl } from 'date-fns/locale';
36
37    const formatted = format(new Date(), 'dd/MM/yyyy', { locale: pl });
38    const isAfterResult = isAfter(date1, date2);
39  `
40};

3. CSS framework optimization

1// PurgeCSS for removing unused styles
2const purgecss = require('@fullhuman/postcss-purgecss');
3
4module.exports = {
5  plugins: [
6    purgecss({
7      content: [
8        './src/**/*.html',
9        './src/**/*.js',
10        './src/**/*.tsx'
11      ],
12      safelist: [
13        /^tooltip-/,
14        /^modal-/,
15        /^dropdown-/
16      ],
17      defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
18    })
19  ]
20};
21
22// Bundle size reduction examples
23const cssOptimizationResults = {
24  bootstrap: {
25    before: '150KB',
26    after: '23KB',
27    reduction: '85%'
28  },
29  tailwind: {
30    before: '3.2MB',
31    after: '12KB',
32    reduction: '99.6%'
33  },
34  materialUI: {
35    before: '890KB',
36    after: '145KB',
37    reduction: '84%'
38  }
39};

Advanced webpack optimization

1. Custom webpack plugins

1// Plugin for bundle analysis and optimization
2class BundleOptimizationPlugin {
3  constructor(options = {}) {
4    this.options = {
5      reportPath: 'bundle-optimization-report.json',
6      thresholds: {
7        maxChunkSize: 250000,
8        maxModuleSize: 50000
9      },
10      ...options
11    };
12  }
13
14  apply(compiler) {
15    compiler.hooks.emit.tapAsync('BundleOptimizationPlugin', (compilation, callback) => {
16      const report = this.generateOptimizationReport(compilation);
17
18      // Save report
19      compilation.assets[this.options.reportPath] = {
20        source: () => JSON.stringify(report, null, 2),
21        size: () => JSON.stringify(report, null, 2).length
22      };
23
24      // Log warnings
25      this.logWarnings(compilation, report);
26
27      callback();
28    });
29  }
30
31  generateOptimizationReport(compilation) {
32    const chunks = Array.from(compilation.chunks);
33    const modules = Array.from(compilation.modules);
34
35    return {
36      timestamp: new Date().toISOString(),
37      summary: {
38        totalChunks: chunks.length,
39        totalModules: modules.length,
40        totalSize: this.calculateTotalSize(chunks)
41      },
42      chunks: chunks.map(chunk => ({
43        id: chunk.id,
44        name: chunk.name,
45        size: chunk.size,
46        modules: chunk.getModules().length,
47        parents: Array.from(chunk.groupsIterable, g => g.getParents()),
48        children: Array.from(chunk.groupsIterable, g => g.getChildren())
49      })),
50      optimizationOpportunities: this.findOptimizationOpportunities(chunks, modules),
51      recommendations: this.generateRecommendations(chunks, modules)
52    };
53  }
54
55  findOptimizationOpportunities(chunks, modules) {
56    const opportunities = [];
57
58    // Find large modules
59    const largeModules = modules.filter(module =>
60      module.size() > this.options.thresholds.maxModuleSize
61    );
62
63    if (largeModules.length > 0) {
64      opportunities.push({
65        type: 'LARGE_MODULES',
66        modules: largeModules.map(m => ({
67          identifier: m.identifier(),
68          size: m.size(),
69          reasons: m.reasons.map(r => r.module?.identifier())
70        }))
71      });
72    }
73
74    // Find duplicates
75    const moduleDuplicates = this.findModuleDuplicates(modules);
76    if (moduleDuplicates.length > 0) {
77      opportunities.push({
78        type: 'MODULE_DUPLICATES',
79        duplicates: moduleDuplicates
80      });
81    }
82
83    return opportunities;
84  }
85}
86
87// Plugin usage
88module.exports = {
89  plugins: [
90    new BundleOptimizationPlugin({
91      thresholds: {
92        maxChunkSize: 200000,
93        maxModuleSize: 30000
94      }
95    })
96  ]
97};

2. Runtime chunk optimization

1// Runtime chunk optimization
2module.exports = {
3  optimization: {
4    runtimeChunk: {
5      name: entrypoint => `runtime-${entrypoint.name}`
6    },
7
8    // Minimizer configuration
9    minimizer: [
10      new TerserPlugin({
11        parallel: true,
12        terserOptions: {
13          parse: {
14            ecma: 2020
15          },
16          compress: {
17            ecma: 2020,
18            comparisons: false,
19            inline: 2,
20            drop_console: process.env.NODE_ENV === 'production',
21            drop_debugger: process.env.NODE_ENV === 'production',
22            pure_funcs: ['console.log', 'console.info', 'console.debug']
23          },
24          mangle: {
25            safari10: true
26          },
27          output: {
28            ecma: 2020,
29            comments: false,
30            ascii_only: true
31          }
32        }
33      }),
34
35      new CssMinimizerPlugin({
36        parallel: true,
37        minimizerOptions: {
38          preset: [
39            'default',
40            {
41              discardComments: { removeAll: true },
42              normalizeWhitespace: true,
43              colormin: true,
44              convertValues: true,
45              discardDuplicates: true,
46              discardEmpty: true,
47              mergeRules: true,
48              minifyFontValues: true,
49              minifyGradients: true,
50              minifyParams: true,
51              minifySelectors: true,
52              normalizeCharset: true,
53              normalizeDisplayValues: true,
54              normalizePositions: true,
55              normalizeRepeatStyle: true,
56              normalizeString: true,
57              normalizeTimingFunctions: true,
58              normalizeUnicode: true,
59              normalizeUrl: true,
60              orderedValues: true,
61              reduceIdents: true,
62              reduceInitial: true,
63              reduceTransforms: true,
64              svgo: true,
65              uniqueSelectors: true
66            }
67          ]
68        }
69      })
70    ]
71  }
72};

Bundle testing and validation

1. Automated bundle testing

1// Test suite for bundle optimization
2const bundleTests = {
3  // Bundle size test
4  async testBundleSize() {
5    const bundleStats = await getBundleStats();
6    const maxSize = 500000; // 500KB
7
8    expect(bundleStats.totalSize).toBeLessThan(maxSize);
9    expect(bundleStats.mainChunk).toBeLessThan(200000);
10    expect(bundleStats.vendorChunk).toBeLessThan(300000);
11  },
12
13  // Duplicate test
14  async testNoDuplicateModules() {
15    const bundleAnalysis = await analyzeBundleContent();
16    const duplicates = bundleAnalysis.duplicatedModules;
17
18    expect(duplicates).toHaveLength(0);
19  },
20
21  // Tree shaking effectiveness test
22  async testTreeShakingEffectiveness() {
23    const unusedExports = await findUnusedExports();
24    const deadCode = await findDeadCode();
25
26    expect(unusedExports.length).toBeLessThan(5);
27    expect(deadCode.totalSize).toBeLessThan(10000);
28  },
29
30  // Cache effectiveness test
31  async testCacheability() {
32    const chunks = await getChunkManifest();
33
34    chunks.forEach(chunk => {
35      if (chunk.type === 'vendor') {
36        expect(chunk.hash).toMatch(/^[a-f0-9]{8}$/);
37        expect(chunk.cacheable).toBe(true);
38      }
39    });
40  }
41};
42
43// CI/CD integration
44describe('Bundle Optimization Tests', () => {
45  test('Bundle size within limits', bundleTests.testBundleSize);
46  test('No duplicate modules', bundleTests.testNoDuplicateModules);
47  test('Tree shaking effective', bundleTests.testTreeShakingEffectiveness);
48  test('Chunks properly cacheable', bundleTests.testCacheability);
49});

2. Performance regression detection

1// System for detecting performance regressions
2class PerformanceRegressionDetector {
3  constructor() {
4    this.baselineMetrics = null;
5    this.threshold = 0.1; // 10% increase = regression
6  }
7
8  async setBaseline() {
9    this.baselineMetrics = await this.measureCurrentMetrics();
10    await this.saveBaseline(this.baselineMetrics);
11  }
12
13  async detectRegressions() {
14    const currentMetrics = await this.measureCurrentMetrics();
15    const regressions = [];
16
17    if (!this.baselineMetrics) {
18      this.baselineMetrics = await this.loadBaseline();
19    }
20
21    // Check bundle sizes
22    const sizeRegression = this.detectSizeRegression(
23      this.baselineMetrics.bundleSize,
24      currentMetrics.bundleSize
25    );
26
27    if (sizeRegression) {
28      regressions.push(sizeRegression);
29    }
30
31    // Check load times
32    const loadTimeRegression = this.detectLoadTimeRegression(
33      this.baselineMetrics.loadTime,
34      currentMetrics.loadTime
35    );
36
37    if (loadTimeRegression) {
38      regressions.push(loadTimeRegression);
39    }
40
41    return regressions;
42  }
43
44  detectSizeRegression(baseline, current) {
45    const increase = (current - baseline) / baseline;
46
47    if (increase > this.threshold) {
48      return {
49        type: 'SIZE_REGRESSION',
50        baseline: baseline,
51        current: current,
52        increase: `${(increase * 100).toFixed(1)}%`,
53        severity: increase > 0.25 ? 'HIGH' : 'MEDIUM'
54      };
55    }
56
57    return null;
58  }
59
60  async generateRegressionReport(regressions) {
61    return {
62      timestamp: new Date().toISOString(),
63      regressions: regressions,
64      recommendations: await this.generateRecommendations(regressions),
65      actionItems: this.generateActionItems(regressions)
66    };
67  }
68}

Production deployment strategies

1. Progressive deployment

1// Progressive deployment strategy for bundles
2class ProgressiveBundleDeployment {
3  constructor() {
4    this.deploymentStages = [
5      { name: 'canary', percentage: 5 },
6      { name: 'blue-green', percentage: 50 },
7      { name: 'full', percentage: 100 }
8    ];
9  }
10
11  async deployNewBundle(bundleVersion) {
12    for (const stage of this.deploymentStages) {
13      console.log(`Deploying to ${stage.name} (${stage.percentage}% traffic)`);
14
15      await this.deployToStage(bundleVersion, stage);
16
17      const metrics = await this.monitorStageMetrics(stage, 300000); // 5 min
18
19      if (!this.validateStageSuccess(metrics)) {
20        await this.rollbackStage(stage);
21        throw new Error(`Deployment failed at ${stage.name} stage`);
22      }
23
24      console.log(`Stage ${stage.name} successful`);
25    }
26  }
27
28  async monitorStageMetrics(stage, duration) {
29    const startTime = Date.now();
30    const metrics = {
31      errorRate: [],
32      loadTimes: [],
33      bundleErrors: []
34    };
35
36    while (Date.now() - startTime < duration) {
37      const sample = await this.collectMetricsSample(stage);
38      metrics.errorRate.push(sample.errorRate);
39      metrics.loadTimes.push(sample.avgLoadTime);
40      metrics.bundleErrors.push(sample.bundleErrors);
41
42      await new Promise(resolve => setTimeout(resolve, 30000)); // 30s intervals
43    }
44
45    return metrics;
46  }
47
48  validateStageSuccess(metrics) {
49    const avgErrorRate = metrics.errorRate.reduce((a, b) => a + b) / metrics.errorRate.length;
50    const avgLoadTime = metrics.loadTimes.reduce((a, b) => a + b) / metrics.loadTimes.length;
51    const totalBundleErrors = metrics.bundleErrors.reduce((a, b) => a + b);
52
53    return avgErrorRate < 0.01 && // < 1% error rate
54           avgLoadTime < 3000 &&  // < 3s load time
55           totalBundleErrors < 5; // < 5 bundle errors
56  }
57}

2. A/B testing for bundle optimization

1// A/B testing different bundle optimization strategies
2class BundleOptimizationABTest {
3  constructor() {
4    this.variants = {
5      control: {
6        strategy: 'current',
7        splitChunks: 'default',
8        compression: 'gzip'
9      },
10      optimized: {
11        strategy: 'aggressive_splitting',
12        splitChunks: 'custom',
13        compression: 'brotli'
14      },
15      experimental: {
16        strategy: 'module_federation',
17        splitChunks: 'micro_chunks',
18        compression: 'zstd'
19      }
20    };
21  }
22
23  async runABTest(duration = 7 * 24 * 60 * 60 * 1000) { // 7 days
24    const testId = this.generateTestId();
25
26    // Start test
27    await this.startTest(testId);
28
29    // Monitor metrics
30    const results = await this.monitorTest(testId, duration);
31
32    // Analyze results
33    const analysis = await this.analyzeResults(results);
34
35    // Select winner
36    const winner = this.selectWinner(analysis);
37
38    return {
39      testId,
40      duration,
41      results,
42      analysis,
43      winner,
44      recommendation: this.generateRecommendation(analysis, winner)
45    };
46  }
47
48  async monitorTest(testId, duration) {
49    const results = {
50      control: { metrics: [], samples: 0 },
51      optimized: { metrics: [], samples: 0 },
52      experimental: { metrics: [], samples: 0 }
53    };
54
55    const startTime = Date.now();
56
57    while (Date.now() - startTime < duration) {
58      for (const variant of Object.keys(this.variants)) {
59        const metrics = await this.collectVariantMetrics(variant);
60        results[variant].metrics.push(metrics);
61        results[variant].samples++;
62      }
63
64      await new Promise(resolve => setTimeout(resolve, 3600000)); // 1 hour
65    }
66
67    return results;
68  }
69
70  selectWinner(analysis) {
71    const scores = {};
72
73    for (const variant of Object.keys(analysis)) {
74      const data = analysis[variant];
75
76      scores[variant] = (
77        (1 / data.avgLoadTime) * 0.4 +     // 40% weight
78        (1 / data.avgBundleSize) * 0.3 +   // 30% weight
79        data.conversionRate * 0.2 +        // 20% weight
80        (1 - data.errorRate) * 0.1         // 10% weight
81      );
82    }
83
84    return Object.keys(scores).reduce((a, b) =>
85      scores[a] > scores[b] ? a : b
86    );
87  }
88}

Summary and checklist

Bundle Optimization Checklist

1const bundleOptimizationChecklist = {
2  analysis: [
3    '✓ Webpack Bundle Analyzer setup',
4    '✓ Source map analysis',
5    '✓ Performance budget configured',
6    '✓ CI/CD bundle size monitoring',
7    '✓ Duplicate detection'
8  ],
9
10  codeOptimization: [
11    '✓ Tree shaking enabled',
12    '✓ Dead code elimination',
13    '✓ Unused dependencies removed',
14    '✓ Code splitting implemented',
15    '✓ Lazy loading configured'
16  ],
17
18  bundleStrategy: [
19    '✓ Chunk splitting optimized',
20    '✓ Vendor chunks separated',
21    '✓ Runtime chunk extracted',
22    '✓ Common chunks identified',
23    '✓ Dynamic imports utilized'
24  ],
25
26  compression: [
27    '✓ Gzip/Brotli enabled',
28    '✓ Minification configured',
29    '✓ CSS optimization',
30    '✓ Image optimization',
31    '✓ Font optimization'
32  ],
33
34  caching: [
35    '✓ Long-term caching setup',
36    '✓ Content-based hashing',
37    '✓ Cache invalidation strategy',
38    '✓ Service worker implemented',
39    '✓ CDN optimization'
40  ],
41
42  monitoring: [
43    '✓ Real user monitoring',
44    '✓ Performance metrics tracking',
45    '✓ Regression detection',
46    '✓ A/B testing capability',
47    '✓ Alert system configured'
48  ]
49};
50
51// ROI calculation
52const optimizationROI = {
53  investment: {
54    developmentTime: '2-3 weeks',
55    toolingSetup: '$5,000',
56    ongoingMaintenance: '$2,000/month'
57  },
58
59  returns: {
60    conversionRateIncrease: '+25-40%',
61    bounceRateDecrease: '-30-50%',
62    serverCostReduction: '-20-30%',
63    mobileUserRetention: '+35-60%',
64    seoRankingImprovement: '+15-25%'
65  },
66
67  businessImpact: {
68    revenueIncrease: '$50,000-150,000/year',
69    costSavings: '$20,000-40,000/year',
70    paybackPeriod: '2-4 months',
71    totalROI: '400-800%'
72  }
73};

Bundle optimization is an investment that pays back very quickly through improvement of all key business metrics. A systematic approach, continuous monitoring, and iterative improvements allow achieving and maintaining optimal application performance.

Go to CodeWorlds