Skip to main content

Comprehensive Mocking Framework Design and Implementation

Detailed low-level design for a flexible, type-safe mocking framework with stubbing, spying, and verification capabilities for frontend testing.

A comprehensive low-level design for building a flexible mocking framework that supports stubbing, spying, and verification patterns while maintaining type safety and seamless integration with modern frontend testing ecosystems.

1. Problem Statement & Requirements

Clarifying questions

  1. Target platforms and browsers: Should this mocking framework support modern browsers (Chrome 90+, Firefox 85+, Safari 14+) and Node.js environments, or is there a specific target ecosystem?
  2. Authentication and authorization: Will the mocking framework need to handle authentication tokens, session management, or role-based access control for mocked endpoints?
  3. Real-time vs. static data: Should the framework support WebSocket mocking, SSE (Server-Sent Events), or only HTTP request/response mocking?
  4. Offline support requirements: Should the framework provide offline capability, caching strategies, or persistence for mocked data between sessions?
  5. Accessibility compliance: Is there a specific accessibility standard (e.g., WCAG 2.1 AA) that the mocking UI must meet?
  6. Internationalization needs: Will the framework support i18n for its UI components and error messages, or is English-only sufficient?
  7. Design system integration: Should the mocking UI integrate with an existing design system (e.g., Material Design, Tailwind CSS) or be built from scratch?

Functional requirements

  • Request interception: Intercept HTTP/HTTPS requests made by the application and route them to mock handlers
  • Response simulation: Return predefined responses (status codes, headers, body) for intercepted requests
  • Request matching: Match incoming requests based on URL path, method, headers, and query parameters
  • Dynamic response generation: Support dynamic response generation using templates or function-based logic
  • Mock configuration management: Allow defining, updating, and deleting mock configurations via UI or API
  • Request logging: Log intercepted requests with details for debugging and monitoring
  • Mock persistence: Save mock configurations to local storage or file system for persistence across sessions
  • Multiple mock scenarios: Support different mock configurations for various test environments (dev, staging, prod)
  • Mock groups and tags: Organize mocks into logical groups or tags for better management
  • Performance monitoring: Track response times and provide performance metrics for mocked endpoints

Non-functional requirements

  • Performance targets:
    • Response time: <50ms for mocked responses
    • Memory usage: <5MB for mock configuration in memory
    • Bundle size: <100KB for core mocking library
  • Accessibility compliance: WCAG 2.1 AA level compliance for all UI components and keyboard navigation support
  • Scalability: Support up to 1000 concurrent mock configurations without performance degradation
  • Reliability: 99.9% uptime for mock server when running in Node.js environment
  • Cross-platform compatibility: Support for Chrome, Firefox, Safari, Edge browsers and Node.js 16+
  • Security: No sensitive data exposure in logs or UI, proper input sanitization

Constraints and assumptions

  • Assumption: The framework will primarily be used in development environments for frontend testing
  • Constraint: Must not interfere with actual network requests in production builds
  • Assumption: The framework will be used alongside existing testing frameworks (Jest, Cypress, Playwright)
  • Constraint: Mock configurations will be defined in JSON or JavaScript format
  • Assumption: Users have basic knowledge of HTTP protocols and request/response structures
  • Constraint: Framework must support both browser and Node.js environments

Out of scope

  • Full API gateway implementation: This is not a production-grade API gateway but a development tool
  • Advanced WebSocket mocking: Only basic HTTP request/response interception is required
  • Complex state management: Not implementing complex state machines or orchestration engines
  • Database integration: Mocks will not persist to external databases, only in-memory or local storage
  • Real-time collaboration features: Multi-user editing or real-time sync between developers
  • Advanced security features: No encryption, authentication, or authorization mechanisms beyond basic request interception
  • IDE integration: Not implementing VS Code or other IDE extensions for direct mock configuration
  • Mobile app support: Focus is on web applications and Node.js environments only

2. Component Architecture

graph TD
    A[MockingFramework] --> B[MockManager]
    A --> C[MockEditor]
    A --> D[MockViewer]
    A --> E[MockRecorder]
    A --> F[MockServer]
    
    B --> B1[MockStore]
    B --> B2[MockService]
    B --> B3[RequestMatcher]
    
    C --> C1[CodeEditor]
    C --> C2[MockForm]
    C --> C3[ValidationEngine]
    
    D --> D1[MockList]
    D --> D2[MockDetail]
    D --> D3[MockFilter]
    
    E --> E1[NetworkInterceptor]
    E --> E2[RequestLogger]
    E --> E3[RecordingStore]
    
    F --> F1[MockProxy]
    F --> F2[ResponseGenerator]
    F --> F3[MockRouter]

Component Tree Breakdown

Smart Components (Container Components):

  • MockingFramework - Main container that orchestrates the entire mocking system
  • MockManager - Central coordinator for mock lifecycle management
  • MockEditor - Editor interface with form and code editor
  • MockViewer - Display and filter mock definitions
  • MockRecorder - Network capture and recording functionality

Dumb Components (Presentational Components):

  • CodeEditor - Syntax-highlighted code editing component
  • MockForm - Form fields for mock configuration
  • MockList - List view of all mocks with filtering capabilities
  • MockDetail - Detailed view of a single mock definition
  • NetworkInterceptor - Low-level network interception component
  • RequestLogger - Request logging display component

TypeScript Props Contracts

// MockManager Component
interface MockManagerProps {
  mocks: MockDefinition[];
  onMockCreated: (mock: MockDefinition) => void;
  onMockUpdated: (id: string, mock: MockDefinition) => void;
  onMockDeleted: (id: string) => void;
  onRecordingStarted: () => void;
  onRecordingStopped: () => void;
}

// MockEditor Component
interface MockEditorProps {
  mock?: MockDefinition;
  onSave: (mock: MockDefinition) => void;
  onCancel: () => void;
  isEditing?: boolean;
  validationErrors?: ValidationError[];
}

// MockViewer Component
interface MockViewerProps {
  mocks: MockDefinition[];
  selectedMockId?: string;
  onMockSelect: (id: string) => void;
  onMockDelete: (id: string) => void;
  onMockEdit: (mock: MockDefinition) => void;
  onMockToggleActive: (id: string, active: boolean) => void;
}

// MockForm Component
interface MockFormProps {
  mock?: MockDefinition;
  onChange: (mock: Partial<MockDefinition>) => void;
  errors?: ValidationError[];
  isSubmitting?: boolean;
}

// CodeEditor Component
interface CodeEditorProps {
  value: string;
  onChange: (value: string) => void;
  language?: 'javascript' | 'json' | 'yaml';
  readOnly?: boolean;
  height?: string;
}

// MockList Component
interface MockListComponentProps {
  mocks: MockDefinition[];
  selectedId?: string;
  onSelect: (id: string) => void;
  onDelete: (id: string) => void;
  onToggleActive: (id: string, active: boolean) => void;
}

// MockDetail Component
interface MockDetailProps {
  mock: MockDefinition;
  isActive: boolean;
  onToggleActive: (id: string, active: boolean) => void;
  onEdit: () => void;
}

UI State Machine

stateDiagram-v2
    [*] --> Idle
    Idle --> Editing : StartEdit
    Editing --> Saving : Save
    Saving --> Idle : Success
    Saving --> Error : Failure
    Editing --> Canceling : Cancel
    Canceling --> Idle : Success
    Idle --> Recording : StartRecording
    Recording --> Idle : StopRecording
    Recording --> Error : NetworkError
    Idle --> Viewing : ViewMock
    Viewing --> Editing : EditMock
    Viewing --> Deleting : DeleteMock
    Deleting --> Idle : Success
    Error --> Idle : Retry
    Error --> Editing : RetryEdit

    state Editing {
        [*] --> Validating
        Validating --> Saving : Valid
        Validating --> Editing : Invalid
    }

    state Recording {
        [*] --> Active
        Active --> Stopped : Stop
        Active --> Error : NetworkFailure
    }

Component Responsibilities and Props Contract

MockingFramework:

  • Responsibilities: Main orchestrator, manages component lifecycle, handles global state
  • Props: mocks, onMockCreated/Updated/Deleted, onRecordingStarted/Stopped

MockManager:

  • Responsibilities: Manages mock lifecycle, handles CRUD operations, coordinates with backend
  • Props: mocks, onMockCreated/Updated/Deleted, onRecordingStarted/Stopped

MockEditor:

  • Responsibilities: Provides UI for creating/editing mocks, handles form validation
  • Props: mock, onSave, onCancel, isEditing, validationErrors

MockViewer:

  • Responsibilities: Displays list of mocks, handles selection and filtering
  • Props: mocks, selectedMockId, onMockSelect/Delete/Edit/ToggleActive

MockRecorder:

  • Responsibilities: Captures network traffic, creates mock definitions from captured requests
  • Props: onRecordingStarted/Stopped, onMockCreated

Reusability Decisions

  1. CodeEditor is highly reusable across different mock types and configuration interfaces
  2. MockForm can be reused for different mock types with prop-based customization
  3. MockList/MockDetail components are generic enough to support various mock types
  4. NetworkInterceptor is a core utility that can be reused in other network monitoring tools

Component Lifecycle Considerations

  1. MockManager initializes with existing mocks from storage
  2. MockEditor handles form validation on input changes and submission
  3. MockViewer updates when mock list changes, handles selection persistence
  4. MockRecorder manages network interception lifecycle with proper cleanup
  5. CodeEditor handles syntax highlighting and theme switching
  6. MockDetail displays mock metadata with proper formatting and error handling

The component architecture follows a clear separation of concerns where container components handle state and business logic, while presentational components focus on UI rendering. The state machine captures the key interaction flows that require explicit state management for complex user workflows.

3. State Management Design

Local state vs. global state decisions

For a mocking framework, we need to distinguish between:

  • Local state: Component-specific UI state (e.g., active tab, form inputs, modal visibility)
  • Global state: Mock configuration data, recording status, and shared state across the entire app (e.g., mock definitions, active mocks, recording mode)

The global state is essential for:

  • Mock configuration persistence
  • Recording/playback status tracking
  • Cross-component communication (e.g., when a mock is updated, all dependent components should refresh)
  • Mock definition synchronization across the app

State management approach

I'll use Zustand as the state management solution for this mocking framework:

  1. Why Zustand:

    • Minimal API with no boilerplate
    • Built-in middleware support (e.g., for persistence, logging)
    • Excellent TypeScript support
    • No provider wrapping needed
    • Good performance characteristics
  2. Why not Redux Toolkit or Context API:

    • Redux Toolkit introduces boilerplate that's unnecessary for this use case
    • Context API requires provider wrapping and can cause unnecessary re-renders

State shape and normalization strategy

// MockDefinition represents a single mock configuration
interface MockDefinition {
  id: string;
  name: string;
  method: 'GET' | 'POST' | 'PUT' | 'DELETE';
  urlPattern: string;
  response: {
    status: number;
    headers?: Record<string, string>;
    body?: any;
  };
  isActive: boolean;
  createdAt: Date;
  updatedAt: Date;
}

// RecordingState represents the current recording status
interface RecordingState {
  isRecording: boolean;
  recordedRequests: RequestLog[];
  activeMockId?: string;
}

// RequestLog represents a captured HTTP request during recording
interface RequestLog {
  id: string;
  timestamp: Date;
  method: string;
  url: string;
  headers?: Record<string, string>;
  body?: any;
}

// Main state interface
interface MockingFrameworkState {
  // Mock definitions
  mocks: Record<string, MockDefinition>;
  
  // Recording state
  recording: RecordingState;
  
  // UI state
  activeTab: 'mocks' | 'recording' | 'settings';
  selectedMockId?: string;
  isModalOpen: boolean;
  
  // Configuration
  settings: {
    autoRecord: boolean;
    mockDelay: number;
    preserveOrder: boolean;
  };
}

Server state vs. client state separation

  • Client state:

    • Mock definitions (in-memory)
    • UI state (active tabs, modals)
    • Recording state
    • Configuration settings
  • Server state:

    • Mock definitions persisted to backend (via API)
    • Recorded request logs (if stored server-side)

Cache invalidation strategy

  1. On mock creation/update: Invalidate dependent components (e.g., request preview, mock list)
  2. On recording start/stop: Clear and reset recording state
  3. On mock deletion: Remove from mocks store and update references

Optimistic updates design

For mock creation/update operations:

  1. Update local state optimistically
  2. Send to backend API
  3. If successful, keep local update
  4. If failed, rollback to previous state with error notification

Critical-path hook implementation

import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';

// Define the state and actions
interface MockingFrameworkStore {
  // State properties
  mocks: Record<string, MockDefinition>;
  recording: RecordingState;
  activeTab: 'mocks' | 'recording' | 'settings';
  selectedMockId?: string;
  isModalOpen: boolean;
  settings: {
    autoRecord: boolean;
    mockDelay: number;
    preserveOrder: boolean;
  };
  
  // Actions
  addMock: (mock: MockDefinition) => void;
  updateMock: (id: string, updates: Partial<MockDefinition>) => void;
  deleteMock: (id: string) => void;
  setActiveTab: (tab: 'mocks' | 'recording' | 'settings') => void;
  setSelectedMockId: (id?: string) => void;
  toggleModal: () => void;
  startRecording: () => void;
  stopRecording: () => void;
  addRecordedRequest: (request: RequestLog) => void;
  updateSettings: (settings: Partial<MockingFrameworkStore['settings']>) => void;
  
  // Async actions with optimistic updates
  saveMock: (mock: MockDefinition) => Promise<void>;
  deleteMockAsync: (id: string) => Promise<void>;
}

// Create the store with Zustand
const useMockingFrameworkStore = create<MockingFrameworkStore>()(
  devtools(
    persist(
      (set, get) => ({
        // Initial state
        mocks: {},
        recording: {
          isRecording: false,
          recordedRequests: [],
        },
        activeTab: 'mocks',
        selectedMockId: undefined,
        isModalOpen: false,
        settings: {
          autoRecord: false,
          mockDelay: 0,
          preserveOrder: true,
        },
        
        // Actions
        addMock: (mock) => {
          set((state) => ({
            mocks: { ...state.mocks, [mock.id]: mock },
          }));
        },
        
        updateMock: (id, updates) => {
          set((state) => ({
            mocks: {
              ...state.mocks,
              [id]: { ...state.mocks[id], ...updates },
            },
          }));
        },
        
        deleteMock: (id) => {
          set((state) => {
            const newMocks = { ...state.mocks };
            delete newMocks[id];
            return { mocks: newMocks };
          });
        },
        
        setActiveTab: (tab) => set({ activeTab: tab }),
        setSelectedMockId: (id) => set({ selectedMockId: id }),
        toggleModal: () => set((state) => ({ isModalOpen: !state.isModalOpen })),
        
        startRecording: () => {
          set({ recording: { isRecording: true, recordedRequests: [] } });
        },
        
        stopRecording: () => {
          set({ recording: { isRecording: false, recordedRequests: [] } });
        },
        
        addRecordedRequest: (request) => {
          set((state) => ({
            recording: {
              ...state.recording,
              recordedRequests: [...state.recording.recordedRequests, request],
            },
          }));
        },
        
        updateSettings: (updates) => {
          set((state) => ({
            settings: { ...state.settings, ...updates },
          }));
        },
        
        // Async actions with optimistic updates
        saveMock: async (mock) => {
          // Optimistically update local state
          set((state) => ({
            mocks: { ...state.mocks, [mock.id]: mock },
          }));
          
          try {
            // Simulate API call
            await fetch(`/api/mocks/${mock.id}`, {
              method: 'PUT',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify(mock),
            });
          } catch (error) {
            // Rollback on failure
            set((state) => {
              const newMocks = { ...state.mocks };
              delete newMocks[mock.id];
              return { mocks: newMocks };
            });
            throw error;
          }
        },
        
        deleteMockAsync: async (id) => {
          // Optimistically remove from local state
          set((state) => {
            const newMocks = { ...state.mocks };
            delete newMocks[id];
            return { mocks: newMocks };
          });
          
          try {
            await fetch(`/api/mocks/${id}`, { method: 'DELETE' });
          } catch (error) {
            // Restore if failed
            set((state) => ({
              mocks: { ...state.mocks, [id]: state.mocks[id] },
            }));
            throw error;
          }
        },
      }),
      {
        name: 'mocking-framework-storage',
        partialize: (state) => ({
          mocks: state.mocks,
          settings: state.settings,
        }),
      }
    )
  )
);

// Custom hook for consuming the store
const useMockingFramework = () => {
  const [
    mocks,
    recording,
    activeTab,
    selectedMockId,
    isModalOpen,
    settings,
    addMock,
    updateMock,
    deleteMock,
    setActiveTab,
    setSelectedMockId,
    toggleModal,
    startRecording,
    stopRecording,
    addRecordedRequest,
    updateSettings,
    saveMock,
    deleteMockAsync
  ] = useMockingFrameworkStore((state) => [
    state.mocks,
    state.recording,
    state.activeTab,
    state.selectedMockId,
    state.isModalOpen,
    state.settings,
    state.addMock,
    state.updateMock,
    state.deleteMock,
    state.setActiveTab,
    state.setSelectedMockId,
    state.toggleModal,
    state.startRecording,
    state.stopRecording,
    state.addRecordedRequest,
    state.updateSettings,
    state.saveMock,
    state.deleteMockAsync
  ]);

  return {
    mocks,
    recording,
    activeTab,
    selectedMockId,
    isModalOpen,
    settings,
    addMock,
    updateMock,
    deleteMock,
    setActiveTab,
    setSelectedMockId,
    toggleModal,
    startRecording,
    stopRecording,
    addRecordedRequest,
    updateSettings,
    saveMock,
    deleteMockAsync
  };
};

export { useMockingFrameworkStore, useMockingFramework };

The hook returns:

  • State: All relevant state properties for the mocking framework
  • Actions: Functions to mutate state and perform async operations
  • Return shape: Clean, flat object with all necessary properties for consumers

Key state transitions:

  1. Mock creation → addMock → updates local store and syncs to backend
  2. Mock update → updateMock → optimistic update with potential rollback
  3. Recording state changes → startRecording/stopRecording → updates recording status
  4. Settings change → updateSettings → persists configuration changes

The implementation includes:

  • Devtools middleware for debugging
  • Persistence middleware to maintain state across sessions
  • Optimistic updates for better UX during async operations
  • TypeScript typing with proper interfaces and contracts
  • Async error handling with rollback capability

4. Routing & Navigation

Route Structure and Hierarchy

The mocking framework will have a clean route structure with the following hierarchy:

graph TD
    A[Root] --> B[Dashboard]
    A --> C[Mocks]
    A --> D[Settings]
    C --> E[MockList]
    C --> F[MockEditor]
    C --> G[MockDetails]
  • / - Dashboard route showing overview and stats
  • /mocks - Main mocks collection view with list and creation
  • /mocks/new - Mock creation form
  • /mocks/:id - Detailed view of a specific mock
  • /mocks/:id/edit - Edit mode for existing mock
  • /settings - Configuration and preferences

Protected routes will require authentication via JWT tokens stored in localStorage or cookies.

Code Splitting Strategy

Route-based code splitting will be implemented using React.lazy and Suspense:

// routes.tsx
const Dashboard = React.lazy(() => import('../pages/Dashboard'));
const MockList = React.lazy(() => import('../pages/MockList'));
const MockEditor = React.lazy(() => import('../pages/MockEditor'));

// Route configuration with lazy loading
const routes = [
  {
    path: '/',
    element: <Dashboard />,
    lazy: true,
  },
  {
    path: '/mocks',
    element: <MockList />,
    lazy: true,
  },
  {
    path: '/mocks/:id/edit',
    element: <MockEditor />,
    lazy: true,
  },
];

Justification: Route-based splitting provides better user experience with faster initial load times and proper loading states during component transitions.

Authentication will be handled through route guards:

// AuthGuard.tsx
const AuthGuard: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
  
  if (!isAuthenticated) {
    return <Navigate to="/login" replace />;
  }
  
  return children;
};

// Protected route configuration
const ProtectedRoute = ({ component: Component, ...rest }: any) => (
  <Route
    {...rest}
    element={
      <AuthGuard>
        <Component />
      </AuthGuard>
    }
  />
);

Deep Linking Support and URL Parameter Design

URL parameters will follow RESTful conventions:

// URL structure examples:
// /mocks/1234567890abcdef
// /mocks/1234567890abcdef/edit
// /mocks?filter=status:active&sort=created_at

interface MockParams {
  id?: string;
}

interface MockQueryParams {
  filter?: string;
  sort?: string;
  page?: number;
}

Browser History Management and Scroll Restoration

Scroll restoration will be handled using React Router's built-in scroll restoration:

// App.tsx
import { BrowserRouter as Router, useScrollRestoration } from 'react-router-dom';

function App() {
  return (
    <Router>
      <ScrollRestoration />
      <Routes>
        {/* Routes */}
      </Routes>
    </Router>
  );
}

// Custom scroll restoration hook for specific use cases
const useScrollRestoration = () => {
  useEffect(() => {
    const handleScroll = () => {
      sessionStorage.setItem('scrollPosition', window.scrollY.toString());
    };

    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);
};

5. API Integration Layer

API Client Design Pattern

The mocking framework will require a flexible API client that can seamlessly switch between real API calls and mocked responses. We'll implement a custom fetch wrapper with interceptor support to handle both scenarios.

// api/client.ts
interface ApiClientConfig {
  baseURL: string;
  timeout?: number;
  headers?: Record<string, string>;
}

class ApiClient {
  private baseURL: string;
  private timeout: number;
  private headers: Record<string, string>;
  private interceptors: {
    request: Array<(config: ApiClientConfig) => Promise<ApiClientConfig>>;
    response: Array<(response: any) => Promise<any>>;
  };

  constructor(config: ApiClientConfig) {
    this.baseURL = config.baseURL;
    this.timeout = config.timeout || 5000;
    this.headers = config.headers || {};
    this.interceptors = {
      request: [],
      response: []
    };
  }

  async request<T>(config: {
    url: string;
    method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
    data?: any;
    headers?: Record<string, string>;
  }): Promise<T> {
    const finalConfig = await this.applyRequestInterceptors({
      ...config,
      url: `${this.baseURL}${config.url}`
    });

    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), this.timeout);

    try {
      const response = await fetch(finalConfig.url, {
        method: finalConfig.method || 'GET',
        headers: {
          ...this.headers,
          ...finalConfig.headers
        },
        body: finalConfig.data ? JSON.stringify(finalConfig.data) : undefined,
        signal: controller.signal
      });

      clearTimeout(timeoutId);

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }

      const result = await response.json();
      return await this.applyResponseInterceptors(result);
    } catch (error) {
      clearTimeout(timeoutId);
      throw error;
    }
  }

  addRequestInterceptor(interceptor: (config: ApiClientConfig) => Promise<ApiClientConfig>) {
    this.interceptors.request.push(interceptor);
  }

  addResponseInterceptor(interceptor: (response: any) => Promise<any>) {
    this.interceptors.response.push(interceptor);
  }

  private async applyRequestInterceptors(config: ApiClientConfig): Promise<ApiClientConfig> {
    let finalConfig = { ...config };
    for (const interceptor of this.interceptors.request) {
      finalConfig = await interceptor(finalFinalConfig);
    }
    return finalConfig;
  }

  private async applyResponseInterceptors(response: any): Promise<any> {
    let finalResponse = response;
    for (const interceptor of this.interceptors.response) {
      finalResponse = await interceptor(finalResponse);
    }
    return finalResponse;
  }
}

export const apiClient = new ApiClient({
  baseURL: process.env.REACT_APP_API_BASE_URL || 'http://localhost:3000/api'
});

Request/Response Interceptors

The framework will support interceptors to modify requests and responses, enabling mocking capabilities:

// api/interceptors.ts
import { apiClient } from './client';
import { MockConfig, MockResponse } from '../types';

class MockInterceptor {
  private mockConfig: MockConfig | null = null;

  constructor() {
    this.setupInterceptors();
  }

  setupInterceptors() {
    // Request interceptor - check if we should mock this request
    apiClient.addRequestInterceptor(async (config) => {
      if (this.shouldMock(config.url)) {
        // Modify config to point to mock server or return mock data directly
        return {
          ...config,
          url: this.getMockUrl(config.url)
        };
      }
      return config;
    });

    // Response interceptor - handle mock responses
    apiClient.addResponseInterceptor(async (response) => {
      if (this.mockConfig && this.shouldMock(response.config?.url)) {
        return this.transformMockResponse(response);
      }
      return response;
    });
  }

  setMockConfig(config: MockConfig) {
    this.mockConfig = config;
  }

  shouldMock(url: string): boolean {
    if (!this.mockConfig) return false;
    
    // Check if URL matches any mock patterns
    return this.mockConfig.mockPatterns.some(pattern => 
      new RegExp(pattern).test(url)
    );
  }

  getMockUrl(originalUrl: string): string {
    // Return mock server endpoint or local mock data URL
    return `${process.env.REACT_APP_MOCK_SERVER_URL || '/mock'}/api${originalUrl.split('/api')[1]}`;
  }

  transformMockResponse(response: any): Promise<any> {
    // Apply mock transformations if needed
    return Promise.resolve(response);
  }
}

export const mockInterceptor = new MockInterceptor();

Error Handling and Retry Strategy

Comprehensive error handling with retry logic for resilient API interactions:

// api/errorHandler.ts
interface RetryConfig {
  maxRetries?: number;
  retryDelay?: number;
  shouldRetry?: (error: Error, attempt: number) => boolean;
}

class ErrorHandler {
  static async handleApiError<T>(
    error: Error,
    retryConfig?: RetryConfig
  ): Promise<T | null> {
    const { maxRetries = 3, retryDelay = 1000 } = retryConfig || {};
    
    // Log error for monitoring
    console.error('API Error:', error);
    
    // Handle network errors (offline, timeout)
    if (error.name === 'AbortError' || error.message.includes('timeout')) {
      return this.retryWithBackoff<T>(error, maxRetries, retryDelay);
    }
    
    // Handle HTTP errors
    if (error.message.includes('HTTP')) {
      const statusCode = parseInt(error.message.split(' ')[1]);
      if (statusCode >= 500) {
        return this.retryWithBackoff<T>(error, maxRetries, retryDelay);
      }
    }
    
    // For client errors (4xx), don't retry
    if (error.message.includes('HTTP') && parseInt(error.message.split(' ')[1]) < 500) {
      return null;
    }
    
    return null;
  }

  private static async retryWithBackoff<T>(
    error: Error,
    maxRetries: number,
    delay: number
  ): Promise<T | null> {
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, attempt - 1)));
        // Retry logic would be implemented here with actual API call
        return null; // Placeholder - actual implementation would make new request
      } catch (retryError) {
        if (attempt === maxRetries) {
          console.error(`Retry failed after ${maxRetries} attempts:`, retryError);
          return null;
        }
      }
    }
    return null;
  }
}

export const errorHandler = new ErrorHandler();

Loading, Error, and Empty States Design

Comprehensive state management for all API interaction scenarios:

// api/types.ts
export type ApiState<T> = 
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: string };

export type MockConfig = {
  enabled: boolean;
  mockPatterns: string[];
  responses: Record<string, MockResponse>;
};

export type MockResponse = {
  status: number;
  body?: any;
  headers?: Record<string, string>;
  delay?: number;
};

export type ApiRequestConfig = {
  url: string;
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
  data?: any;
  params?: Record<string, string>;
};

Data Fetching Strategy

The framework will support multiple data fetching strategies:

// api/fetchStrategies.ts
import { useEffect, useState } from 'react';
import { ApiState } from './types';

export function useApi<T>(
  fetcher: () => Promise<T>,
  initialData?: T,
  options?: {
    enabled?: boolean;
    cacheKey?: string;
    staleTime?: number;
  }
): ApiState<T> {
  const [state, setState] = useState<ApiState<T>>({
    status: options?.enabled ? 'loading' : 'idle'
  });

  useEffect(() => {
    if (!options?.enabled) return;

    const fetchData = async () => {
      try {
        setState({ status: 'loading' });
        const data = await fetcher();
        setState({ status: 'success', data });
      } catch (error) {
        setState({
          status: 'error',
          error: error instanceof Error ? error.message : 'Unknown error'
        });
      }
    };

    fetchData();
  }, [options?.enabled]);

  return state;
}

// For mock-aware data fetching
export function useMockableApi<T>(
  url: string,
  options?: {
    enabled?: boolean;
    mockConfig?: MockConfig;
  }
): ApiState<T> {
  const [state, setState] = useState<ApiState<T>>({ status: 'idle' });

  useEffect(() => {
    if (!options?.enabled) return;

    const fetchData = async () => {
      try {
        setState({ status: 'loading' });
        
        // Check if mocking is enabled and should apply
        const mockEnabled = options.mockConfig?.enabled;
        const shouldMock = mockEnabled && 
          options.mockConfig?.mockPatterns.some(pattern => 
            new RegExp(pattern).test(url)
          );

        let data: T;
        if (shouldMock && options.mockConfig?.responses) {
          // Apply mock response logic here
          data = await applyMockResponse<T>(url, options.mockConfig.responses);
        } else {
          // Normal API call
          const response = await apiClient.request<T>({ url });
          data = response;
        }

        setState({ status: 'success', data });
      } catch (error) {
        setState({
          status: 'error',
          error: error instanceof Error ? error.message : 'Unknown error'
        });
      }
    };

    fetchData();
  }, [options?.enabled, url]);

  return state;
}

async function applyMockResponse<T>(url: string, mockResponses: Record<string, MockResponse>): Promise<T> {
  // Implementation for applying mock responses based on URL patterns
  const response = mockResponses[url] || mockResponses['default'];
  
  if (response.delay) {
    await new Promise(resolve => setTimeout(resolve, response.delay));
  }

  return response.body as T;
}

API Response Transformation Layer

DTO to domain model mapping with proper type safety:

// api/transformers.ts
import { MockResponse } from './types';

export interface ApiResponse<T> {
  data: T;
  metadata?: {
    timestamp: Date;
    version: string;
    status: 'success' | 'error';
  };
}

export interface MockApiResponse<T> extends ApiResponse<T> {
  isMocked: boolean;
  mockSource?: string;
}

// Generic transformer for API responses
export function transformApiResponse<T>(rawResponse: any): ApiResponse<T> {
  return {
    data: rawResponse.data,
    metadata: {
      timestamp: new Date(),
      version: rawResponse.version || '1.0',
      status: rawResponse.status || 'success'
    }
  };
}

// Mock-aware transformer
export function transformMockApiResponse<T>(
  rawResponse: any,
  mockConfig?: MockConfig
): MockApiResponse<T> {
  const baseResponse = transformApiResponse<T>(rawResponse);
  
  return {
    ...baseResponse,
    isMocked: !!mockConfig?.enabled,
    mockSource: mockConfig?.responses ? 'configured' : 'default'
  };
}

Sequence Diagram for Primary Data Flows

sequenceDiagram
    participant U as User
    participant F as Frontend
    participant M as Mock Server
    participant A as Actual API
    
    U->>F: Trigger API Call (e.g., fetchUsers())
    F->>F: Check Mock Configuration
    alt Mock Enabled
        F->>M: Forward to Mock Server
        M-->>F: Return Mock Response
    else Mock Disabled
        F->>A: Forward to Actual API
        A-->>F: Return Real Response
    end
    F->>U: Display Result (Success/Error)

6. Performance Design

Core Web Vitals targets (LCP, INP, CLS) with specific numbers

  • Largest Contentful Paint (LCP): <2.5s for 95% of users
  • Interaction to Next Paint (INP): <200ms for 95% of interactions
  • Cumulative Layout Shift (CLS): <0.1 for 95% of users

Bundle size budget per route

  • Mocking Framework Dashboard: <200KB (including dependencies)
  • Mock Creation Wizard: <150KB
  • Mock Playback Interface: <100KB
  • Settings/Configuration: <50KB

Critical rendering path optimization

  • Critical CSS: Inline essential styles for dashboard and wizard views
  • Lazy loading: Non-critical components (e.g., advanced filters, statistics) loaded on demand
  • Code splitting: Route-based splitting with dynamic imports for heavy components like visualization tools
  • Resource hints: Preload key assets (fonts, icons) using <link rel="preload">

Image optimization strategy

  • Formats: WebP for modern browsers, JPEG fallback for legacy support
  • Lazy loading: loading="lazy" attribute on all dashboard images and preview thumbnails
  • Responsive images: srcset with multiple sizes for different viewport widths
  • Image compression: 20% reduction in file size without perceptible quality loss

Font loading strategy

  • Font display: swap for all custom fonts to prevent FOIT (Flash of Invisible Text)
  • Preload: Critical font files preloaded via <link rel="preload">
  • Subset loading: Only load characters needed for current view (e.g., Latin subset for English UI)
  • Fallback fonts: Robust fallback stack to prevent layout shifts

Memoization strategy

// Memoize expensive operations in mock comparison logic
const compareMocks = React.memo(
  ({ mock1, mock2 }: { mock1: Mock; mock2: Mock }) => {
    const result = useMemo(() => {
      return compareMockData(mock1, mock2);
    }, [mock1, mock2]);
    
    return <ComparisonResult result={result} />;
  }
);

// Use useCallback for event handlers to prevent unnecessary re-renders
const handleMockUpdate = useCallback((mockId: string, updates: Partial<Mock>) => {
  dispatch(updateMock(mockId, updates));
}, [dispatch]);

// Use React.memo for child components that receive stable props
const MockCard = React.memo(({ mock }: { mock: Mock }) => {
  return <div>{mock.name}</div>;
});

Virtualization for large lists

For mock list rendering with >1000 entries:

// Using react-window for efficient virtualization
import { FixedSizeList as List } from 'react-window';

const VirtualizedMockList = ({ mocks }: { mocks: Mock[] }) => {
  const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => (
    <div style={style}>
      <MockCard mock={mocks[index]} />
    </div>
  );

  return (
    <List
      height={600}
      itemCount={mocks.length}
      itemSize={80}
      width="100%"
    >
      {Row}
    </List>
  );
};

Prefetching and preloading strategy

  • Mock templates: Preload popular template definitions on dashboard load
  • API response schemas: Cache and prefetch expected response structures for mock creation
  • Visualization data: Preload sample data for chart rendering in mock preview mode
  • Dependency preloading: Load heavy visualization libraries (d3, chart.js) only when needed in mock preview mode

Performance monitoring and optimization

  • Bundle analysis: Use Webpack Bundle Analyzer to identify large dependencies and optimize imports
  • Memory profiling: Monitor for memory leaks in mock data handling and comparison operations
  • CPU usage tracking: Profile heavy operations like deep object comparison and diff generation
  • Network optimization: Implement request deduplication for concurrent mock data fetches

Optimization techniques

  • Debounced input handling: For search/filter operations in mock lists (500ms debounce)
  • Throttled rendering: For real-time mock comparison updates (100ms throttle)
  • Efficient diff algorithms: Use optimized diffing for comparing large mock objects
  • Async rendering: Use React's useDeferredValue for non-critical UI updates like search results
  • Service worker caching: Cache static assets and frequently accessed mock templates for offline use

7. Error Handling & Resilience

Error Boundary Placement Strategy

Error boundaries should be strategically placed to prevent cascading failures while maintaining user experience. The placement follows a hierarchical approach:

  • Application-level error boundary: Catches unhandled errors in the entire app, displaying a generic "Something went wrong" message
  • Feature-level error boundaries: Wrap major feature components (e.g., dashboard, user profile)
  • Component-level error boundaries: Handle specific UI component failures (e.g., chart rendering errors)
graph TD
    A[App Root] --> B[ErrorBoundary]
    B --> C[DashboardFeature]
    B --> D[UserProfileFeature]
    B --> E[NavigationComponent]
    C --> F[ChartComponent]
    C --> G[TableComponent]
    D --> H[AvatarComponent]
    D --> I[UserDetailsComponent]

Global Error Handler Design

A centralized error handler manages uncaught exceptions and logs them for monitoring:

// src/core/error/ErrorLogger.ts
export interface ErrorContext {
  componentStack?: string;
  url?: string;
  userAgent?: string;
}

export class ErrorLogger {
  static log(error: Error, context: ErrorContext = {}): void {
    const errorData = {
      timestamp: new Date().toISOString(),
      message: error.message,
      stack: error.stack,
      context,
      userAgent: navigator.userAgent,
      url: window.location.href,
    };

    // Send to monitoring service (Sentry, LogRocket)
    if (process.env.NODE_ENV === 'production') {
      // Example: Sentry.captureException(error, { contexts: context });
    }

    console.error('Application Error:', errorData);
  }
}

Offline / Network Failure Handling

The mocking framework must gracefully handle network interruptions:

// src/core/network/NetworkManager.ts
class NetworkManager {
  private isOnline = navigator.onLine;
  
  constructor() {
    window.addEventListener('online', () => this.handleOnlineChange(true));
    window.addEventListener('offline', () => this.handleOnlineChange(false));
  }

  private handleOnlineChange(isOnline: boolean): void {
    this.isOnline = isOnline;
    if (!isOnline) {
      // Show offline indicator in UI
      // Queue pending requests for retry when online
    }
  }

  isOnline(): boolean {
    return this.isOnline;
  }

  async withRetry<T>(fn: () => Promise<T>, retries = 3): Promise<T> {
    let lastError: Error;
    
    for (let i = 0; i <= retries; i++) {
      try {
        return await fn();
      } catch (error) {
        lastError = error as Error;
        
        if (i === retries) {
          throw lastError;
        }
        
        // Exponential backoff
        await new Promise(resolve => 
          setTimeout(resolve, Math.pow(2, i) * 1000)
        );
      }
    }
    
    throw lastError;
  }
}

Graceful Degradation Patterns

When mocking fails or is unavailable, the framework should provide fallback behavior:

// src/core/mocking/MockManager.ts
class MockManager {
  private fallbackMode = false;
  
  async loadMockData(): Promise<MockData[]> {
    try {
      // Attempt to load from cache or network
      const data = await this.fetchFromCacheOrNetwork();
      return data;
    } catch (error) {
      // Fallback to hardcoded mock data
      console.warn('Failed to load mocks, using fallback:', error);
      this.fallbackMode = true;
      return this.getFallbackMockData();
    }
  }

  private getFallbackMockData(): MockData[] {
    return [
      { id: 'fallback-1', name: 'Default Mock', data: {} },
      { id: 'fallback-2', name: 'Fallback Template', data: {} }
    ];
  }

  isInFallbackMode(): boolean {
    return this.fallbackMode;
  }
}

User Feedback on Errors

Comprehensive error feedback mechanisms:

// src/components/ToastManager.tsx
import React, { useState, useEffect } from 'react';

interface Toast {
  id: string;
  type: 'error' | 'warning' | 'success';
  message: string;
  duration?: number;
}

const ToastManager = () => {
  const [toasts, setToasts] = useState<Toast[]>([]);
  
  const addToast = (toast: Omit<Toast, 'id'>) => {
    const id = Date.now().toString();
    setToasts(prev => [...prev, { ...toast, id }]);
    
    if (toast.duration) {
      setTimeout(() => removeToast(id), toast.duration);
    }
  };
  
  const removeToast = (id: string) => {
    setToasts(prev => prev.filter(t => t.id !== id));
  };
  
  return (
    <div className="toast-container">
      {toasts.map(toast => (
        <div 
          key={toast.id} 
          className={`toast toast--${toast.type}`}
        >
          {toast.message}
        </div>
      ))}
    </div>
  );
};

export default ToastManager;

Error Recovery Mechanisms

// src/core/mocking/RecoveryManager.ts
class RecoveryManager {
  private recoveryQueue: Array<{ 
    operation: () => Promise<any>; 
    retries: number; 
  }> = [];

  async executeWithRecovery<T>(operation: () => Promise<T>): Promise<T> {
    try {
      return await operation();
    } catch (error) {
      // Queue for retry if appropriate
      if (this.shouldRetry(error)) {
        this.recoveryQueue.push({
          operation,
          retries: 3
        });
        return this.processRecoveryQueue();
      }
      
      throw error;
    }
  }

  private shouldRetry(error: any): boolean {
    // Retry on network errors, timeout, or transient failures
    const retryableErrors = ['Network Error', 'Timeout', '503'];
    return retryableErrors.some(msg => 
      error.message?.includes(msg) || 
      error.status === 503
    );
  }

  private async processRecoveryQueue(): Promise<any> {
    // Process queued operations with exponential backoff
    while (this.recoveryQueue.length > 0) {
      const { operation, retries } = this.recoveryQueue.shift()!;
      
      try {
        return await operation();
      } catch (error) {
        if (retries > 0) {
          this.recoveryQueue.push({
            operation,
            retries: retries - 1
          });
          
          await new Promise(resolve => 
            setTimeout(resolve, Math.pow(2, retries) * 1000)
          );
        } else {
          throw error;
        }
      }
    }
  }
}

Error Boundary Implementation

// src/core/error/ErrorBoundary.tsx
import React from 'react';

interface ErrorBoundaryState {
  hasError: boolean;
  error?: Error;
  errorInfo?: React.ErrorInfo;
}

class ErrorBoundary extends React.Component<
  { children: React.ReactNode },
  ErrorBoundaryState
> {
  constructor(props: { children: React.ReactNode }) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
    // Log error to monitoring service
    ErrorLogger.log(error, {
      componentStack: errorInfo.componentStack,
      url: window.location.href
    });
    
    this.setState({ errorInfo });
  }

  render(): React.ReactNode {
    if (this.state.hasError) {
      return (
        <div className="error-boundary">
          <h2>Something went wrong.</h2>
          <details style={{ whiteSpace: 'pre-wrap' }}>
            {this.state.error?.toString()}
            <br />
            {this.state.errorInfo?.componentStack}
          </details>
        </div>
      );
    }

    return this.props.children;
  }
}

export default ErrorBoundary;

8. Testing Strategy

Unit Testing Approach

Component Tests (70% of test coverage):

  • Test all interactive components with various prop combinations
  • Mock external dependencies (APIs, services) using Jest mocks
  • Test edge cases and error states for each component
  • Validate proper rendering of different state transitions

Utility Tests (20% of test coverage):

  • Test core utility functions (data transformation, validation logic)
  • Test helper methods for request/response processing
  • Validate edge cases in data normalization and error handling

Hook Tests (10% of test coverage):

  • Test custom hooks with different state transitions
  • Validate side-effect execution (API calls, event listeners)
  • Test hook return values and callback behavior

Integration Testing Strategy

API Integration Tests:

  • End-to-end tests for complete request/response cycles
  • Test integration between mock configuration and actual HTTP requests
  • Validate that mocks properly intercept and respond to requests

State Management Integration:

  • Test that mock configurations persist through state updates
  • Validate proper interaction between mock store and component state
  • Test that multiple concurrent mocks don't interfere with each other

E2E Testing Scope

Cypress Test Coverage:

  • Complete mock setup workflow (create, edit, delete)
  • User journey testing for mock configuration UI
  • Integration tests for mock behavior in real browser context
  • Test that mocked responses match expected format and timing

Critical User Flows:

  1. Creating a new mock configuration with various HTTP methods
  2. Editing existing mock responses in real-time
  3. Deleting mocks and verifying cleanup behavior
  4. Testing concurrent mock scenarios

Visual Regression Testing

Screenshot Strategy:

  • Capture UI states for all major mock configuration views
  • Test responsive behavior across different screen sizes
  • Validate that UI updates properly when mocks are added/modified

Accessibility Testing Automation

Axe-core Integration:

  • Run accessibility checks on all mock configuration UIs
  • Validate proper ARIA attributes for mock list items and controls
  • Test keyboard navigation through mock configuration forms

Test Data Strategy

Fixtures:

// Mock configuration fixtures for testing
const mockConfigurations = {
  basicGet: {
    id: '1',
    method: 'GET',
    path: '/api/users',
    response: { status: 200, body: { users: [] } },
    delay: 100
  },
  errorResponse: {
    id: '2',
    method: 'POST',
    path: '/api/users',
    response: { status: 500, body: { error: 'Internal Server Error' } }
  },
  delayedResponse: {
    id: '3',
    method: 'GET',
    path: '/api/data',
    response: { status: 200, body: { data: 'mocked' } },
    delay: 2000
  }
};

Test Data Generation:

  • Generate realistic mock configurations with various HTTP methods
  • Create test scenarios for edge cases (empty responses, malformed data)
  • Implement data factories for consistent test setup

Test Coverage Targets

Code Coverage:

  • Components: 85% line coverage, 90% branch coverage
  • Utilities: 95% line coverage
  • Hooks: 90% line coverage
  • Integration: 80% end-to-end coverage

Test Types:

  • Unit tests: 65% of total test suite
  • Integration tests: 20% of total test suite
  • E2E tests: 15% of total test suite

Test Environment Setup

Jest Configuration:

// jest.config.ts
export default {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/src/test/setup.ts'],
  collectCoverageFrom: [
    'src/**/*.{ts,tsx}',
    '!src/**/*.d.ts',
    '!src/**/types/**'
  ],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 85,
      lines: 85,
      statements: 85
    }
  }
};

Test Isolation:

  • Each test runs in isolated environment with clean mock state
  • Use Jest's built-in mock reset between tests
  • Implement proper cleanup of global mock configurations

9. Accessibility (A11y)

WCAG 2.1 AA compliance checklist for this feature

The mocking framework must meet WCAG 2.1 AA standards across all user interactions:

  • Text Contrast: All text must have a contrast ratio of at least 4.5:1 against its background
  • Keyboard Navigation: Full keyboard accessibility for all interactive elements (no mouse-only interactions)
  • Focus Management: Proper focus order and visible focus indicators for all interactive components
  • Screen Reader Support: Complete ARIA labeling and semantic HTML structure
  • Error Identification: Clear error messages with programmatically associated labels
  • Input Validation: Real-time feedback for invalid inputs with accessible error messages

Keyboard navigation support

All mocking framework UI components must be fully operable via keyboard:

// Keyboard navigation handler for mock editor
const handleKeyboardNavigation = (e: KeyboardEvent) => {
  if (e.key === 'Tab') {
    // Handle tab navigation between fields
    e.preventDefault();
    handleTabNavigation(e.shiftKey);
  } else if (e.key === 'Enter' && e.target instanceof HTMLElement) {
    // Handle form submission with Enter key
    handleFormSubmit();
  } else if (e.key === 'Escape') {
    // Close modals with Escape key
    handleCloseModal();
  }
};

Focus management strategy

Focus trapping within modal dialogs and proper focus restoration:

// Focus trap implementation for mock creation modal
const useFocusTrap = (isOpen: boolean) => {
  const modalRef = useRef<HTMLDivElement>(null);
  
  useEffect(() => {
    if (isOpen) {
      const focusableElements = modalRef.current?.querySelectorAll(
        'button, input, select, textarea, [tabindex]:not([tabindex="-1"])'
      ) || [];
      
      const firstElement = focusableElements[0] as HTMLElement;
      const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement;
      
      const handleFocus = (e: FocusEvent) => {
        if (!modalRef.current?.contains(e.target as Node)) {
          lastElement.focus();
        }
      };
      
      firstElement?.focus();
      document.addEventListener('keydown', handleTabKey);
      return () => {
        document.removeEventListener('keydown', handleTabKey);
      };
    }
  }, [isOpen]);
};

ARIA roles and attributes plan

Comprehensive ARIA implementation for all interactive elements:

// Mock list item with proper ARIA attributes
const MockListItem = ({ mock, isSelected, onClick }: {
  mock: MockDefinition;
  isSelected: boolean;
  onClick: () => void;
}) => {
  return (
    <li
      role="button"
      tabIndex={0}
      aria-label={`Edit mock ${mock.name}`}
      aria-selected={isSelected}
      onClick={onClick}
      onKeyDown={(e) => e.key === 'Enter' && onClick()}
      className={`mock-item ${isSelected ? 'selected' : ''}`}
    >
      <span aria-hidden="true">→</span>
      <span>{mock.name}</span>
      <span className="mock-status" aria-label={`Mock status: ${mock.status}`}>
        {mock.status}
      </span>
    </li>
  );
};

Screen reader compatibility

All UI elements must be fully described for screen reader users:

// Mock creation form with screen reader accessibility
const MockForm = ({ 
  mock, 
  onSave, 
  onCancel 
}: { 
  mock: MockDefinition; 
  onSave: () => void; 
  onCancel: () => void; 
}) => {
  return (
    <form 
      aria-labelledby="mock-form-title"
      aria-describedby="mock-form-description"
      onSubmit={(e) => {
        e.preventDefault();
        onSave();
      }}
    >
      <h2 id="mock-form-title" className="sr-only">Create New Mock</h2>
      <p id="mock-form-description" className="sr-only">
        Fill in the details to create a new mock endpoint definition
      </p>
      
      <div>
        <label htmlFor="mock-name">
          Mock Name
        </label>
        <input
          id="mock-name"
          aria-describedby="name-hint"
          type="text"
          value={mock.name}
          onChange={(e) => updateMock({ ...mock, name: e.target.value })}
        />
        <span id="name-hint" className="sr-only">
          Enter a unique name for this mock endpoint
        </span>
      </div>
      
      <div>
        <label htmlFor="mock-path">
          Path
        </label>
        <input
          id="mock-path"
          aria-describedby="path-hint"
          type="text"
          value={mock.path}
          onChange={(e) => updateMock({ ...mock, path: e.target.value })}
        />
      </div>
      
      <div role="region" aria-label="Mock response preview">
        <label htmlFor="mock-response">
          Response Body
        </label>
        <textarea
          id="mock-response"
          aria-describedby="response-hint"
          value={mock.response}
          onChange={(e) => updateMock({ ...mock, response: e.target }}
        />
      </div>
      
      <button type="submit" aria-describedby="save-hint">
        Save Mock
      </button>
      <button type="button" onClick={onCancel} aria-describedby="cancel-hint">
        Cancel
      </button>
    </form>
  );
};

Keyboard accessibility patterns

All interactive elements must support:

  • Tab navigation through all focusable elements
  • Enter key for activating buttons and form submission
  • Escape key for closing modals and dialogs
  • Arrow keys for navigating lists when appropriate
  • Spacebar for toggle elements (when applicable)

Focus indicators

Visible focus rings must be implemented with sufficient contrast:

/* Focus indicator styling */
:focus {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
}

/* Ensure focus styles are visible in all themes */
@media (prefers-contrast: high) {
  :focus {
    outline: 3px solid #000;
  }
}

Semantic HTML structure

All UI components must use appropriate semantic HTML:

// Semantic mock list with proper heading hierarchy
const MockList = ({ mocks }: { mocks: MockDefinition[] }) => {
  return (
    <main>
      <h1>Mock Definitions</h1>
      <section aria-labelledby="mocks-heading">
        <h2 id="mocks-heading">Available Mocks</h2>
        <ul role="list">
          {mocks.map((mock) => (
            <MockListItem key={mock.id} mock={mock} />
          ))}
        </ul>
      </section>
    </main>
  );
};

Dynamic content updates

When mock data changes dynamically, screen readers must be notified:

// Live region for status updates
const StatusUpdater = ({ message }: { message: string }) => {
  return (
    <div 
      aria-live="polite" 
      aria-atomic="true"
      className="sr-only"
    >
      {message}
    </div>
  );
};

Color contrast compliance

All UI elements must meet minimum color contrast ratios:

  • Text: 4.5:1 against background
  • Interactive elements: 3:1 for non-text elements
  • Focus indicators: 3:1 against surrounding elements

Alternative text and labels

All images, icons, and non-text elements must have appropriate alt text or ARIA labels:

// Icon with accessible label
const MockIcon = ({ className }: { className?: string }) => (
  <svg 
    className={className}
    aria-label="Mock endpoint"
    role="img"
  >
    <path d="..." />
  </svg>
);

10. Security Considerations

XSS Prevention Strategy

The mocking framework must prevent cross-site scripting attacks by sanitizing all mock data and configuration inputs before rendering or executing. This includes:

  • Sanitizing mock response bodies, headers, and status codes
  • Escaping template variables in mock definitions
  • Validating and filtering all user-provided mock configurations
  • Using React's built-in XSS protection for DOM rendering

CSRF Protection

When the mocking framework supports HTTP request interception, it must implement CSRF protection:

  • Generate and validate CSRF tokens for mock endpoints
  • Implement same-origin policy enforcement for mock API calls
  • Prevent unauthorized mock configuration changes via CSRF attacks

Content Security Policy (CSP) Headers

The framework must respect and enforce CSP policies:

  • Validate that mock configurations don't violate CSP directives (script-src, object-src)
  • Prevent inline script execution in mock response bodies
  • Ensure mock data doesn't introduce unsafe-inline content

Sensitive Data Handling in Frontend State

Mock configurations may contain sensitive information:

  • Never store authentication tokens, API keys, or secrets in frontend state
  • Mask sensitive fields in UI displays (e.g., show *** instead of actual values)
  • Implement secure storage patterns for mock credentials (encrypted local storage or secure session storage)
  • Clear sensitive data from memory when mocks are disabled

Input Sanitization Approach

All inputs to the mocking framework must be sanitized:

// Example input sanitization for mock configuration
const sanitizeMockConfig = (config: MockConfig): MockConfig => {
  return {
    ...config,
    // Sanitize headers to prevent XSS
    headers: Object.fromEntries(
      Object.entries(config.headers || {}).map(([key, value]) => [
        key,
        sanitizeString(value)
      ])
    ),
    // Validate and sanitize response body
    response: {
      ...config.response,
      body: sanitizeString(config.response?.body || '')
    }
  };
};

const sanitizeString = (str: string): string => {
  return str
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;');
};

Secure Configuration Loading

Mock configurations should be loaded securely:

  • Validate configuration file signatures when loading from disk or network
  • Implement secure parsing of mock definitions (avoid eval/Function constructor)
  • Support configuration encryption for sensitive environments
  • Implement proper file access controls for local mock files

Authentication & Authorization

When the mocking framework supports authentication:

  • Validate that only authorized users can modify mock configurations
  • Implement proper session management for mock UI access
  • Enforce role-based access control for sensitive mock operations
  • Securely store authentication tokens for mock API endpoints

Mock Data Integrity

Ensure that mock responses don't inadvertently expose system information:

  • Validate that mock responses don't leak internal system paths or stack traces
  • Prevent template injection in mock response bodies
  • Sanitize all dynamic content in mock configurations
  • Implement proper error handling that doesn't expose internal state

11. Internationalization (i18n)

Translation Key Structure and Naming Convention

Following a hierarchical key structure with clear semantic grouping:

// Translation keys organized by feature and context
{
  "common": {
    "buttons": {
      "save": "Save",
      "cancel": "Cancel"
    },
    "labels": {
      "name": "Name",
      "email": "Email"
    }
  },
  "dashboard": {
    "welcome": "Welcome to your dashboard",
    "stats": {
      "revenue": "Revenue: {{amount}}",
      "users": "Active Users: {{count}}"
    }
  },
  "modals": {
    "confirmation": {
      "title": "Confirm Action",
      "message": "Are you sure you want to proceed?",
      "confirm": "Yes, Confirm",
      "cancel": "No, Cancel"
    }
  }
}

RTL Support Considerations

RTL layout support implemented through CSS classes and direction properties:

/* CSS for RTL support */
[dir="rtl"] .container {
  text-align: right;
  direction: rtl;
}

[dir="rtl"] .button-group {
  flex-direction: row-reverse;
}

Date, Number, Currency Formatting Strategy

Using Intl API for locale-aware formatting:

// Format utilities with fallbacks
const formatNumber = (value: number, locale: string = 'en-US') => {
  return new Intl.NumberFormat(locale, {
    style: 'decimal',
    minimumFractionDigits: 0,
    maximumFractionDigits: 2
  }).format(value);
};

const formatCurrency = (value: number, currency: string, locale: string = 'en-US') => {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency,
    minimumFractionDigits: 2
  }).format(value);
};

