Frontend System Design (HLD) Framework for Production Projects
A standardized 7-step architectural blueprint to systematically structure, articulate, and defend any complex frontend production system in Senior/Lead/Staff technical interviews.
Why it matters in interviews
Visual & Interactive Explanation
1. Edge CDN
Cloudflare/CloudFront: Brotli, HTTP/3, Edge SSR, Asset Caching
2. App Shell
Next.js App Router / Micro-Frontend Host, Auth Guards, Code Splitting
3. UI Components
Page Container -> Feature Widgets -> Design System Primitives
4. State & Cache
URL SearchParams + Server Cache (TanStack Query) + Local Store
5. API Layer
BFF Client, Retry Backoff, Request Deduplication, WebSocket / SSE
6. Offline Store
IndexedDB (Drafts/Offline Queue) + LocalStorage (Preferences)
7. Observability
CSP Headers, HttpOnly Cookies, OpenTelemetry, Sentry, Web Vitals RUM
Code Examples & Implementation
/**
* PRODUCTION FRONTEND ARCHITECTURE BLUEPRINT
*
* 1. MODULE BOUNDARIES:
* - Shell / Host: Authentication, Navigation, Dynamic Theming, Notification Toast Hub
* - Feature Pods: Independent domain packages (@app/analytics, @app/inventory, @app/checkout)
* - Shared UI Core: Headless design system primitives, formatting utilities, API clients
*
* 2. COMPONENT HIERARCHY:
* <AppShell>
* <GlobalErrorBoundary>
* <QueryClientProvider>
* <HeaderNavigation />
* <Suspense fallback={<PageSkeleton />}>
* <FeatureContainer>
* <WidgetGrid>
* <WidgetBoundary name="ChartWidget">
* <VirtualizedDataGrid />
* </WidgetBoundary>
* </WidgetGrid>
* </FeatureContainer>
* </Suspense>
* </QueryClientProvider>
* </GlobalErrorBoundary>
* </AppShell>
*
* 3. STATE TAXONOMY:
* - Server Cache: TanStack Query (staleTime: 60s, cacheTime: 5m, retry: 3)
* - Navigation State: URL query params (?filter=active&page=2) (Single source of truth)
* - Ephemeral UI State: Local useState / useReducer (Modal open, hover tooltip)
* - Offline Store: IndexedDB via idb-keyval (Draft form mutations)
*/import { TaskScheduler } from "./scheduler";
export class ResilientApiClient {
private inFlightRequests = new Map<string, Promise<any>>();
private scheduler = new TaskScheduler(6); // Max 6 concurrent HTTP/2 streams
async request<T>(url: string, options: RequestInit = {}): Promise<T> {
const method = options.method || "GET";
const cacheKey = `${method}:${url}:${JSON.stringify(options.body || {})}`;
// 1. Request Deduplication for concurrent GETs
if (method === "GET" && this.inFlightRequests.has(cacheKey)) {
return this.inFlightRequests.get(cacheKey)!;
}
const promise = this.scheduler.add(async () => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10s deadline
try {
const response = await fetch(url, { ...options, signal: controller.signal });
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} finally {
this.inFlightRequests.delete(cacheKey);
}
});
if (method === "GET") {
this.inFlightRequests.set(cacheKey, promise);
}
return promise;
}
}Interview-Ready Answers
Structure any frontend HLD into 7 layers: Scope/Scale, Module Boundaries, Component Hierarchy, API Contracts, Multi-Tier Caching, Performance Optimizations, and NFRs (Availability, Latency, Observability).
Follow-up Questions & Deep Dives
🎯 Code Output — What Does This Print?
// What is the primary advantage of navigator.sendBeacon over fetch?
// A: It supports HTTP PUT and DELETE
// B: It is guaranteed to send even if the user closes the tab immediately
// C: It bypasses CORS preflight checks for all requests
// D: It runs in a separate Web Worker threadCommon Mistakes & Anti-Patterns
- Dumping all API responses into global Redux store without cache invalidation or TTL policies
- Not mentioning localized Error Boundaries for widget isolation
- Ignoring mobile networks (relying on high-bandwidth desktop Wi-Fi assumptions)
- Failing to articulate clear Non-Functional Requirements (NFRs) like INP, LCP, and availability metrics
Implementation Evolution: Anti-Pattern to Production
❌ Anti-Pattern: Monolithic Global State & Missing Fault Isolation
// ❌ Anti-Pattern: Putting ALL server data into a single Redux store without error boundaries
function BigDashboard() {
// If ANY widget fails to load data or throws during render, the ENTIRE dashboard crashes!
return (
<div>
<WidgetA />
<WidgetB />
<WidgetC />
</div>
);
}✅ Correct Pattern: Granular Widget Boundaries & Server-State Segregation
// ✅ Correct: Isolated Error Boundaries with retry fallbacks and independent query hooks
function ResilientDashboard() {
return (
<div className="grid grid-cols-3 gap-4">
<WidgetBoundary fallbackTitle="Sales Analytics">
<Suspense fallback={<WidgetSkeleton />}>
<SalesWidget />
</Suspense>
</WidgetBoundary>
<WidgetBoundary fallbackTitle="Inventory Health">
<Suspense fallback={<WidgetSkeleton />}>
<InventoryWidget />
</Suspense>
</WidgetBoundary>
</div>
);
}🚀 Production-Grade (Hardened): Real User Monitoring (RUM) & Performance Telemetry Beacon
// 🚀 Production: Automated Core Web Vitals & Error Telemetry Pipeline
export function initializeFrontendTelemetry() {
// 1. Report unhandled Promise rejections and runtime errors
window.addEventListener("error", (event) => {
reportTelemetry("error", {
message: event.message,
stack: event.error?.stack,
url: window.location.href,
timestamp: Date.now(),
});
});
window.addEventListener("unhandledrejection", (event) => {
reportTelemetry("unhandled_rejection", {
reason: String(event.reason),
timestamp: Date.now(),
});
});
}
function reportTelemetry(type: string, payload: Record<string, any>) {
const body = JSON.stringify({ type, payload });
if (navigator.sendBeacon) {
navigator.sendBeacon("/api/telemetry", body);
} else {
fetch("/api/telemetry", { method: "POST", body, keepalive: true }).catch(() => {});
}
}⚠️ Trick Questions & Interviewer Traps
Q1:How do you guarantee 99.99% frontend availability if backend microservices experience intermittent 500 errors?
Q2:Why is `navigator.sendBeacon` preferred over `fetch()` for error/analytics logging?
📋 Rapid Revision Cheat Sheet
- Step 1: Scale & Constraints (DAU, mobile vs desktop, bandwidth limits).
- Step 2: Module Boundaries (BFF, Micro-Frontends vs Modular Monorepo).
- Step 3: Component Tree (Smart Containers -> Dumb Pure Primitives).
- Step 4: State Taxonomy (Server Cache vs URL State vs Local Ephemeral).
- Step 5: Multi-Tier Cache (Edge CDN -> Service Worker -> TanStack Query).
- Step 6: Performance (Code Splitting, 2D Virtualization, Brotli, AVIF).
- Step 7: NFRs (LCP < 1.8s, INP < 100ms, 99.99% Availability, sendBeacon RUM).
Real-World Architectural Scenario
The interviewer asks: 'Design the high-level frontend architecture for our high-throughput enterprise dashboard with 50 live widgets.' How do you answer in 5 minutes?
Rate Your Readiness
Rate how comfortably you can explain this in an interview to update your global readiness gauge: