Performance Optimization at Enterprise Scale
Deep dive into performance optimization strategies, profiling, monitoring, and optimization patterns for large-scale applications.
Topics Covered:
Prerequisites:
- Creating Your Own React Library
Overview
At enterprise scale, performance optimization is a systematic discipline requiring measurement, optimization, and continuous monitoring. This tutorial covers the complete performance optimization lifecycle used by companies like Google, Facebook, and Netflix. You'll learn profiling techniques, bundle optimization strategies, runtime performance patterns, monitoring solutions, and how to establish performance budgets and SLAs for large-scale React applications serving millions of users.
Lesson 1: Establishing Performance Metrics and Goals
Before optimizing, establish what you're optimizing for. Different metrics matter at different stages. Core Web Vitals (Google's Metrics): • LCP (Largest Contentful Paint): < 2.5s • FID (First Input Delay): < 100ms • CLS (Cumulative Layout Shift): < 0.1 Performance Metrics: • Time to First Byte (TTFB): < 600ms • First Contentful Paint (FCP): < 1.8s • Time to Interactive (TTI): < 3.8s • Total Blocking Time (TBT): < 200ms • Bundle Size: Track JavaScript bundle sizes Setting Performance Budgets: • Maximum bundle size per route • Maximum number of requests • Performance budgets in CI/CD • Alerting thresholds Real User Monitoring (RUM): • Collect metrics from real users • Identify slow devices/networks • Track p50, p75, p95, p99 percentiles
// Example: Performance budget configuration
// .budgetrc.js or package.json
module.exports = {
budget: [
{
path: '/',
timings: [
{
metric: 'interactive',
budget: 3000, // 3 seconds
},
{
metric: 'first-meaningful-paint',
budget: 2000,
},
],
resourceSizes: [
{
resourceType: 'script',
budget: 200, // 200KB
},
{
resourceType: 'total',
budget: 500, // 500KB total
},
],
resourceCounts: [
{
resourceType: 'third-party',
budget: 10, // Max 10 third-party requests
},
],
},
],
};
// Lighthouse CI configuration
// .lighthouserc.js
module.exports = {
ci: {
collect: {
numberOfRuns: 3,
url: ['http://localhost:3000'],
},
assert: {
assertions: {
'categories:performance': ['error', { minScore: 0.9 }],
'categories:accessibility': ['error', { minScore: 0.9 }],
'first-contentful-paint': ['error', { maxNumericValue: 2000 }],
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
},
},
},
};
// Performance monitoring setup
// utils/performance.ts
export function trackPerformance() {
// Core Web Vitals
import('web-vitals').then(({ onCLS, onFID, onLCP }) => {
onCLS(console.log);
onFID(console.log);
onLCP(console.log);
});
// Custom metrics
performance.mark('app-start');
window.addEventListener('load', () => {
performance.mark('app-loaded');
performance.measure('app-load-time', 'app-start', 'app-loaded');
const measure = performance.getEntriesByName('app-load-time')[0];
// Send to analytics
analytics.track('performance', {
loadTime: measure.duration,
});
});
}Establish clear performance budgets and metrics. Use Lighthouse CI to enforce budgets in CI/CD. Track Core Web Vitals and custom metrics from real users.
Lesson 2: Profiling with React DevTools and Chrome DevTools
Profiling identifies bottlenecks. Learn to use professional profiling tools effectively. React DevTools Profiler: • Record component renders • Identify expensive renders • Find unnecessary re-renders • Measure render times Chrome DevTools Performance: • Record JavaScript execution • Analyze flame charts • Identify long tasks • Find memory leaks Chrome DevTools Memory: • Heap snapshots • Allocation timeline • Memory leaks detection
// Using React DevTools Profiler
// 1. Install React DevTools browser extension
// 2. Open DevTools → Profiler tab
// 3. Click record
// 4. Interact with your app
// 5. Stop recording
// 6. Analyze results
// What to look for:
// - Components rendering unnecessarily
// - Long render times
// - Components causing cascading re-renders
// - Expensive calculations during render
// Programmatic profiling
// utils/profiler.ts
import { Profiler } from 'react';
interface ProfilerResult {
id: string;
phase: 'mount' | 'update';
actualDuration: number;
baseDuration: number;
startTime: number;
commitTime: number;
}
function onRenderCallback(
id: string,
phase: 'mount' | 'update',
actualDuration: number,
baseDuration: number,
startTime: number,
commitTime: number
) {
// Log slow renders
if (actualDuration > 16) { // More than one frame
console.warn(`Slow render: ${id} took ${actualDuration}ms`);
// Send to monitoring
analytics.track('slow-render', {
component: id,
duration: actualDuration,
phase,
});
}
}
// Wrap expensive components
<Profiler id="Dashboard" onRender={onRenderCallback}>
<Dashboard />
</Profiler>
// Chrome DevTools Performance Recording
// 1. Open DevTools → Performance tab
// 2. Click record
// 3. Interact with app
// 4. Stop recording
// 5. Analyze flame chart
// What to look for:
// - Long tasks (blocking main thread > 50ms)
// - Layout thrashing (repeated layout recalculations)
// - Paint storms (many repaints)
// - JavaScript execution time
// Performance.mark API for custom timing
performance.mark('component-render-start');
renderComponent();
performance.mark('component-render-end');
performance.measure(
'component-render',
'component-render-start',
'component-render-end'
);
// Get all measures
const measures = performance.getEntriesByType('measure');
measures.forEach(measure => {
console.log(`${measure.name}: ${measure.duration}ms`);
});
// Memory profiling
// Take heap snapshot before/after actions
const snapshot1 = performance.memory;
// ... do action
const snapshot2 = performance.memory;
const memoryDiff = snapshot2.usedJSHeapSize - snapshot1.usedJSHeapSize;
console.log(`Memory increased by: ${memoryDiff / 1024 / 1024}MB`);Profiling is the foundation of optimization. Use React DevTools for component-level profiling and Chrome DevTools for JavaScript/memory profiling. Measure before optimizing.
Lesson 3: Bundle Size Optimization Strategies
Bundle size directly impacts load time. At enterprise scale, every KB matters. Optimization Strategies: • Code splitting (route-based, component-based) • Tree-shaking (remove unused code) • Minification and compression • Dependency analysis • Bundle analysis • Dynamic imports Bundle Analysis Tools: • webpack-bundle-analyzer • source-map-explorer • Bundlephobia (for npm packages) • Lighthouse Common Issues: • Large dependencies • Duplicate dependencies • Unused code • Large images in bundles
// Bundle analysis setup
// Install webpack-bundle-analyzer
// npm install -D webpack-bundle-analyzer
// next.config.js or webpack.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({
// Your config
});
// Run: ANALYZE=true npm run build
// Analyze bundle composition
// utils/bundle-analysis.js
const fs = require('fs');
const { execSync } = require('child_process');
// Find large dependencies
execSync('npx source-map-explorer .next/static/chunks/*.js', {
stdio: 'inherit',
});
// Code splitting strategies
// 1. Route-based splitting (automatic in Next.js)
// Already handled by framework
// 2. Component-based splitting
import { lazy, Suspense } from 'react';
const HeavyChart = lazy(() => import('./HeavyChart'));
const CodeEditor = lazy(() => import('./CodeEditor'));
function Dashboard() {
return (
<Suspense fallback={<ChartSkeleton />}>
<HeavyChart />
</Suspense>
);
}
// 3. Library splitting
// Split large libraries into chunks
const MonacoEditor = lazy(() =>
import('monaco-editor').then(module => ({
default: module.editor,
}))
);
// 4. Dynamic imports with conditions
const Chart = lazy(() => {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
return import('./SimpleChart');
}
return import('./AdvancedChart');
});
// Tree-shaking optimization
// ✅ GOOD: Named imports (tree-shakeable)
import { debounce } from 'lodash-es';
// or
import debounce from 'lodash-es/debounce';
// ❌ BAD: Import entire library
import _ from 'lodash';
// Configure tree-shaking in package.json
{
"sideEffects": false, // Enable tree-shaking
// or
"sideEffects": [
"*.css",
"./src/polyfills.js"
]
}
// Bundle size budgets in CI
// package.json scripts
{
"scripts": {
"build": "next build",
"analyze": "ANALYZE=true next build",
"check-size": "bundlesize"
}
}
// bundlesize.config.js
module.exports = [
{
path: '.next/static/chunks/pages/_app-*.js',
maxSize: '200 KB',
},
{
path: '.next/static/chunks/pages/index-*.js',
maxSize: '100 KB',
},
];
// Dependency optimization
// Analyze dependency sizes
// Use Bundlephobia or size-limit
// Replace heavy dependencies
// ❌ moment.js (70KB) → ✅ date-fns (2KB)
// ❌ lodash (70KB) → ✅ lodash-es (tree-shakeable) or individual functions
// ❌ axios (13KB) → ✅ native fetch or ky (4KB)
// Remove unused dependencies
// Use depcheck to find unused deps
// npm install -D depcheck
// npx depcheck
// Optimize images
// Use next/image for automatic optimization
// Or use WebP format
// Compress images before bundlingBundle size optimization requires systematic analysis. Use bundle analyzers to identify large dependencies, implement code splitting, enable tree-shaking, and set size budgets. Replace heavy dependencies with lighter alternatives.
Lesson 4: Runtime Performance Optimization
Optimize how your application runs, not just how it loads. Key Optimization Areas: • Reduce re-renders • Memoization strategies • Virtual scrolling for large lists • Debouncing/throttling • Web Workers for heavy computations • Efficient state updates • Event handler optimization React Performance Patterns: • React.memo for components • useMemo for expensive calculations • useCallback for stable function references • Code splitting at component level • Lazy loading non-critical components
// Runtime performance optimization patterns
// 1. Reduce unnecessary re-renders
// ❌ BAD: Component re-renders on every parent render
function ExpensiveComponent({ data }) {
const result = expensiveCalculation(data); // Runs every render
return <div>{result}</div>;
}
// ✅ GOOD: Memoize expensive calculations
function ExpensiveComponent({ data }) {
const result = useMemo(
() => expensiveCalculation(data),
[data] // Only recalculate when data changes
);
return <div>{result}</div>;
}
// 2. Memoize components
// ❌ BAD: Re-renders even with same props
function ListItem({ item, onClick }) {
return (
<div onClick={onClick}>
{item.name}
</div>
);
}
// ✅ GOOD: Memo prevents re-render with same props
const ListItem = React.memo(function ListItem({ item, onClick }) {
return (
<div onClick={onClick}>
{item.name}
</div>
);
}, (prevProps, nextProps) => {
// Custom comparison
return prevProps.item.id === nextProps.item.id &&
prevProps.item.name === nextProps.item.name;
});
// 3. Stable function references
// ❌ BAD: New function created every render
function Parent({ items }) {
const handleClick = (id) => {
// handle click
};
return items.map(item => (
<ListItem key={item.id} item={item} onClick={handleClick} />
));
}
// ✅ GOOD: Stable reference with useCallback
function Parent({ items }) {
const handleClick = useCallback((id) => {
// handle click
}, []); // Stable reference
return items.map(item => (
<ListItem key={item.id} item={item} onClick={handleClick} />
));
}
// 4. Virtual scrolling for large lists
// Use react-window or react-virtualized
import { FixedSizeList } from 'react-window';
function VirtualizedList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
{items[index].name}
</div>
);
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</FixedSizeList>
);
}
// 5. Debounce/throttle expensive operations
import { useMemo } from 'react';
import { debounce } from 'lodash-es';
function SearchInput() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
// Debounce search API calls
const debouncedSearch = useMemo(
() => debounce(async (searchQuery) => {
const results = await searchAPI(searchQuery);
setResults(results);
}, 300),
[]
);
useEffect(() => {
if (query) {
debouncedSearch(query);
}
return () => {
debouncedSearch.cancel();
};
}, [query, debouncedSearch]);
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
);
}
// 6. Web Workers for heavy computations
// worker.js
self.onmessage = function(e) {
const { data } = e.data;
const result = heavyComputation(data);
self.postMessage({ result });
};
// Component
function Component() {
const [result, setResult] = useState(null);
const workerRef = useRef<Worker>();
useEffect(() => {
workerRef.current = new Worker('/worker.js');
workerRef.current.onmessage = (e) => {
setResult(e.data.result);
};
return () => {
workerRef.current?.terminate();
};
}, []);
const handleCompute = () => {
workerRef.current?.postMessage({ data: largeDataSet });
};
}
// 7. Efficient state updates
// ❌ BAD: Multiple state updates
function Component() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
setCount(count + 1); // Doesn't work as expected
setCount(count + 1);
};
}
// ✅ GOOD: Functional updates
function Component() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1); // Works correctly
};
}
// 8. Batch state updates (React 18+ automatic)
// React 18 automatically batches all updates
function Component() {
const [a, setA] = useState(0);
const [b, setB] = useState(0);
const handleClick = () => {
setA(1);
setB(2);
// React 18: Single re-render
// React 17: Two re-renders
};
}Runtime performance requires careful optimization of renders, calculations, and updates. Use memoization strategically, implement virtual scrolling for large lists, debounce/throttle expensive operations, and use Web Workers for heavy computations.
Lesson 5: Network and Loading Optimization
Optimize how your application loads resources from the network. Optimization Strategies: • Resource hints (preload, prefetch, preconnect) • HTTP/2 and HTTP/3 • CDN usage • Image optimization • Font optimization • Critical CSS • Service Workers for caching • Compression (gzip, brotli) Loading Strategies: • Preload critical resources • Prefetch likely-needed resources • Lazy load below-the-fold content • Progressive loading
// Network optimization techniques
// 1. Resource hints in Next.js
// app/layout.tsx or pages/_document.tsx
<Head>
{/* Preconnect to external domains */}
<link rel="preconnect" href="https://api.example.com" />
<link rel="dns-prefetch" href="https://api.example.com" />
{/* Preload critical resources */}
<link
rel="preload"
href="/fonts/main-font.woff2"
as="font"
type="font/woff2"
crossOrigin="anonymous"
/>
{/* Prefetch likely-needed resources */}
<link rel="prefetch" href="/dashboard" />
</Head>
// 2. Image optimization
// Use Next.js Image component
import Image from 'next/image';
<Image
src="/hero.jpg"
width={800}
height={600}
priority // Preload above-the-fold images
placeholder="blur" // Show blur while loading
alt="Hero image"
/>
// 3. Font optimization
// fonts.css
@font-face {
font-family: 'CustomFont';
src: url('/fonts/font.woff2') format('woff2');
font-display: swap; // Show fallback immediately
font-weight: 400;
}
// 4. Critical CSS
// Extract above-the-fold CSS
// Use tools like critical or purgecss
const critical = require('critical');
critical.generate({
src: 'index.html',
target: 'styles/critical.css',
width: 1300,
height: 900,
});
// 5. Service Worker for caching
// public/sw.js
const CACHE_NAME = 'my-app-v1';
const urlsToCache = [
'/',
'/styles/main.css',
'/scripts/main.js',
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
});
// 6. HTTP compression
// next.config.js
module.exports = {
compress: true, // Enable gzip compression
// Also enable brotli at server level
};
// 7. CDN configuration
// next.config.js
module.exports = {
images: {
domains: ['cdn.example.com'],
loader: 'custom',
loaderFile: './image-loader.js',
},
};
// 8. Streaming SSR
// Next.js 13+ supports streaming by default
// app/layout.tsx
export default async function Layout({ children }) {
return (
<html>
<body>
<Suspense fallback={<Loading />}>
{children}
</Suspense>
</body>
</html>
);
}
// 9. Progressive enhancement
// Load critical content first, enhance later
function App() {
const [enhanced, setEnhanced] = useState(false);
useEffect(() => {
// Load enhancements after initial render
import('./enhancements').then(() => {
setEnhanced(true);
});
}, []);
return enhanced ? <EnhancedApp /> : <BasicApp />;
}
// 10. Request deduplication
// Prevent duplicate requests
const requestCache = new Map();
async function fetchData(url: string) {
if (requestCache.has(url)) {
return requestCache.get(url);
}
const promise = fetch(url).then(res => res.json());
requestCache.set(url, promise);
return promise;
}Network optimization reduces load times through resource hints, image optimization, font optimization, caching strategies, and compression. Use CDNs and Service Workers for efficient resource delivery.
Lesson 6: Monitoring and Alerting
Performance monitoring ensures you catch regressions before users do. Monitoring Solutions: • Real User Monitoring (RUM) • Synthetic monitoring • Performance budgets • Error tracking • Custom metrics Tools: • Google Analytics (Core Web Vitals) • New Relic • Datadog • Sentry (errors + performance) • Custom dashboards Alerting: • Performance budget violations • Error rate spikes • Slow API responses • Bundle size increases
// Performance monitoring setup
// 1. Core Web Vitals tracking
// utils/analytics.ts
import { onCLS, onFID, onLCP, onTTFB, onINP } from 'web-vitals';
function sendToAnalytics(metric: any) {
// Send to your analytics service
analytics.track('web-vital', {
name: metric.name,
value: metric.value,
rating: metric.rating, // 'good', 'needs-improvement', 'poor'
id: metric.id,
delta: metric.delta,
});
}
onCLS(sendToAnalytics);
onFID(sendToAnalytics);
onLCP(sendToAnalytics);
onTTFB(sendToAnalytics);
onINP(sendToAnalytics);
// 2. Custom performance metrics
// utils/performance-monitor.ts
export class PerformanceMonitor {
private metrics: Map<string, number[]> = new Map();
mark(name: string) {
performance.mark(name);
}
measure(name: string, startMark: string, endMark: string) {
performance.measure(name, startMark, endMark);
const measure = performance.getEntriesByName(name)[0];
if (!this.metrics.has(name)) {
this.metrics.set(name, []);
}
this.metrics.get(name)?.push(measure.duration);
// Send to monitoring service
this.sendMetric(name, measure.duration);
}
private sendMetric(name: string, value: number) {
// Send to monitoring service (DataDog, New Relic, etc.)
if (window.monitoring) {
window.monitoring.track(name, value);
}
}
getMetrics() {
const result: Record<string, any> = {};
this.metrics.forEach((values, name) => {
result[name] = {
avg: values.reduce((a, b) => a + b, 0) / values.length,
p50: this.percentile(values, 50),
p95: this.percentile(values, 95),
p99: this.percentile(values, 99),
};
});
return result;
}
private percentile(arr: number[], p: number) {
const sorted = arr.sort((a, b) => a - b);
const index = Math.ceil((sorted.length * p) / 100) - 1;
return sorted[index];
}
}
// 3. Performance budget monitoring
// utils/budget-monitor.ts
export function checkPerformanceBudget() {
// Check bundle sizes
const scripts = Array.from(document.querySelectorAll('script'));
const totalSize = scripts.reduce((total, script) => {
return total + (script.src ? getScriptSize(script.src) : 0);
}, 0);
const budget = 500 * 1024; // 500KB
if (totalSize > budget) {
alertMonitoring({
type: 'budget-exceeded',
metric: 'bundle-size',
value: totalSize,
budget,
});
}
// Check load time
window.addEventListener('load', () => {
const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart;
if (loadTime > 3000) { // 3 seconds
alertMonitoring({
type: 'budget-exceeded',
metric: 'load-time',
value: loadTime,
budget: 3000,
});
}
});
}
// 4. Error tracking with performance context
// utils/error-monitor.ts
window.addEventListener('error', (event) => {
const performanceData = {
loadTime: performance.timing.loadEventEnd - performance.timing.navigationStart,
domContentLoaded: performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart,
firstPaint: performance.getEntriesByType('paint')[0]?.startTime,
};
errorTracking.captureException(event.error, {
contexts: {
performance: performanceData,
},
});
});
// 5. API performance monitoring
// utils/api-monitor.ts
const originalFetch = window.fetch;
window.fetch = async function(...args) {
const startTime = performance.now();
const url = args[0];
try {
const response = await originalFetch(...args);
const duration = performance.now() - startTime;
// Track slow API calls
if (duration > 1000) {
monitoring.track('slow-api', {
url,
duration,
status: response.status,
});
}
return response;
} catch (error) {
const duration = performance.now() - startTime;
monitoring.track('api-error', {
url,
duration,
error: error.message,
});
throw error;
}
};
// 6. React component render monitoring
// HOC for automatic monitoring
function withPerformanceMonitoring<P extends object>(
Component: React.ComponentType<P>,
componentName: string
) {
return function MonitoredComponent(props: P) {
return (
<Profiler
id={componentName}
onRender={(id, phase, actualDuration) => {
if (actualDuration > 16) { // More than one frame
monitoring.track('slow-render', {
component: id,
phase,
duration: actualDuration,
});
}
}}
>
<Component {...props} />
</Profiler>
);
};
}
// Usage
export default withPerformanceMonitoring(ExpensiveComponent, 'ExpensiveComponent');Continuous monitoring catches performance regressions early. Track Core Web Vitals, custom metrics, performance budgets, and errors. Set up alerting for violations to maintain performance standards.
Lesson 7: Performance Testing in CI/CD
Automate performance testing to prevent regressions. CI/CD Performance Testing: • Lighthouse CI integration • Bundle size checks • Performance budget enforcement • Visual regression testing • Load testing Tools: • Lighthouse CI • bundlesize • Performance budgets • WebPageTest API
// CI/CD performance testing setup
// .github/workflows/performance.yml
name: Performance Tests
on: [pull_request]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- run: npm install
- run: npm run build
- run: npm run start &
- name: Run Lighthouse CI
uses: treosh/lighthouse-ci-action@v7
with:
urls: |
http://localhost:3000
http://localhost:3000/dashboard
uploadArtifacts: true
temporaryPublicStorage: true
// bundlesize check
// package.json
{
"scripts": {
"test:size": "bundlesize"
}
}
// bundlesize.config.js
module.exports = [
{
path: '.next/static/chunks/main-*.js',
maxSize: '200 KB',
},
{
path: '.next/static/chunks/pages/_app-*.js',
maxSize: '100 KB',
},
];
// Lighthouse CI config
// .lighthouserc.js
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000'],
numberOfRuns: 3,
},
assert: {
assertions: {
'categories:performance': ['error', { minScore: 0.9 }],
'first-contentful-paint': ['error', { maxNumericValue: 2000 }],
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
},
},
},
};
// Performance regression testing
// scripts/performance-test.js
const lighthouse = require('lighthouse');
const chromeLauncher = require('chrome-launcher');
async function runLighthouse(url) {
const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] });
const options = { logLevel: 'info', output: 'json', port: chrome.port };
const runnerResult = await lighthouse(url, options);
await chrome.kill();
const { lhr } = runnerResult;
const score = lhr.categories.performance.score * 100;
if (score < 90) {
console.error(`Performance score ${score} below threshold of 90`);
process.exit(1);
}
return score;
}
// Run in CI
runLighthouse('http://localhost:3000');Automate performance testing in CI/CD to catch regressions before they reach production. Use Lighthouse CI for performance scores and bundlesize for bundle size checks.
Lesson 8: Performance Optimization Checklist
Use this comprehensive checklist for performance optimization. Initial Load: • [ ] Code splitting implemented • [ ] Images optimized and lazy loaded • [ ] Fonts optimized with font-display • [ ] Critical CSS extracted • [ ] Bundle size under budget • [ ] Gzip/Brotli compression enabled • [ ] CDN configured Runtime: • [ ] Unnecessary re-renders eliminated • [ ] Expensive calculations memoized • [ ] Virtual scrolling for long lists • [ ] Debounce/throttle expensive operations • [ ] Web Workers for heavy computations Monitoring: • [ ] Core Web Vitals tracked • [ ] Performance budgets configured • [ ] Error tracking with performance context • [ ] Alerting set up • [ ] Dashboard for metrics Testing: • [ ] Lighthouse CI integrated • [ ] Bundle size checks in CI • [ ] Performance regression tests
// Performance optimization checklist implementation
// scripts/performance-check.js
const fs = require('fs');
const { execSync } = require('child_process');
class PerformanceChecklist {
async runChecks() {
const checks = {
bundleSize: await this.checkBundleSize(),
lighthouse: await this.checkLighthouse(),
dependencies: await this.checkDependencies(),
images: await this.checkImages(),
};
const allPassed = Object.values(checks).every(check => check.passed);
if (!allPassed) {
console.error('Performance checks failed');
console.log(checks);
process.exit(1);
}
console.log('All performance checks passed!');
}
async checkBundleSize() {
const bundlePath = '.next/static/chunks/main-*.js';
const maxSize = 200 * 1024; // 200KB
// Check bundle size
// Implementation here
return { passed: true, message: 'Bundle size within budget' };
}
async checkLighthouse() {
// Run Lighthouse and check scores
return { passed: true, message: 'Lighthouse scores passed' };
}
async checkDependencies() {
// Check for large dependencies
return { passed: true, message: 'No oversized dependencies' };
}
async checkImages() {
// Check image optimization
return { passed: true, message: 'Images optimized' };
}
}
// Run checklist
new PerformanceChecklist().runChecks();Use a comprehensive checklist to ensure all performance optimizations are implemented. Automate checks where possible.
Conclusion
Performance is a feature, not an afterthought. At enterprise scale, systematic measurement, optimization, and monitoring are essential. Establish performance budgets, profile regularly, optimize continuously, and monitor in production. Remember: optimize based on real user data, not synthetic benchmarks. Performance optimization is an ongoing process, not a one-time task.