Modern Development Best Practices

1. Clean Code Principles SOLID

Apply SOLID principles and clean code practices to write maintainable, testable, and scalable frontend applications.

Principle Definition Frontend Application Violation Example
Single Responsibility One reason to change Component does one thing well Component handles UI + API + state
Open/Closed Open for extension, closed for modification Use composition, HOCs, hooks Modify existing code for new features
Liskov Substitution Subtypes must be substitutable Consistent component interfaces Child breaks parent's contract
Interface Segregation Small, specific interfaces Focused props interfaces Large props with many optionals
Dependency Inversion Depend on abstractions Inject dependencies via props/context Hard-coded dependencies

Example: Single Responsibility Principle

// ❌ Bad: Component doing too much
function UserDashboard() {
  const [user, setUser] = useState(null);
  const [posts, setPosts] = useState([]);
  const [comments, setComments] = useState([]);
  
  useEffect(() => {
    // Fetching data
    fetch('/api/user').then(r => r.json()).then(setUser);
    fetch('/api/posts').then(r => r.json()).then(setPosts);
    fetch('/api/comments').then(r => r.json()).then(setComments);
  }, []);
  
  // Data transformation logic
  const processedPosts = posts.map(post => ({
    ...post,
    commentCount: comments.filter(c => c.postId === post.id).length
  }));
  
  // Rendering complex UI
  return (
    <div>
      <header>{user?.name}</header>
      <div>{/* Complex post list */}</div>
      <div>{/* Complex comment section */}</div>
    </div>
  );
}

// ✅ Good: Separated responsibilities
// Custom hook for data fetching
function useUserData() {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    fetch('/api/user').then(r => r.json()).then(setUser);
  }, []);
  
  return user;
}

function usePosts() {
  const [posts, setPosts] = useState([]);
  
  useEffect(() => {
    fetch('/api/posts').then(r => r.json()).then(setPosts);
  }, []);
  
  return posts;
}

// Separate component for user header
function UserHeader({ user }) {
  return <header>{user?.name}</header>;
}

// Separate component for posts
function PostList({ posts }) {
  return <div>{posts.map(post => <PostCard key={post.id} post={post} />)}</div>;
}

// Main component just orchestrates
function UserDashboard() {
  const user = useUserData();
  const posts = usePosts();
  
  return (
    <div>
      <UserHeader user={user} />
      <PostList posts={posts} />
    </div>
  );
}

Example: Open/Closed Principle with composition

// ❌ Bad: Modifying component for each variation
function Button({ type, icon, loading, disabled, onClick }) {
  if (loading) {
    return <button disabled><Spinner /> Loading...</button>;
  }
  
  if (type === 'primary') {
    return <button className="btn-primary" onClick={onClick}>{icon} Click</button>;
  }
  
  if (type === 'secondary') {
    return <button className="btn-secondary" onClick={onClick}>Click</button>;
  }
  
  // Adding new types requires modifying this component
}