const formatDate = (date: Date, locale: string = 'en-US') => {
  return new Intl.DateTimeFormat(locale, {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }).format(date);
};

Locale Detection and Switching Strategy

Automatic browser locale detection with manual override capability:

// Locale detection logic
const detectLocale = (): string => {
  const storedLocale = localStorage.getItem('preferred-locale');
  if (storedLocale) return storedLocale;
  
  const browserLocale = navigator.language || 'en-US';
  const supportedLocales = ['en-US', 'fr-FR', 'es-ES', 'ar-SA'];
  
  // Fallback to English if unsupported
  return supportedLocales.includes(browserLocale) 
    ? browserLocale 
    : 'en-US';
};

// Locale switching with persistence
const switchLocale = (newLocale: string) => {
  localStorage.setItem('preferred-locale', newLocale);
  document.documentElement.lang = newLocale;
  // Trigger re-render or refresh translation context
};

Translation Key Management

Translation files organized by locale with fallback mechanism:

// Translation file structure (en-US.json)
{
  "app": {
    "title": "Mocking Framework",
    "description": "A powerful tool for API mocking and testing"
  },
  "components": {
    "modal": {
      "close": "Close",
      "confirm": "Confirm"
    }
  },
  "pages": {
    "home": {
      "welcome": "Welcome to the Mocking Framework",
      "setup": "Get Started with Setup"
    }
  }
}

// Fallback translation loader
const loadTranslation = async (locale: string): Promise<Record<string, any>> => {
  try {
    const response = await fetch(`/locales/${locale}.json`);
    return await response.json();
  } catch (error) {
    // Fallback to English
    console.warn(`Failed to load translations for ${locale}, falling back to en-US`);
    return await fetch('/locales/en-US.json').then(r => r.json());
  }
};

i18n Context Provider Implementation

// i18n context with type safety
interface I18nContextType {
  locale: string;
  translations: Record<string, any>;
  formatMessage: (key: string, params?: Record<string, any>) => string;
  switchLocale: (locale: string) => void;
}

const I18nContext = React.createContext<I18nContextType>({
  locale: 'en-US',
  translations: {},
  formatMessage: () => '',
  switchLocale: () => {}
});

// Translation provider component
const I18nProvider: React.FC<{ 
  initialLocale?: string; 
  children: React.ReactNode 
}> = ({ initialLocale = 'en-US', children }) => {
  const [locale, setLocale] = useState<string>(initialLocale);
  const [translations, setTranslations] = useState<Record<string, any>>({});

  useEffect(() => {
    loadTranslation(locale).then(setTranslations);
  }, [locale]);

  const formatMessage = useCallback((key: string, params?: Record<string, any>): string => {
    const message = getNestedProperty(translations, key);
    if (!message) return key;
    
    if (params) {
      return message.replace(/\{\{(\w+)\}\}/g, (_, param) => 
        params[param] ?? `{{${param}}}`
      );
    }
    return message;
  }, [translations]);

  const switchLocale = useCallback((newLocale: string) => {
    setLocale(newLocale);
  }, []);

  return (
    <I18nContext.Provider value={{
      locale,
      translations,
      formatMessage,
      switchLocale
    }}>
      {children}
    </I18nContext.Provider>
  );
};

i18n Hook Implementation

// Custom hook for translation access
const useTranslation = () => {
  const context = useContext(I18nContext);
  
  if (!context) {
    throw new Error('useTranslation must be used within an I18nProvider');
  }
  
  return {
    t: context.formatMessage,
    locale: context.locale,
    switchLocale: context.switchLocale
  };
};

// Usage example in component
const WelcomeMessage: React.FC = () => {
  const { t, locale } = useTranslation();
  
  return (
    <div lang={locale}>
      <h1>{t('pages.home.welcome')}</h1>
      <p>{t('pages.home.setup', { count: 5 })}</p>
    </div>
  );
};

i18n Testing Strategy

Test coverage includes:

  • Translation key existence validation
  • RTL layout rendering verification
  • Number/date formatting consistency across locales
  • Fallback behavior testing for missing translations

i18n Performance Considerations

  • Translation files loaded on-demand per locale
  • Caching of parsed translation objects to avoid re-parsing
  • Bundle size optimized through dynamic imports for locale files

12. Monitoring & Observability

Real User Monitoring (RUM) Metrics to Track

The mocking framework should provide comprehensive RUM capabilities to understand real-world usage patterns and performance characteristics:

  • Mock Request Latency: Track average, 95th percentile, and 99th percentile response times for mocked endpoints
  • Mock Hit Rate: Monitor percentage of actual requests that were intercepted and mocked vs. forwarded to real APIs
  • Mock Coverage: Measure how much of the application's API surface is being mocked (e.g., 85% of GET /api/users)
  • Error Rate: Track percentage of failed mock responses (5XX, 404, etc.) and their distribution
  • Memory Usage: Monitor memory consumption of the mock state store and request tracking
  • Network Efficiency: Measure reduction in actual API calls when mocking is active

Error Tracking Integration

The framework should integrate with error tracking platforms to capture and report mock-related issues:

  • Mock Configuration Errors: Invalid route patterns, missing response bodies, malformed headers
  • Runtime Mock Failures: When mock interceptors fail to match or process requests properly
  • State Management Issues: Memory leaks in mock store, race conditions in concurrent mock handling
  • Integration Failures: When mock framework fails to properly intercept or restore network calls

Custom Events and User Journey Tracking

Implement custom event tracking for key mock interactions:

// Event tracking interface
interface MockEvent {
  type: 'mock_intercepted' | 'mock_missed' | 'mock_disabled' | 'mock_enabled';
  timestamp: number;
  payload?: {
    url: string;
    method: string;
    mockId?: string;
    duration?: number;
  };
}

// Event tracking service
class MockEventTracker {
  private events: MockEvent[] = [];
  
  track(event: MockEvent) {
    this.events.push(event);
    // Send to monitoring service (Sentry, DataDog, etc.)
    this.sendToMonitoringService(event);
  }
  
  getEvents() {
    return this.events;
  }
}

Performance Monitoring and Regression Detection

Implement performance monitoring to detect regressions in mock handling:

  • Request Processing Time: Measure time from request interception to response generation
  • Memory Allocation Patterns: Track mock state growth over time to detect memory leaks
  • Concurrent Request Handling: Monitor performance under high concurrent load scenarios
  • Mock State Persistence: Track time to load and restore mock configurations from storage

Implementation Strategy

The monitoring system should be designed with minimal performance overhead:

// Performance monitoring hook for mock framework
const useMockPerformance = () => {
  const [metrics, setMetrics] = useState<MockMetrics>({
    totalRequests: 0,
    mockHits: 0,
    avgResponseTime: 0,
    memoryUsage: 0
  });

  const recordRequest = (duration: number, wasMocked: boolean) => {
    setMetrics(prev => ({
      ...prev,
      totalRequests: prev.totalRequests + 1,
      mockHits: wasMocked ? prev.mockHits + 1 : prev.mockHits,
      avgResponseTime: (prev.avgResponseTime * prev.totalRequests + duration) / 
                      (prev.totalRequests + 1)
    }));
  };

  return { metrics, recordRequest };
};

interface MockMetrics {
  totalRequests: number;
  mockHits: number;
  avgResponseTime: number;
  memoryUsage: number;
}

Observability Dashboard Considerations

The framework should support integration with observability dashboards through:

  • Exportable Metrics: JSON format for ingestion into Grafana, Prometheus, or similar systems
  • Real-time Metrics Streaming: WebSocket or polling-based real-time dashboard updates
  • Custom Metric Labels: Support for tagging metrics with environment, user context, or feature flags
  • Alerting Configuration: Built-in support for defining alert thresholds (e.g., mock hit rate drops below 80%)

Data Privacy and Security

Monitoring should respect user privacy:

  • Anonymized Metrics: Remove PII from captured metrics before sending to monitoring systems
  • Opt-in/Opt-out Support: Allow users to disable or configure monitoring behavior
  • Secure Transport: All monitoring data should be sent over HTTPS with proper authentication
  • Compliance Considerations: Ensure monitoring doesn't violate data protection regulations like GDPR or CCPA