// ✅ Good: Open for extension via composition
function Button({ children, disabled, onClick, className }) {
  return (
    <button 
      className={`btn ${className}`}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
}

// Extend via composition without modifying Button
function PrimaryButton({ children, ...props }) {
  return <Button className="btn-primary" {...props}>{children}</Button>;
}

function SecondaryButton({ children, ...props }) {
  return <Button className="btn-secondary" {...props}>{children}</Button>;
}

function LoadingButton({ loading, children, ...props }) {
  return (
    <Button disabled={loading} {...props}>
      {loading ? <><Spinner /> Loading...</> : children}
    </Button>
  );
}

function IconButton({ icon, children, ...props }) {
  return (
    <Button {...props}>
      {icon} {children}
    </Button>
  );
}

// Easy to add new variants without touching base Button
function DangerButton({ children, ...props }) {
  return <Button className="btn-danger" {...props}>{children}</Button>;
}

Example: Dependency Inversion with dependency injection

// ❌ Bad: Hard-coded dependencies
function UserList() {
  const [users, setUsers] = useState([]);
  
  useEffect(() => {
    // Directly coupled to specific API implementation
    axios.get('https://api.example.com/users')
      .then(response => setUsers(response.data));
  }, []);
  
  return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}

// ✅ Good: Depend on abstraction (interface/contract)
// Abstract API service
interface UserService {
  getUsers(): Promise<User[]>;
}

// Concrete implementation
class ApiUserService implements UserService {
  async getUsers() {
    const response = await axios.get('https://api.example.com/users');
    return response.data;
  }
}

// Mock implementation for testing
class MockUserService implements UserService {
  async getUsers() {
    return [
      { id: 1, name: 'John' },
      { id: 2, name: 'Jane' }
    ];
  }
}

// Component depends on abstraction, not concrete implementation
function UserList({ userService }: { userService: UserService }) {
  const [users, setUsers] = useState<User[]>([]);
  
  useEffect(() => {
    userService.getUsers().then(setUsers);
  }, [userService]);
  
  return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}

// Usage with dependency injection
function App() {
  const userService = new ApiUserService(); // or MockUserService for testing
  return <UserList userService={userService} />;
}

// Even better: Use context for DI
const ServiceContext = createContext<UserService>(new ApiUserService());

function UserList() {
  const userService = useContext(ServiceContext);
  const [users, setUsers] = useState<User[]>([]);
  
  useEffect(() => {
    userService.getUsers().then(setUsers);
  }, [userService]);
  
  return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
Clean Code Guidelines: Functions should be small (5-20 lines), do one thing. Names should be descriptive. Avoid magic numbers. Comments explain why, not what. DRY (Don't Repeat Yourself). KISS (Keep It Simple, Stupid).

2. Design Patterns Factory Observer

Apply proven design patterns to solve common frontend architecture challenges with reusable, maintainable solutions.

Pattern Purpose Frontend Use Case Implementation
Factory Pattern Object creation abstraction Component factories, form builders Function returns components
Observer Pattern Event-driven updates State management, pub/sub Subscribe/notify pattern
Singleton Pattern Single instance API client, config, store Module exports instance
Strategy Pattern Interchangeable algorithms Payment methods, validators Interface + implementations
Decorator Pattern Add behavior dynamically HOCs, wrapper components Enhance component/function
Facade Pattern Simplified interface API wrappers, complex libs Wrapper with simple API

Example: Factory Pattern for dynamic component creation

// Factory function to create form fields based on config
type FieldType = 'text' | 'email' | 'number' | 'select' | 'checkbox';

interface FieldConfig {
  type: FieldType;
  name: string;
  label: string;
  options?: { value: string; label: string }[];
  validation?: any;
}

function createFormField(config: FieldConfig) {
  switch (config.type) {
    case 'text':
    case 'email':
    case 'number':
      return <TextInput key={config.name} {...config} />;
    
    case 'select':
      return <SelectInput key={config.name} {...config} />;
    
    case 'checkbox':
      return <CheckboxInput key={config.name} {...config} />;
    
    default:
      throw new Error(`Unknown field type: ${config.type}`);
  }
}

// Form builder using factory
function DynamicForm({ fields }: { fields: FieldConfig[] }) {
  return (
    <form>
      {fields.map(field => createFormField(field))}
      <button type="submit">Submit</button>
    </form>
  );
}

// Usage
const formConfig: FieldConfig[] = [
  { type: 'text', name: 'name', label: 'Name' },
  { type: 'email', name: 'email', label: 'Email' },
  { type: 'select', name: 'country', label: 'Country', options: [
    { value: 'us', label: 'United States' },
    { value: 'uk', label: 'United Kingdom' }
  ]}
];

<DynamicForm fields={formConfig} />

// Chart factory example
function createChart(type: 'line' | 'bar' | 'pie', data: any) {
  const chartComponents = {
    line: LineChart,
    bar: BarChart,
    pie: PieChart
  };
  
  const ChartComponent = chartComponents[type];
  return <ChartComponent data={data} />;
}

Example: Observer Pattern for event system

// Event bus implementation (Observer pattern)
class EventBus {
  private events: Map<string, Set<Function>> = new Map();
  
  subscribe(event: string, callback: Function): () => void {
    if (!this.events.has(event)) {
      this.events.set(event, new Set());
    }
    
    this.events.get(event)!.add(callback);
    
    // Return unsubscribe function
    return () => {
      this.events.get(event)?.delete(callback);
    };
  }
  
  publish(event: string, data?: any): void {
    const callbacks = this.events.get(event);
    if (callbacks) {
      callbacks.forEach(callback => callback(data));
    }
  }
  
  clear(event?: string): void {
    if (event) {
      this.events.delete(event);
    } else {
      this.events.clear();
    }
  }
}

export const eventBus = new EventBus();

// Usage: Shopping cart example
// CartButton.tsx
function CartButton() {
  const addToCart = (product: Product) => {
    eventBus.publish('cart:add', product);
  };
  
  return <button onClick={() => addToCart(product)}>Add to Cart</button>;
}

// CartBadge.tsx - Observes cart events
function CartBadge() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    const unsubscribe = eventBus.subscribe('cart:add', () => {
      setCount(prev => prev + 1);
    });
    
    return unsubscribe; // Cleanup on unmount
  }, []);
  
  return <div className="badge">{count}</div>;
}

// State management with observer pattern
class Store {
  private state: any;
  private listeners = new Set<Function>();
  
  getState() {
    return this.state;
  }
  
  setState(newState: any) {
    this.state = { ...this.state, ...newState };
    this.notify();
  }
  
  subscribe(listener: Function) {
    this.listeners.add(listener);
    return () => this.listeners.delete(listener);
  }
  
  private notify() {
    this.listeners.forEach(listener => listener(this.state));
  }
}

const store = new Store();

// React hook for store
function useStore() {
  const [state, setState] = useState(store.getState());
  
  useEffect(() => {
    return store.subscribe((newState: any) => {
      setState(newState);
    });
  }, []);
  
  return [state, (newState: any) => store.setState(newState)];
}

Example: Strategy Pattern for payment processing

// Strategy interface
interface PaymentStrategy {
  processPayment(amount: number): Promise<PaymentResult>;
  validate(): boolean;
}

// Concrete strategies
class CreditCardPayment implements PaymentStrategy {
  constructor(private cardNumber: string, private cvv: string) {}
  
  validate(): boolean {
    return this.cardNumber.length === 16 && this.cvv.length === 3;
  }
  
  async processPayment(amount: number): Promise<PaymentResult> {
    // Credit card processing logic
    const response = await fetch('/api/payment/credit-card', {
      method: 'POST',
      body: JSON.stringify({ cardNumber: this.cardNumber, amount })
    });
    return response.json();
  }
}

class PayPalPayment implements PaymentStrategy {
  constructor(private email: string) {}
  
  validate(): boolean {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email);
  }
  
  async processPayment(amount: number): Promise<PaymentResult> {
    // PayPal processing logic
    const response = await fetch('/api/payment/paypal', {
      method: 'POST',
      body: JSON.stringify({ email: this.email, amount })
    });
    return response.json();
  }
}

class CryptoPayment implements PaymentStrategy {
  constructor(private walletAddress: string) {}
  
  validate(): boolean {
    return this.walletAddress.length === 42;
  }
  
  async processPayment(amount: number): Promise<PaymentResult> {
    // Cryptocurrency processing logic
    const response = await fetch('/api/payment/crypto', {
      method: 'POST',
      body: JSON.stringify({ wallet: this.walletAddress, amount })
    });
    return response.json();
  }
}

// Context that uses strategy
class PaymentProcessor {
  constructor(private strategy: PaymentStrategy) {}
  
  setStrategy(strategy: PaymentStrategy) {
    this.strategy = strategy;
  }
  
  async execute(amount: number): Promise<PaymentResult> {
    if (!this.strategy.validate()) {
      throw new Error('Invalid payment details');
    }
    
    return this.strategy.processPayment(amount);
  }
}

// React component using strategies
function CheckoutForm() {
  const [paymentMethod, setPaymentMethod] = useState<'card' | 'paypal' | 'crypto'>('card');
  const [processor] = useState(new PaymentProcessor(new CreditCardPayment('', '')));
  
  const handlePayment = async (amount: number) => {
    // Select strategy based on user choice
    let strategy: PaymentStrategy;
    
    switch (paymentMethod) {
      case 'card':
        strategy = new CreditCardPayment(cardNumber, cvv);
        break;
      case 'paypal':
        strategy = new PayPalPayment(email);
        break;
      case 'crypto':
        strategy = new CryptoPayment(walletAddress);
        break;
    }
    
    processor.setStrategy(strategy);
    const result = await processor.execute(amount);
    
    if (result.success) {
      alert('Payment successful!');
    }
  };
  
  return (
    <form>
      <select onChange={(e) => setPaymentMethod(e.target.value as any)}>
        <option value="card">Credit Card</option>
        <option value="paypal">PayPal</option>
        <option value="crypto">Cryptocurrency</option>
      </select>
      
      {/* Render appropriate form fields based on strategy */}
      
      <button onClick={() => handlePayment(99.99)}>Pay Now</button>
    </form>
  );
}
Pattern Overuse: Don't force patterns where they're not needed. Patterns add complexity and abstraction. Use them to solve specific problems, not to demonstrate knowledge. Start simple, refactor to patterns when needed.

3. Performance Budgets Lighthouse CI

Define and enforce performance budgets with automated testing to prevent performance regressions in production.

Metric Good Target Tool Impact
First Contentful Paint (FCP) <1.8s Lighthouse, WebPageTest Perceived speed
Largest Contentful Paint (LCP) <2.5s Core Web Vitals Loading performance
Time to Interactive (TTI) <3.8s Lighthouse Interactivity
Total Blocking Time (TBT) <200ms Lighthouse Responsiveness
Cumulative Layout Shift (CLS) <0.1 Core Web Vitals Visual stability
JavaScript Bundle Size <200KB (gzipped) Webpack Bundle Analyzer Load time

Example: Lighthouse CI setup for automated performance testing

// Installation
npm install -D @lhci/cli

// lighthouserc.json
{
  "ci": {
    "collect": {
      "startServerCommand": "npm run build && npm run preview",
      "url": [
        "http://localhost:4173/",
        "http://localhost:4173/products",
        "http://localhost:4173/checkout"
      ],
      "numberOfRuns": 3
    },
    "assert": {
      "preset": "lighthouse:recommended",
      "assertions": {
        // Performance budgets
        "first-contentful-paint": ["error", { "maxNumericValue": 1800 }],
        "largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
        "interactive": ["error", { "maxNumericValue": 3800 }],
        "speed-index": ["error", { "maxNumericValue": 3400 }],
        "total-blocking-time": ["error", { "maxNumericValue": 200 }],
        "cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
        
        // Resource budgets
        "resource-summary:script:size": ["error", { "maxNumericValue": 204800 }], // 200KB
        "resource-summary:stylesheet:size": ["error", { "maxNumericValue": 51200 }], // 50KB
        "resource-summary:image:size": ["error", { "maxNumericValue": 512000 }], // 500KB
        "resource-summary:total:size": ["error", { "maxNumericValue": 1048576 }], // 1MB
        
        // Best practices
        "uses-responsive-images": "error",
        "offscreen-images": "error",
        "modern-image-formats": "warn",
        "uses-text-compression": "error",
        "unused-javascript": "warn",
        
        // Accessibility
        "color-contrast": "error",
        "heading-order": "error",
        "label": "error"
      }
    },
    "upload": {
      "target": "temporary-public-storage"
    }
  }
}

// package.json scripts
{
  "scripts": {
    "lhci:collect": "lhci collect",
    "lhci:assert": "lhci assert",
    "lhci:upload": "lhci upload",
    "lhci:full": "lhci collect && lhci assert && lhci upload"
  }
}

Example: GitHub Actions CI workflow with Lighthouse

// .github/workflows/lighthouse-ci.yml
name: Lighthouse CI

on:
  pull_request:
    branches: [main]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build
        run: npm run build
      
      - name: Run Lighthouse CI
        run: |
          npm install -g @lhci/cli
          lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
      
      - name: Upload results
        uses: actions/upload-artifact@v3
        with:
          name: lighthouse-results
          path: .lighthouseci/
      
      - name: Comment PR
        uses: treosh/lighthouse-ci-action@v9
        with:
          urls: |
            http://localhost:4173
            http://localhost:4173/products
          uploadArtifacts: true
          temporaryPublicStorage: true

Example: Webpack performance budgets

// webpack.config.js
module.exports = {
  performance: {
    maxAssetSize: 244000, // 244KB
    maxEntrypointSize: 244000,
    hints: 'error', // or 'warning'
    assetFilter: function(assetFilename) {
      // Only check JS and CSS files
      return /\.(js|css)$/.test(assetFilename);
    }
  },
  
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10
        },
        common: {
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true
        }
      }
    }
  }
};

// Next.js config with budgets
// next.config.js
module.exports = {
  experimental: {
    performanceBudgets: [
      {
        path: '/',
        maxSize: {
          total: 200 * 1024, // 200KB
          javascript: 150 * 1024,
          css: 50 * 1024
        }
      },
      {
        path: '/products',
        maxSize: {
          total: 250 * 1024
        }
      }
    ]
  }
};

// bundlesize package
// package.json
{
  "bundlesize": [
    {
      "path": "./dist/main.*.js",
      "maxSize": "150 kB"
    },
    {
      "path": "./dist/vendor.*.js",
      "maxSize": "50 kB"
    },
    {
      "path": "./dist/styles.*.css",
      "maxSize": "30 kB"
    }
  ]
}

// CI script
npm install -g bundlesize
bundlesize
Monitoring Strategy: Set budgets 20% lower than current performance to encourage optimization. Run Lighthouse on every PR. Track Core Web Vitals in production with Real User Monitoring (RUM). Review budgets quarterly.

4. Code Splitting Route Based

Implement route-based and component-based code splitting to reduce initial bundle size and improve load performance.

Technique When to Use Benefits Trade-offs
Route-based Splitting Different pages/routes Smaller initial bundle, faster FCP Navigation delay
Component-based Splitting Large components (modals, charts) Load on demand Complexity
Vendor Splitting Third-party libraries Better caching More requests
Dynamic Import Conditional features Load only when needed Network waterfall

Example: React route-based code splitting

// App.tsx - Route-based lazy loading
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

// Eagerly loaded (always needed)
import { Header } from './components/Header';
import { Footer } from './components/Footer';

// Lazy loaded routes (loaded on demand)
const Home = lazy(() => import('./pages/Home'));
const Products = lazy(() => import('./pages/Products'));
const ProductDetail = lazy(() => import('./pages/ProductDetail'));
const Checkout = lazy(() => import('./pages/Checkout'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Admin = lazy(() => import('./pages/Admin'));

// Loading fallback
function PageLoader() {
  return (
    <div className="page-loader">
      <div className="spinner" />
      <p>Loading...</p>
    </div>
  );
}

export function App() {
  return (
    <BrowserRouter>
      <Header />
      
      <main>
        <Suspense fallback={<PageLoader />}>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/products" element={<Products />} />
            <Route path="/products/:id" element={<ProductDetail />} />
            <Route path="/checkout" element={<Checkout />} />
            <Route path="/dashboard" element={<Dashboard />} />
            <Route path="/admin/*" element={<Admin />} />
          </Routes>
        </Suspense>
      </main>
      
      <Footer />
    </BrowserRouter>
  );
}

// Result: Each route is a separate chunk
// home.chunk.js, products.chunk.js, checkout.chunk.js, etc.

Example: Component-based code splitting with prefetching

// Lazy load heavy components
import { lazy, Suspense } from 'react';

const HeavyChart = lazy(() => import('./components/HeavyChart'));
const VideoPlayer = lazy(() => import('./components/VideoPlayer'));
const RichTextEditor = lazy(() => import('./components/RichTextEditor'));

// Prefetch on hover for better UX
function ProductPage() {
  const [showChart, setShowChart] = useState(false);
  
  const handleMouseEnter = () => {
    // Prefetch chart component
    import('./components/HeavyChart');
  };
  
  return (
    <div>
      <h1>Product Analytics</h1>
      
      <button 
        onClick={() => setShowChart(true)}
        onMouseEnter={handleMouseEnter}
      >
        Show Chart
      </button>
      
      {showChart && (
        <Suspense fallback={<div>Loading chart...</div>}>
          <HeavyChart data={data} />
        </Suspense>
      )}
    </div>
  );
}

// Conditional feature loading
function AdminPanel() {
  const { user } = useAuth();
  const [AdminTools, setAdminTools] = useState(null);
  
  useEffect(() => {
    if (user?.role === 'admin') {
      // Only load admin tools for admin users
      import('./components/AdminTools').then(module => {
        setAdminTools(() => module.default);
      });
    }
  }, [user]);
  
  if (!user) return <Login />;
  if (user.role !== 'admin') return <Forbidden />;
  if (!AdminTools) return <Loading />;
  
  return <AdminTools />;
}

// Named exports lazy loading
const { AdvancedFilters, DataExport } = lazy(() => 
  import('./components/AdvancedFeatures').then(module => ({
    default: {
      AdvancedFilters: module.AdvancedFilters,
      DataExport: module.DataExport
    }
  }))
);

Example: Next.js dynamic imports with loading states

// Next.js dynamic import
import dynamic from 'next/dynamic';

// Basic dynamic import
const DynamicComponent = dynamic(() => import('./components/Heavy'), {
  loading: () => <p>Loading...</p>,
  ssr: false // Disable SSR for this component
});

// Import with named export
const DynamicChart = dynamic(
  () => import('./components/Charts').then(mod => mod.LineChart),
  { loading: () => <ChartSkeleton /> }
);

// Conditional dynamic import
function Dashboard() {
  const DynamicMap = dynamic(() => import('./components/Map'), {
    loading: () => <MapSkeleton />,
    ssr: false
  });
  
  return (
    <div>
      <h1>Dashboard</h1>
      <DynamicMap center={[51.505, -0.09]} zoom={13} />
    </div>
  );
}

// Multiple dynamic components
const [Modal, Tooltip, Drawer] = [
  dynamic(() => import('./components/Modal')),
  dynamic(() => import('./components/Tooltip')),
  dynamic(() => import('./components/Drawer'))
];

// Webpack magic comments for better chunk names
const ProductDetail = lazy(() => 
  import(
    /* webpackChunkName: "product-detail" */
    /* webpackPrefetch: true */
    './pages/ProductDetail'
  )
);
Best Practices: Split routes by default. Lazy load modals, charts, rich editors. Prefetch on hover/focus. Use loading skeletons. Keep critical path synchronous. Monitor chunk sizes with bundle analyzer.

5. Tree Shaking Dead Code Elimination

Eliminate unused code from bundles through proper module exports, sideEffects configuration, and build optimizations.

Technique Purpose Configuration Impact
ES6 Modules Enable static analysis Use import/export Required for tree shaking
sideEffects Flag Mark pure modules package.json Aggressive elimination
Named Exports Import only what's needed Avoid default exports Better tree shaking
Production Mode Enable optimizations NODE_ENV=production Removes dev code

Example: Proper module structure for tree shaking

// ❌ Bad: Default export with everything
// utils.js
export default {
  add: (a, b) => a + b,
  subtract: (a, b) => a - b,
  multiply: (a, b) => a * b,
  divide: (a, b) => a / b,
  // ... 50 more functions
};

// Usage imports everything (no tree shaking)
import utils from './utils';
utils.add(1, 2);

// ✅ Good: Named exports
// utils.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const multiply = (a, b) => a * b;
export const divide = (a, b) => a / b;

// Usage imports only what's needed (tree shaken)
import { add } from './utils';
add(1, 2);

// ❌ Bad: Barrel exports without re-exports
// index.js
export * from './utils';
export * from './validators';
export * from './formatters';

// ✅ Good: Direct imports or selective barrel exports
// Import directly from source
import { add } from './utils/math';
import { validateEmail } from './validators/email';

// Or use selective barrel exports
// index.js
export { add, subtract } from './utils/math';
export { validateEmail, validatePhone } from './validators';

// ❌ Bad: Side effects prevent tree shaking
// module.js
import './styles.css'; // Side effect
console.log('Module loaded'); // Side effect

export const doSomething = () => {};

// ✅ Good: Pure module
// module.js
export const doSomething = () => {};

// Import styles separately when needed
// App.tsx
import './styles.css';
import { doSomething } from './module';

Example: package.json sideEffects configuration

// package.json - Library configuration
{
  "name": "my-library",
  "version": "1.0.0",
  "main": "dist/index.js",
  "module": "dist/index.esm.js",
  "sideEffects": false, // No side effects, safe to tree shake
  "exports": {
    ".": {
      "import": "./dist/index.esm.js",
      "require": "./dist/index.js"
    },
    "./utils": {
      "import": "./dist/utils.esm.js",
      "require": "./dist/utils.js"
    }
  }
}

// Or specify files with side effects
{
  "sideEffects": [
    "*.css",
    "*.scss",
    "./src/polyfills.js"
  ]
}

// Webpack configuration
module.exports = {
  mode: 'production', // Enables tree shaking
  optimization: {
    usedExports: true, // Mark unused exports
    minimize: true, // Remove unused code
    sideEffects: true // Respect package.json sideEffects
  }
};

// Rollup configuration
export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm'
  },
  plugins: [
    resolve(),
    commonjs(),
    terser() // Minify and tree shake
  ],
  treeshake: {
    moduleSideEffects: false,
    propertyReadSideEffects: false
  }
};

Example: Optimizing library imports for tree shaking

// ❌ Bad: Import entire library
import _ from 'lodash'; // Imports all of lodash (~70KB)
import * as MUI from '@mui/material'; // Imports everything
import moment from 'moment'; // Imports full moment.js

_.debounce(fn, 300);
const button = <MUI.Button />;
moment().format();

// ✅ Good: Import specific functions/components
import debounce from 'lodash/debounce'; // Only debounce (~2KB)
import Button from '@mui/material/Button'; // Only Button component
import dayjs from 'dayjs'; // Smaller alternative to moment

debounce(fn, 300);
const button = <Button />;
dayjs().format();

// ✅ Better: Use libraries with good tree shaking
// lodash-es (ES modules version)
import { debounce, throttle } from 'lodash-es';

// date-fns (tree-shakeable by default)
import { format, addDays } from 'date-fns';

// ✅ Best: Babel plugin for automatic optimization
// .babelrc
{
  "plugins": [
    ["import", {
      "libraryName": "lodash",
      "libraryDirectory": "",
      "camel2DashComponentName": false
    }],
    ["import", {
      "libraryName": "@mui/material",
      "libraryDirectory": "",
      "camel2DashComponentName": false
    }]
  ]
}

// Write normal imports, babel transforms them
import { Button, TextField } from '@mui/material';
// Becomes: 
// import Button from '@mui/material/Button';
// import TextField from '@mui/material/TextField';