Integration Points

The observability layer should integrate with:

  • Sentry: For error tracking and performance monitoring
  • Datadog: For comprehensive APM and infrastructure monitoring
  • New Relic: For application performance insights
  • Custom Logging Systems: Support for enterprise logging solutions

This monitoring approach ensures that the mocking framework not only provides value in testing but also enables teams to understand and optimize their mock usage patterns in production-like environments.

13. File & Folder Structure

Proposed Directory Structure

/src
  /mocking-framework
    /core
      index.ts
      MockManager.ts
      MockRegistry.ts
      MockConfig.ts
    /types
      index.ts
      types.ts
    /utils
      index.ts
      matcher.ts
      serializer.ts
      validator.ts
    /api
      index.ts
      mock-api.ts
      request-handler.ts
    /fixtures
      index.ts
      default-fixtures.ts
    /config
      index.ts
      config-loader.ts
    /decorators
      index.ts
      mock-decorator.ts
      stub-decorator.ts
    /middleware
      index.ts
      logging-middleware.ts
      timing-middleware.ts
    /reporting
      index.ts
      reporter.ts
      console-reporter.ts
    /test
      mock-manager.test.ts
      matcher.test.ts
      serializer.test.ts
  /shared
    /hooks
      useMockingContext.ts
    /utils
      index.ts
      logger.ts
      error-handler.ts
  /app
    /components
      MockDashboard.tsx
      MockEditor.tsx
      RequestLog.tsx
    /pages
      MockingDashboardPage.tsx
      MockEditorPage.tsx
  /types
    index.ts
    global.d.ts

Module Boundaries and Import Rules

  • Core modules (/core, /types) are pure logic modules with no side effects and can be imported anywhere
  • API modules (/api) handle HTTP request/response interception and should only be imported by the main mocking engine
  • Test modules (/test) are unit test files that should not be part of the production build
  • Shared modules (/shared) contain cross-cutting concerns like hooks and utilities that can be imported by components or other modules
  • Feature-specific modules (/app) contain UI components and pages that consume the mocking framework

Import Strategy

  • No cross-feature imports: Each feature module should only import from its own submodules or shared modules
  • Barrel exports: Use barrel files (index.ts) for clean public APIs at each level
  • Type-only imports: Use import type for types to avoid runtime dependencies

Shared vs Feature-Specific Code

Shared code includes:

  • Common utility functions (logger, error-handler)
  • Type definitions (types.ts)
  • Hook implementations (useMockingContext.ts)

Feature-specific code includes:

  • Component UI (MockDashboard.tsx)
  • Page components (MockingDashboardPage.tsx)
  • Mock configuration logic
  • API interception logic

This structure enables clear separation of concerns, allows for easy testing and mocking, and supports scalable growth of the framework while maintaining clean module boundaries.

14. Dependencies & Third-Party Libraries

Key Libraries Selected with Justification

1. MSW (Mock Service Worker)

  • Justification: Industry standard for API mocking in development and testing. Provides realistic network interception without requiring changes to application code. Supports both browser and Node.js environments.
  • Bundle Impact: ~20KB gzipped
  • Alternatives Considered:
    • nock - Only works in Node.js, not suitable for browser mocking
    • mock-axios - Too specific to axios, limited flexibility

2. React Testing Library

  • Justification: Focuses on testing component behavior rather than implementation details, aligns with testing strategy for mocking framework. Provides excellent DOM querying and user event simulation.
  • Bundle Impact: ~5KB gzipped
  • Alternatives Considered:
    • Enzyme - Complex API, less maintainable, deprecated in favor of RTL

3. Jest

  • Justification: Industry standard for JavaScript testing with built-in mocking capabilities, excellent TypeScript support, and comprehensive test coverage. Integrates well with React Testing Library.
  • Bundle Impact: ~15KB gzipped (test runtime)
  • Alternatives Considered:
    • Vitest - Faster but less mature ecosystem for complex mocking scenarios

4. TypeScript

  • Justification: Essential for type safety in a mocking framework where precise typing of mocks, handlers, and expectations is critical. Enables better developer experience with autocomplete and compile-time checks.
  • Bundle Impact: None (compile-time only)
  • Alternatives Considered:
    • Pure JavaScript - Would lose type safety and developer experience benefits

5. React Hook Form

  • Justification: Provides robust form handling and validation capabilities that are essential for mocking framework UI components. Offers excellent integration with TypeScript and supports complex form structures.
  • Bundle Impact: ~15KB gzipped
  • Alternatives Considered:
    • Custom form handling - Would require significant development effort and maintenance overhead

Alternatives Considered and Rejected

1. Fetch Mock

  • Rejected Because: Limited to Node.js environments, lacks browser support, and doesn't provide the same level of network interception capabilities as MSW.

2. Sinon.js

  • Rejected Because: Primarily focused on stubbing and mocking individual functions rather than network-level mocking. Not optimized for API mocking in frontend applications.

3. Mocky.io

  • Rejected Because: External service dependency that introduces network latency and reliability issues. Not suitable for local development environments.

4. Supertest

  • Rejected Because: Node.js-only HTTP testing library, not appropriate for frontend mocking frameworks.

Licensing and Bundle Size Impact Assessment

Bundle Size Budget Allocation:

  • MSW: 20KB (15% of total)
  • React Testing Library: 5KB (4% of total)
  • Jest: 15KB (12% of total)
  • TypeScript: 0KB (compile-time only)
  • React Hook Form: 15KB (12% of total)

Total Bundle Size Impact: ~55KB gzipped for core mocking framework dependencies

Licensing Compliance:

  • All selected libraries are MIT licensed with no restrictions on commercial usage
  • MSW and React Testing Library have strong community support and active maintenance
  • Jest has enterprise support options but is free for open-source use

Security Considerations:

  • All dependencies are actively maintained with security vulnerability monitoring
  • Regular dependency updates integrated into CI pipeline to prevent security risks
  • No known vulnerabilities in current version selections

Performance Impact Analysis

Build Time Impact:

  • MSW adds ~200ms to build process due to service worker compilation
  • Jest adds minimal overhead to test execution
  • React Hook Form has negligible impact on runtime performance

Runtime Performance:

  • MSW's service worker operates in background thread, no impact on main UI thread
  • React Testing Library's DOM queries are optimized for performance
  • Jest's test execution is highly optimized with built-in caching mechanisms

Memory Usage:

  • MSW maintains minimal memory footprint in browser environment
  • Jest's test runner uses efficient memory management for large test suites
  • React Hook Form's form state management is optimized for performance

15. Open Questions, Risks & Trade-offs

Decisions that need product/engineering alignment

  • Mock scope granularity: Should mocks be defined at the endpoint level, or should we support more granular control (e.g., by HTTP method, query parameters, headers)? This decision impacts the complexity of the configuration API and developer experience.

  • Mock lifecycle management: How should mocks be activated/deactivated? Should they be enabled via environment variables, UI toggle, or configuration file? This affects the operational model and developer workflow.

  • Conflict resolution strategy: When multiple mocks match a request, how should conflicts be resolved? Should we support priority-based matching or throw an error to force explicit configuration?

Known trade-offs made in this design

  • Configuration complexity vs. flexibility: Prioritized flexible mock definition over simple configuration by supporting complex matching rules (headers, query params, body patterns). This increases cognitive load but provides more powerful mocking capabilities.

  • Runtime performance vs. feature richness: Trade-off between runtime overhead of complex matching logic and the need to support rich mock patterns. We've chosen to optimize for feature richness with efficient matching algorithms.

  • Developer experience vs. system complexity: Prioritized developer-friendly mock definition syntax over system-level complexity by abstracting away low-level HTTP details, but this introduces additional layers of abstraction.

Risks and mitigations

  • Risk: Performance degradation in development builds: Complex matching logic could slow down request handling during development.

    • Mitigation: Implement efficient matching algorithms with early exit conditions and caching of frequently matched patterns.
  • Risk: Configuration drift between environments: Mocks defined in development might not behave consistently in staging/production.

    • Mitigation: Enforce strict environment-based configuration rules and provide clear warnings when mocks are active in non-development environments.
  • Risk: Security vulnerabilities from mock configuration: Malicious or incorrect mock definitions could expose sensitive data.

    • Mitigation: Implement strict validation of mock configuration and provide security guidelines for mock definition.

Technical debt acknowledged

  • Mock configuration validation: The current implementation lacks comprehensive validation of mock definitions, which could lead to runtime errors or silent failures. Future iterations should include robust schema validation.

  • Mock pattern matching efficiency: The current regex-based matching approach may not scale well with large numbers of mock patterns. Consider implementing a trie-based structure for better performance.

  • Type safety gaps: While TypeScript is used, there are still areas where runtime type checking could be strengthened to prevent configuration errors that only surface at runtime.

  • Documentation coverage: The framework's API is not fully documented with examples, which could make adoption more difficult for new users. This represents technical debt in developer onboarding and maintenance.

  • Cross-platform compatibility: The framework assumes a Node.js-like environment but may not work seamlessly in browser environments without additional configuration or polyfills.

Was this article helpful?

Topics in this article

More from Frontend LLD

Stay in the Loop

Get curated DSA patterns, coding problems, frontend and backend LLD/HLD, and AI/ML System Design insights delivered straight to your inbox. No spam, just focused interview and engineering content.