// Next.js optimized imports (built-in)
// next.config.js
module.exports = {
  modularizeImports: {
    '@mui/material': {
      transform: '@mui/material/{{member}}'
    },
    'lodash': {
      transform: 'lodash/{{member}}'
    }
  }
};
Common Pitfalls: CommonJS modules can't be tree shaken. Barrel exports (index.js) can prevent tree shaking. Side effects (CSS imports, global mutations) prevent elimination. Always analyze bundle with webpack-bundle-analyzer.

6. Bundle Optimization Webpack Rollup

Optimize production bundles with advanced techniques: minification, compression, code splitting, and caching strategies.

Optimization Technique Tool Benefit
Minification Remove whitespace, shorten names Terser, esbuild 30-50% size reduction
Compression Gzip, Brotli compression-webpack-plugin 60-80% size reduction
Code Splitting Separate vendor/async chunks SplitChunksPlugin Better caching
Content Hashing Filename based on content [contenthash] Long-term caching
Scope Hoisting Flatten module scope ModuleConcatenationPlugin Smaller bundle, faster

Example: Advanced Webpack optimization configuration

// webpack.config.js - Production optimization
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  mode: 'production',
  
  output: {
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].chunk.js',
    path: path.resolve(__dirname, 'dist'),
    clean: true,
    publicPath: '/assets/'
  },
  
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true, // Remove console.log
            drop_debugger: true,
            pure_funcs: ['console.info', 'console.debug']
          },
          mangle: {
            safari10: true
          },
          format: {
            comments: false
          }
        },
        extractComments: false,
        parallel: true
      })
    ],
    
    // Split chunks for better caching
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        // Vendor libraries
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10,
          reuseExistingChunk: true
        },
        // Common code shared across chunks
        common: {
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true,
          enforce: true
        },
        // Large libraries in separate chunks
        react: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'react',
          priority: 20
        },
        // UI libraries
        mui: {
          test: /[\\/]node_modules[\\/]@mui[\\/]/,
          name: 'mui',
          priority: 15
        }
      },
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      minSize: 20000,
      maxSize: 244000
    },
    
    // Runtime chunk for better caching
    runtimeChunk: {
      name: 'runtime'
    },
    
    // Module IDs based on path
    moduleIds: 'deterministic'
  },
  
  plugins: [
    // Gzip compression
    new CompressionPlugin({
      filename: '[path][base].gz',
      algorithm: 'gzip',
      test: /\.(js|css|html|svg)$/,
      threshold: 10240,
      minRatio: 0.8
    }),
    
    // Brotli compression (better than gzip)
    new CompressionPlugin({
      filename: '[path][base].br',
      algorithm: 'brotliCompress',
      test: /\.(js|css|html|svg)$/,
      compressionOptions: {
        level: 11
      },
      threshold: 10240,
      minRatio: 0.8
    }),
    
    // Bundle analysis
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      openAnalyzer: false,
      reportFilename: 'bundle-report.html'
    })
  ],
  
  performance: {
    maxAssetSize: 244000,
    maxEntrypointSize: 244000,
    hints: 'warning'
  }
};

Example: Vite optimization configuration

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { visualizer } from 'rollup-plugin-visualizer';
import compression from 'vite-plugin-compression';

export default defineConfig({
  plugins: [
    react(),
    
    // Gzip compression
    compression({
      algorithm: 'gzip',
      ext: '.gz'
    }),
    
    // Brotli compression
    compression({
      algorithm: 'brotliCompress',
      ext: '.br'
    }),
    
    // Bundle analysis
    visualizer({
      open: false,
      filename: 'dist/stats.html',
      gzipSize: true,
      brotliSize: true
    })
  ],
  
  build: {
    target: 'es2020',
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    },
    
    // Chunk splitting
    rollupOptions: {
      output: {
        manualChunks: {
          // Vendor chunks
          'react-vendor': ['react', 'react-dom', 'react-router-dom'],
          'ui-vendor': ['@mui/material', '@emotion/react'],
          'utils': ['lodash-es', 'date-fns']
        },
        
        // Chunk naming with content hash
        chunkFileNames: 'assets/[name].[hash].js',
        entryFileNames: 'assets/[name].[hash].js',
        assetFileNames: 'assets/[name].[hash].[ext]'
      }
    },
    
    // Chunk size warnings
    chunkSizeWarningLimit: 500,
    
    // Source maps for production debugging
    sourcemap: 'hidden',
    
    // CSS code splitting
    cssCodeSplit: true,
    
    // Asset inlining threshold
    assetsInlineLimit: 4096
  },
  
  // Optimize dependencies
  optimizeDeps: {
    include: ['react', 'react-dom'],
    exclude: ['@vite/client', '@vite/env']
  }
});

Example: Advanced bundle analysis and optimization

// Analyze bundle with webpack-bundle-analyzer
npm install -D webpack-bundle-analyzer

// Run analysis
npx webpack --profile --json > stats.json
npx webpack-bundle-analyzer stats.json

// Identify optimization opportunities:
// 1. Large dependencies (replace with lighter alternatives)
// 2. Duplicate code (adjust splitChunks)
// 3. Unused imports (tree shaking issues)
// 4. Large assets (optimize images, fonts)

// Source map explorer (alternative)
npm install -D source-map-explorer

// package.json
{
  "scripts": {
    "analyze": "source-map-explorer 'dist/*.js'"
  }
}

// Optimization strategies based on analysis:

// 1. Replace large libraries
// moment.js (67KB) → dayjs (2KB)
// lodash (71KB) → lodash-es + tree shaking
// MaterialUI lighter alternative or selective imports

// 2. Dynamic imports for large features
const Chart = lazy(() => import(
  /* webpackChunkName: "chart" */
  /* webpackPrefetch: true */
  './components/Chart'
));

// 3. Optimize images
// Use next/image or webpack image-loader
{
  test: /\.(png|jpg|jpeg)$/,
  type: 'asset',
  parser: {
    dataUrlCondition: {
      maxSize: 8 * 1024 // 8KB inline limit
    }
  },
  use: [
    {
      loader: 'image-webpack-loader',
      options: {
        mozjpeg: { progressive: true, quality: 65 },
        optipng: { enabled: true },
        pngquant: { quality: [0.65, 0.90], speed: 4 },
        gifsicle: { interlaced: false },
        webp: { quality: 75 }
      }
    }
  ]
}

// 4. Preload critical chunks
<link rel="preload" href="/assets/main.js" as="script">
<link rel="prefetch" href="/assets/chart.chunk.js" as="script">

// 5. Enable HTTP/2 server push
// In your server configuration
Link: </assets/main.js>; rel=preload; as=script
Link: </assets/styles.css>; rel=preload; as=style

Bundle Optimization Checklist

Optimization Target Tool Expected Reduction
Minification JS, CSS, HTML Terser, cssnano 30-40%
Compression All text files Gzip, Brotli 60-80%
Tree Shaking Unused code Webpack, Rollup 20-50%
Code Splitting Routes, vendors Dynamic imports Initial: 40-60%
Image Optimization PNG, JPG, SVG imagemin, WebP 50-70%
Font Subsetting Web fonts glyphhanger 70-90%