Frontend System Design Trade-offs: A Comprehensive Guide
Table of Contentsβ
- Introduction
- Rendering Strategies
- State Management
- Data Fetching
- Performance Optimization
- Component Architecture
- Styling Approaches
- Routing Architecture
- Micro-Frontend Architecture
- Security Considerations
- Accessibility
- Observability
- Testing Strategy
- Deployment Strategies
- Mobile Considerations
- Interview Framework
Introductionβ
This guide provides a comprehensive overview of frontend system design trade-offs for technical interviews at companies like Google, Amazon, Microsoft, and other tech giants. Each section explores real-world decisions, their implications, and when to apply them.
Key Principle: Every architectural decision is a trade-off. There are no universally "best" solutionsβonly appropriate choices for specific contexts.
Rendering Strategiesβ
CSR vs SSR vs SSG vs ISRβ
| Approach | Pros | Cons | Best Use Cases | Example |
|---|---|---|---|---|
| CSR (Client-Side Rendering) | β’ Simple deployment β’ Low server costs β’ Rich interactivity β’ Easy state management | β’ Slow First Contentful Paint β’ Poor SEO (without workarounds) β’ Blank screen on slow networks β’ Higher client device load | β’ Admin dashboards β’ Internal tools β’ Apps behind auth β’ Highly interactive SPAs | Gmail, Notion |
| SSR (Server-Side Rendering) | β’ Fast First Contentful Paint β’ Excellent SEO β’ Works without JS β’ Fresh data on every request | β’ Higher server costs β’ TTFB dependency β’ More complex deployment β’ Hydration overhead | β’ E-commerce product pages β’ News sites β’ Social media feeds β’ Content-heavy apps | Amazon, Twitter |
| SSG (Static Site Generation) | β’ Fastest possible loads β’ CDN-friendly β’ Lowest hosting cost β’ Best SEO | β’ Build time scales poorly β’ Data can be stale β’ Not suitable for personalized content | β’ Blogs β’ Documentation β’ Marketing sites β’ Landing pages | Vercel docs, Gatsby sites |
| ISR (Incremental Static Regeneration) | β’ Balance of fresh + fast β’ On-demand regeneration β’ CDN benefits β’ Scales better than SSG | β’ More complex mental model β’ Stale-while-revalidate behavior β’ Platform-specific (Next.js) | β’ Product catalogs β’ SEO landing pages β’ News sites with updates | E-commerce listings |
Decision Framework:
Is data user-specific?
ββ Yes β CSR or SSR
β ββ Need SEO?
β ββ Yes β SSR
β ββ No β CSR
ββ No β SSG or ISR
ββ Updates frequently?
ββ Yes β ISR
ββ No β SSG
Hydration Strategiesβ
| Strategy | Description | Trade-off | Use When |
|---|---|---|---|
| Full Hydration | Hydrate entire page at once | Simple but slow on large pages | Small to medium apps |
| Progressive Hydration | Hydrate components as needed | Complex but faster TTI | Large apps with clear priority |
| Partial Hydration (Islands Architecture) | Only hydrate interactive parts | Best performance but requires careful planning | Mixed static/dynamic content |
| Resumability (Qwik) | Zero hydration, resume from HTML | Fastest but new paradigm | Performance-critical apps |
Key Insight: Hydration is pure overhead. The less you hydrate, the faster your app, but the more complex your architecture.
Streaming SSRβ
Pros:
- Progressive rendering (faster perceived performance)
- Doesn't block on slow data fetching
- Better TTFB for complex pages
Cons:
- More complex error handling
- Requires React 18+ or similar framework support
- Can't set HTTP headers after streaming starts
Use when: You have pages with mixed fast/slow data sources (e.g., user profile with slow recommendation feed).
State Managementβ
Local vs Global Stateβ
| Approach | Pros | Cons | Best For |
|---|---|---|---|
| Local State (useState, useReducer) | β’ Simple mental model β’ No external dependencies β’ Predictable performance β’ Easy to test | β’ Hard to share between distant components β’ Prop drilling β’ Duplication across components | β’ UI state (modals, forms) β’ Component-specific logic β’ Temporary data |
| Context API | β’ Built into React β’ No extra library β’ Good for theme/i18n | β’ Re-renders all consumers β’ Poor performance at scale β’ Not designed for frequent updates | β’ Global config β’ Theme/locale β’ User authentication status |
| Redux | β’ Predictable state changes β’ Time-travel debugging β’ Middleware ecosystem β’ DevTools | β’ Boilerplate heavy β’ Learning curve β’ Over-engineering for simple apps | β’ Complex business logic β’ Apps with many features β’ Need for undo/redo |
| Zustand | β’ Minimal boilerplate β’ Great DX β’ Flexible | β’ Less mature ecosystem β’ Fewer DevTools | β’ Medium apps β’ When Redux feels heavy |
| Jotai/Recoil | β’ Atomic state β’ Minimal re-renders β’ Derived state | β’ Different paradigm β’ Smaller community | β’ Performance-critical apps β’ Complex derived state |
Server State Managementβ
| Library | Pros | Cons | Use When |
|---|---|---|---|
| React Query / TanStack Query | β’ Caching built-in β’ Automatic refetching β’ Optimistic updates β’ DevTools | β’ Learning curve β’ Additional dependency | Default choice for server state |
| SWR | β’ Simpler API β’ Stale-while-revalidate β’ Smaller bundle | β’ Fewer features than React Query | Simpler apps, Vercel ecosystem |
| Apollo Client (GraphQL) | β’ GraphQL integration β’ Normalized cache β’ Real-time subscriptions | β’ Heavy bundle β’ GraphQL-only β’ Complex cache | GraphQL apps with complex data |
| RTK Query | β’ Redux integration β’ Code generation β’ Cache invalidation | β’ Redux required β’ More setup | Already using Redux |
Golden Rule:
- UI state β Local state
- Server state β React Query/SWR
- Cross-feature business logic β Redux/Zustand
State Synchronizationβ
| Pattern | Description | Trade-off |
|---|---|---|
| Optimistic Updates | Update UI before server confirms | Fast UX but requires rollback logic |
| Pessimistic Updates | Wait for server before updating UI | Slower UX but simpler logic |
| Event Sourcing | Store state changes as events | Complete history but complex |
| CQRS | Separate read/write models | Scales well but more infrastructure |
Data Fetchingβ
API Architectural Patternsβ
| Pattern | Pros | Cons | Best For |
|---|---|---|---|
| REST | β’ Simple, well-understood β’ HTTP caching friendly β’ Stateless β’ Wide tooling support | β’ Over-fetching (get more data than needed) β’ Under-fetching (multiple requests needed) β’ Versioning challenges | β’ Public APIs β’ Simple CRUD operations β’ When HTTP caching is critical |
| GraphQL | β’ Request exactly what you need β’ Single endpoint β’ Strong typing β’ Real-time subscriptions | β’ Complex caching β’ Learning curve β’ Heavier client bundle β’ Potential N+1 queries | β’ Complex, nested data β’ Mobile apps (reduce requests) β’ Rapidly changing requirements |
| tRPC | β’ End-to-end type safety β’ No code generation β’ Simple setup | β’ Backend lock-in (TypeScript/Node) β’ Smaller ecosystem β’ Not suitable for public APIs | β’ Full-stack TypeScript apps β’ Internal tools β’ Monorepos |
| gRPC | β’ Extremely fast (binary) β’ Built-in streaming β’ Strong contracts | β’ Browser support requires proxy β’ Debugging harder β’ Overkill for simple apps | β’ Microservices β’ High-performance needs β’ Server-to-server |
Interview Insight: REST's advantage is HTTP caching infrastructure. GraphQL trades that for precision but requires custom caching solutions.
Caching Strategiesβ
| Layer | Strategy | Pros | Cons |
|---|---|---|---|
| Browser Cache | HTTP headers (Cache-Control, ETag) | β’ Free β’ Automatic β’ Reduces server load | β’ Hard to invalidate β’ User-specific data risks β’ Storage limits |
| CDN Cache | Edge caching (Cloudflare, Fastly) | β’ Massive scale β’ Geographic distribution β’ DDoS protection | β’ Stale data β’ Purge delays β’ Cost at scale |
| In-Memory Cache | React Query, SWR, Redux cache | β’ Instant access β’ Full control | β’ Memory leaks if not managed β’ Lost on refresh |
| Service Worker | Progressive Web App cache | β’ Offline support β’ Full control β’ Background sync | β’ Complexity β’ Debugging harder β’ Version management |
| IndexedDB | Client-side database | β’ Large storage β’ Structured queries | β’ Async API complexity β’ Browser differences |
Cache Invalidation Strategies:
- Time-based (TTL)
- Event-based (on mutation)
- Stale-while-revalidate
- Cache-aside pattern
Real-time Dataβ
| Technology | Pros | Cons | Use When |
|---|---|---|---|
| WebSocket | β’ True bidirectional β’ Low latency β’ Persistent connection | β’ More complex infrastructure β’ Scaling challenges β’ Connection management | β’ Chat apps β’ Live dashboards β’ Multiplayer games |
| Server-Sent Events (SSE) | β’ Simpler than WebSocket β’ Auto-reconnect β’ HTTP/2 friendly | β’ One-way only β’ Limited browser support | β’ Live feeds β’ Notifications β’ Stock tickers |
| Long Polling | β’ Works everywhere β’ Simple | β’ Inefficient β’ Higher latency | β’ Legacy browser support β’ Fallback option |
| GraphQL Subscriptions | β’ Integrated with queries β’ Type-safe | β’ Requires GraphQL β’ WebSocket dependency | β’ GraphQL apps needing real-time |
Performance Optimizationβ
Bundle Optimizationβ
| Technique | Impact | Trade-off | Implementation |
|---|---|---|---|
| Code Splitting | Reduce initial bundle by 40-70% | More network requests, complexity | Dynamic imports, route-based splitting |
| Tree Shaking | Remove unused code (10-30% reduction) | Requires ES modules, build tool config | ESM exports, sideEffects: false |
| Minification | 30-50% size reduction | Build time increase | Terser, esbuild |
| Compression (Gzip/Brotli) | 60-80% size reduction | Server CPU usage | Nginx/CDN configuration |
| Dynamic Imports | Load code on demand | Waterfalls if not careful | React.lazy(), Next.js dynamic() |
| Bundle Analysis | Identify large dependencies | Time investment | webpack-bundle-analyzer |
Bundle Budget Recommendations:
- Initial JS: < 200KB (gzipped)
- Total page weight: < 1MB
- Time to Interactive: < 3.5s (3G)
Runtime Performanceβ
| Optimization | Benefit | Cost | When to Apply |
|---|---|---|---|
| Memoization (useMemo, memo) | Prevent expensive recalculations | Memory overhead, mental complexity | Expensive computations only |
| useCallback | Prevent prop changes | Negligible | Passing callbacks to memoized components |
| Virtualization (react-window) | Render only visible items | Implementation complexity | Lists with 100+ items |
| Debouncing/Throttling | Reduce function calls | Delayed response | Search inputs, scroll handlers |
| Web Workers | Offload heavy computation | Communication overhead | Image processing, data parsing |
| Concurrent Features (React 18) | Non-blocking rendering | Requires React 18+ | Complex UIs with heavy updates |
Performance Anti-patterns:
- Premature optimization (optimize what matters)
- Over-memoization (everything wrapped in useMemo)
- Inline object/array creation in render
- Anonymous functions in JSX (when causing issues)
Network Performanceβ
| Strategy | Impact | Implementation |
|---|---|---|
| Resource Hints (prefetch, preload, dns-prefetch) | Reduce perceived latency | <link rel="prefetch"> |
| HTTP/2 & HTTP/3 | Multiplexing, reduced latency | Server/CDN configuration |
| Image Optimization (WebP, AVIF, lazy loading) | 50-80% size reduction | Picture element, loading="lazy" |
| Request Batching | Reduce waterfall | GraphQL, custom batch endpoint |
| Service Worker Caching | Offline support, instant loads | SW registration, cache strategies |
Rendering Performanceβ
Critical Rendering Path Optimization:
| Metric | Target | Strategy |
|---|---|---|
| FCP (First Contentful Paint) | < 1.8s | Inline critical CSS, reduce blocking resources |
| LCP (Largest Contentful Paint) | < 2.5s | Optimize images, preload key resources |
| TTI (Time to Interactive) | < 3.8s | Code splitting, reduce JavaScript |
| CLS (Cumulative Layout Shift) | < 0.1 | Reserve space, avoid dynamic content |
| FID (First Input Delay) | < 100ms | Reduce long tasks, use web workers |
| INP (Interaction to Next Paint) | < 200ms | Optimize event handlers, debounce |
Component Architectureβ
Component Patternsβ
| Pattern | Description | Pros | Cons | Use Case |
|---|---|---|---|---|
| Presentational Components | Pure UI, no business logic | Reusable, testable | More files | UI libraries, design systems |
| Container Components | Handle data/logic, pass to presentational | Separation of concerns | Boilerplate | Feature components |
| Compound Components | Multiple components work together | Flexible API, control inversion | More complex | Tabs, Accordion, Select |
| Render Props | Share code via function prop | Flexible, composable | Callback hell risk | Auth, data fetching |
| Higher-Order Components | Enhance components with shared logic | Reusable logic | Props collision, wrapper hell | Legacy code, cross-cutting |
| Hooks | Share stateful logic | Composable, clean | Rules of Hooks | Modern React standard |
| Controlled vs Uncontrolled | Parent controls state vs local state | Controlled: full control; Uncontrolled: simpler | Controlled: more code | Forms (controlled), simple inputs (uncontrolled) |
Composition vs Inheritanceβ
Composition (React's recommendation):
// Good: Composition
<Layout>
<Sidebar />
<Content />
</Layout>
Why composition over inheritance:
- More flexible
- Easier to understand
- Avoids tight coupling
- Better tree shaking
Code Reusabilityβ
| Level | Strategy | When to Extract |
|---|---|---|
| Functions | Utility functions | Used 3+ times |
| Hooks | Custom hooks | Shared stateful logic |
| Components | Reusable UI | Used 2+ times, stable API |
| Libraries | Published packages | Used across projects |
YAGNI Principle: Don't extract until you need it in multiple places.
Styling Approachesβ
CSS Methodologiesβ
| Approach | Pros | Cons | Bundle Impact | Best For |
|---|---|---|---|---|
| Plain CSS | β’ Fast β’ Simple β’ No build step | β’ Global scope β’ Naming conflicts β’ Hard to delete | Smallest | Small projects |
| CSS Modules | β’ Scoped by default β’ Type-safe (with TS) β’ No runtime | β’ Tooling required β’ Dynamic styles harder | Small | Component libraries |
| Styled-components / Emotion | β’ Dynamic styling β’ Theming β’ Co-location | β’ Runtime cost β’ Larger bundle β’ SSR complexity | Medium-Large | Apps with complex theming |
| Tailwind CSS | β’ Fast development β’ Consistent design β’ Purge removes unused | β’ Large class names β’ HTML clutter β’ Learning curve | Small (with purge) | Rapid prototyping, design systems |
| CSS-in-JS (Zero-runtime) (Vanilla Extract, Linaria) | β’ Type-safe β’ No runtime cost β’ Scoped | β’ More setup β’ Less flexible | Small | Performance-critical apps |
| Sass/Less | β’ Variables, mixins β’ Mature ecosystem | β’ Extra build step β’ Can encourage complexity | Small | Traditional projects |
Runtime vs Build-timeβ
Runtime CSS-in-JS (Styled-components, Emotion):
- Pros: Maximum flexibility, dynamic theming
- Cons: ~10-30KB runtime cost, serialization overhead
- Use when: Heavy theming, runtime style changes
Build-time CSS (Tailwind, CSS Modules, Vanilla Extract):
- Pros: Zero runtime, smaller bundles
- Cons: Less dynamic, more build config
- Use when: Performance is critical
Theming Systemsβ
| Approach | Flexibility | Performance |
|---|---|---|
| CSS Variables | High | Best |
| Context + CSS-in-JS | Very High | Moderate |
| Class swapping | Low | Best |
| Theme prop injection | High | Poor (re-renders) |
Routing Architectureβ
Client vs Server Routingβ
| Type | Pros | Cons | Best For |
|---|---|---|---|
| Client Routing (React Router, Tanstack Router) | β’ Instant transitions β’ Preserved state β’ Smooth animations β’ No full page reload | β’ Initial SEO challenges β’ Deep linking requires setup β’ Larger initial bundle | β’ SPAs β’ Apps behind auth β’ Interactive dashboards |
| Server Routing (Traditional MPAs) | β’ SEO friendly β’ Simple mental model β’ Progressive enhancement β’ Smaller JS bundles | β’ Slower navigation β’ Lost state on navigation β’ No smooth transitions | β’ Content sites β’ Marketing pages β’ E-commerce |
| Hybrid (Next.js App Router, Remix) | β’ Best of both worlds β’ Fast navigation + SEO β’ Streaming | β’ Framework-specific β’ More complexity | β’ Modern web apps (default choice) |
Route-based Code Splittingβ
Automatic in Next.js/Remix, manual in React Router:
// Lazy load routes
const Dashboard = lazy(() => import('./Dashboard'));
Benefits:
- Smaller initial bundles
- Faster first load
- Load code as needed
Trade-offs:
- Route transition delay (can be mitigated with prefetch)
- More complex error handling
Deep Linkingβ
Challenges:
- Preserving app state in URLs
- Handling authentication redirects
- Shareable links
Solutions:
- Query parameters for filters
- Route parameters for resources
- Hash fragments for scroll position
Micro-Frontend Architectureβ
Integration Approachesβ
| Approach | Pros | Cons | Use When |
|---|---|---|---|
| Build-time (NPM packages) | β’ Simple β’ Type-safe β’ Optimized | β’ Coordinated releases β’ No independent deploy | Shared component library |
| Runtime (Module Federation) | β’ Independent deploy β’ Version flexibility | β’ Runtime overhead β’ Complex debugging | Large orgs, independent teams |
| iFrame | β’ Complete isolation β’ Tech-agnostic | β’ Communication overhead β’ UX challenges (navigation, styling) | Legacy integration |
| Web Components | β’ Standards-based β’ Framework-agnostic | β’ Browser support β’ Limited state sharing | Shared widgets |
Communication Patternsβ
| Pattern | Use Case | Trade-off |
|---|---|---|
| Props | Parent to child | Simple but requires direct relationship |
| Custom Events | Cross-micro-frontend | Loose coupling but harder to debug |
| Shared State (Redux, Zustand) | Global coordination | Tight coupling |
| URL State | Deep linking, filters | Limited data types |
Shared Dependenciesβ
Problem: Each micro-frontend bundles React, lodash, etc., leading to duplication.
Solutions:
- Module Federation (share at runtime)
- External dependencies (load from CDN)
- Monorepo (coordinate versions)
Trade-off: Sharing dependencies saves bandwidth but couples deployment.
Security Considerationsβ
Authentication Patternsβ
| Pattern | Pros | Cons | Use When |
|---|---|---|---|
| JWT in localStorage | β’ Simple β’ Accessible from JS | β’ Vulnerable to XSS β’ No automatic expiry | Simple apps with low risk |
| JWT in HttpOnly cookie | β’ Immune to XSS β’ Automatic send | β’ Requires CSRF protection β’ Complex CORS | Production apps (recommended) |
| Session cookie | β’ Server-side control β’ Easy revocation | β’ Server state β’ Scaling challenges | Traditional server-rendered apps |
| OAuth/OpenID Connect | β’ Delegated auth β’ Standard protocol | β’ Complex setup β’ Third-party dependency | SSO, social login |
Data Validationβ
| Layer | Purpose | Tools |
|---|---|---|
| Client-side | UX feedback | Zod, Yup, HTML5 validation |
| Server-side | Security (ALWAYS required) | Zod, Joi, class-validator |
| Type-level | Development safety | TypeScript, JSDoc |
Golden Rule: Client validation for UX, server validation for security. Never trust client input.
XSS and CSRF Protectionβ
XSS (Cross-Site Scripting):
- Sanitize user input (DOMPurify)
- Use React (auto-escapes by default)
- Content Security Policy headers
- Avoid
dangerouslySetInnerHTML
CSRF (Cross-Site Request Forgery):
- CSRF tokens
- SameSite cookies
- Double submit cookies
- Origin header validation
Accessibilityβ
WCAG Complianceβ
| Level | Requirements | Use Case |
|---|---|---|
| A | Basic accessibility | Minimum legal requirement |
| AA | Reasonable accommodations | Standard target (most laws) |
| AAA | Highest accessibility | Specialized applications |
Key Principles (POUR):
- Perceivable: Alt text, captions, contrast
- Operable: Keyboard navigation, focus management
- Understandable: Clear language, consistent navigation
- Robust: Semantic HTML, ARIA when needed
Performance vs Accessibilityβ
Common Tension:
- Heavy focus trapping (performance) vs. simple navigation (a11y)
- Custom controls (design) vs. native controls (a11y)
Solution: Accessible patterns often improve performance:
- Semantic HTML is lighter than div soup
- Keyboard navigation reduces JS events
- Screen reader support improves SEO
Testing Strategiesβ
| Tool | What it Catches | Limitations |
|---|---|---|
| axe-core / Lighthouse | 30-40% of issues | Automated only |
| Keyboard testing | Focus management, operability | Manual |
| Screen reader testing (NVDA, JAWS, VoiceOver) | Real user experience | Time-consuming |
| jest-axe | Unit-level a11y | Limited context |
Observabilityβ
Logging Strategiesβ
| Type | Use Case | Cost | Retention |
|---|---|---|---|
| Console logs | Development | Free | None |
| Application logs (Winston, Pino) | Errors, events | Low | 7-30 days |
| Structured logs (JSON) | Queryable logs | Low | 30-90 days |
| APM logs (Datadog, New Relic) | Production monitoring | High | 90+ days |
Best Practices:
- Log levels (error, warn, info, debug)
- Correlation IDs for request tracing
- Avoid logging PII
- Sample high-volume logs
Error Trackingβ
| Service | Pros | Cons |
|---|---|---|
| Sentry | β’ Great DX β’ Source maps β’ Breadcrumbs | β’ Can be expensive at scale |
| Rollbar | β’ Good alerting β’ Telemetry | β’ Less intuitive UI |
| BugSnag | β’ Mobile-friendly β’ Session tracking | β’ Smaller ecosystem |
| LogRocket / FullStory | β’ Session replay β’ User context | β’ Privacy concerns β’ Expensive |
Error Boundaries (React):
- Prevent entire app crashes
- Graceful degradation
- User-friendly error messages
Performance Monitoringβ
Real User Monitoring (RUM):
- Actual user experience
- Geographic insights
- Device/browser breakdown
Synthetic Monitoring:
- Controlled tests
- Consistent benchmarks
- Early warning system
Key Metrics to Track:
- Core Web Vitals (LCP, FID/INP, CLS)
- Custom business metrics (time to checkout, etc.)
- Error rates
- API latency
Testing Strategyβ
Testing Pyramidβ
/\
/E2E\ Few (10%)
/------\
/ INT \ Some (30%)
/----------\
/ UNIT \ Many (60%)
/--------------\
| Level | Quantity | Speed | Confidence | Cost to Maintain |
|---|---|---|---|---|
| Unit | Many | Fast (ms) | Low | Low |
| Integration | Some | Medium (seconds) | Medium | Medium |
| E2E | Few | Slow (minutes) | High | High |
Testing Typesβ
| Type | Tools | What to Test | What to Avoid |
|---|---|---|---|
| Unit Tests | Jest, Vitest | β’ Pure functions β’ Utilities β’ Hooks logic | β’ Implementation details β’ UI styling |
| Integration Tests | React Testing Library | β’ User interactions β’ Component integration β’ Data flow | β’ Mocking everything |
| E2E Tests | Playwright, Cypress | β’ Critical user paths β’ Multi-page flows β’ Real backend | β’ Testing every feature |
| Visual Regression | Chromatic, Percy | β’ UI consistency β’ Responsive design | β’ Dynamic content |
| Performance Tests | Lighthouse CI, WebPageTest | β’ Load times β’ Bundle sizes β’ Core Web Vitals | β’ Micro-optimizations |
Test Coverage vs Speedβ
Trade-off:
- High coverage (90%+) = slow CI, maintenance burden
- Low coverage (30-50%) = faster, but less confidence
Balanced Approach:
- 70-80% unit test coverage
- Test critical paths with integration/E2E
- Avoid testing third-party libraries
- Test behavior, not implementation
Anti-patterns:
- Testing implementation details (how) vs behavior (what)
- 100% coverage dogma
- Brittle selectors (test-ids preferred)
Deployment Strategiesβ
Feature Flagsβ
| Benefit | Implementation | Trade-off |
|---|---|---|
| Progressive rollout | 1% β 10% β 50% β 100% | Technical debt if not cleaned |
| A/B testing | Random user assignment | Complex analytics |
| Kill switch | Instant feature disable | Code complexity |
| Beta features | Opt-in for specific users | User segmentation logic |
Popular Tools:
- LaunchDarkly (enterprise)
- Flagsmith (open source)
- Unleash (self-hosted)
- Custom Redis-based solution
Best Practices:
- Clean up flags after full rollout
- Use typed flags (TypeScript)
- Default to safe fallback
- Monitor flag performance impact
Progressive Rolloutsβ
| Strategy | Description | Risk Level | Use Case |
|---|---|---|---|
| Canary Release | Deploy to small % of servers first | Low | Backend changes, API updates |
| Blue-Green Deployment | Full environment switch | Medium | Major releases, database migrations |
| Rolling Deployment | Gradual server-by-server update | Low | Standard deploys |
| Shadow Mode | Run new version alongside old (no user impact) | Very Low | Testing in production |
Monitoring During Rollouts:
- Error rate spikes
- Performance degradation
- User complaints/support tickets
- Business metrics (conversion, engagement)
Rollback Mechanismsβ
| Mechanism | Speed | Complexity | Data Concerns |
|---|---|---|---|
| Feature flag toggle | Instant | Low | None (code still deployed) |
| Git revert + redeploy | 5-15 min | Low | None (if stateless) |
| Blue-green switch | Instant | High | Database schema compatibility |
| Database rollback | Slow | Very High | Data loss risk |
Golden Rule: Design for forward compatibility. Never break old clients.
Mobile Considerationsβ
Responsive vs Adaptiveβ
| Approach | How It Works | Pros | Cons | Use Case |
|---|---|---|---|---|
| Responsive Design | Fluid grids, flexible images, CSS media queries | β’ One codebase β’ Future-proof β’ Easier maintenance | β’ Can be slow on mobile β’ Compromise on experience β’ Downloads unused assets | Most websites, content-heavy apps |
| Adaptive Design | Detect device, serve different layouts/bundles | β’ Optimized per device β’ Faster on mobile β’ Device-specific features | β’ Multiple codebases β’ More maintenance β’ Detection complexity | Apps with very different mobile UX |
| Hybrid (Responsive + Adaptive) | Responsive base + adaptive enhancements | β’ Best of both β’ Progressive enhancement | β’ Most complex | Large-scale applications |
Decision Framework:
Same UX across devices?
ββ Yes β Responsive
ββ No β Consider Adaptive
ββ Budget for maintenance?
ββ Yes β Adaptive
ββ No β Responsive with mobile-first
Mobile-first vs Desktop-firstβ
| Approach | Philosophy | Pros | Cons |
|---|---|---|---|
| Mobile-first | Design for mobile, enhance for desktop | β’ Forces prioritization β’ Better mobile performance β’ Progressive enhancement β’ Smaller base bundle | β’ Desktop might feel basic β’ More CSS media queries |
| Desktop-first | Design for desktop, simplify for mobile | β’ Richer desktop experience β’ Familiar workflow | β’ Mobile as afterthought β’ Performance issues β’ Harder to simplify than enhance |
Modern Best Practice: Mobile-first (60-70% of traffic is mobile for most consumer apps)
Mobile-specific Optimizations:
| Optimization | Impact | Implementation |
|---|---|---|
| Touch-friendly targets | Better UX | Min 44x44px tap targets |
| Reduced animations | Battery, performance | prefers-reduced-motion media query |
| Image compression | Bandwidth savings | WebP/AVIF, responsive images |
| Offline support | PWA capability | Service Workers |
| Reduce font sizes | Performance | Font subsetting, system fonts |
| Lazy load below fold | Initial load time | Intersection Observer |
Progressive Web Appsβ
PWA Capabilities:
| Feature | Benefit | Browser Support | Implementation Effort |
|---|---|---|---|
| Add to Home Screen | App-like icon | Excellent | Low (manifest.json) |
| Offline Support | Works without internet | Good | Medium (Service Worker) |
| Push Notifications | Re-engagement | Good (not iOS Safari) | Medium (Push API) |
| Background Sync | Reliable data sync | Limited | High |
| Install Prompts | Increase installs | Variable | Low |
PWA vs Native App Trade-offs:
| Aspect | PWA | Native App | Winner |
|---|---|---|---|
| Development cost | Lower (one codebase) | Higher (iOS + Android) | PWA |
| Distribution | No app store | App stores | PWA (easier) |
| Device access | Limited | Full | Native |
| Performance | Good | Excellent | Native |
| Updates | Instant | Store review delay | PWA |
| Discoverability | SEO | App store optimization | Depends |
| Offline capability | Good | Excellent | Native |
When to Choose PWA:
- Content-focused apps
- Limited budget
- Need SEO
- Frequent updates
- Cross-platform priority
When to Choose Native:
- Heavy device integration (camera, sensors, etc.)
- Performance-critical (games, AR/VR)
- Native UX is important
- App store presence needed
Hybrid Approach (PWA + Native shell):
- LinkedIn, Twitter use this
- PWA for web, native wrapper for app stores
- Shared web codebase
Interview Frameworkβ
How to Approach Questionsβ
Step-by-step Framework:
1. Clarify Requirements (5 min)β
Ask questions to understand:
- Scale: How many users? Geographic distribution?
- Features: Core vs nice-to-have?
- Constraints: Timeline? Team size? Budget?
- Users: Technical level? Devices? Network conditions?
- Performance: What matters most? (SEO, TTI, offline, etc.)
Example Questions:
- "How many concurrent users do we expect?"
- "Is this a public-facing site or internal tool?"
- "What's more important: initial load time or runtime performance?"
- "Do we need to support offline functionality?"
- "What browsers/devices must we support?"
2. Define High-level Architecture (10 min)β
Structure your answer:
1. Rendering strategy (CSR/SSR/SSG)
2. State management approach
3. Data fetching pattern
4. Key infrastructure (CDN, caching)
5. Major trade-offs
Visual representation helps:
[User] β [CDN] β [Next.js SSR] β [API Gateway] β [Services]
β
[React Query Cache]
β
[Component Tree]
3. Dive into Components (15 min)β
Pick 2-3 interesting areas to detail:
- Performance bottlenecks
- Complex state management
- Real-time features
- Security concerns
Show trade-off thinking: "I would start with X because [reason], but if we see Y problem, we could switch to Z."
4. Discuss Trade-offs (5 min)β
For every decision, mention:
- Why this choice? (requirements it satisfies)
- What's the downside? (what we're sacrificing)
- When would we change? (what would make us revisit)
5. Scaling & Evolution (5 min)β
Discuss how the system evolves:
- "At 10K users: Simple SSR works fine"
- "At 100K users: Add CDN caching, API rate limiting"
- "At 1M users: Consider ISR, edge functions, micro-frontends"
Common Pitfallsβ
| Pitfall | Why It's Bad | Fix |
|---|---|---|
| Jumping to solutions | Miss requirements, wrong architecture | Always clarify first |
| Over-engineering | Adds complexity unnecessarily | Start simple, justify complexity |
| Ignoring trade-offs | Shows lack of depth | Every decision has pros AND cons |
| Buzzword dropping | Sounds hollow without context | Explain WHY you'd use a technology |
| Not asking questions | Seems overconfident or inexperienced | Show curiosity, clarify ambiguity |
| Ignoring non-functional requirements | Incomplete design | Consider security, a11y, i18n, monitoring |
| One-size-fits-all | Different problems need different solutions | Adapt to requirements |
| Forgetting about users | Too technical, not user-centric | Consider UX, accessibility, performance |
Sample Answersβ
Example 1: Design Instagram Feedβ
Clarification Questions:
- How many users? Daily active users?
- Real-time updates required?
- Image upload/processing requirements?
- Offline support needed?
High-level Design:
Architecture:
ββ Rendering: SSR for initial feed (SEO, fast FCP)
ββ Client: React SPA after hydration
ββ State:
β ββ React Query for feed data
β ββ Zustand for UI state (modals, selections)
ββ Real-time: WebSocket for new posts notification
ββ Media: CDN with progressive image loading
Key Decisions:
-
Rendering: SSR β CSR hybrid
- Why: Fast initial load + SEO, then rich client interactivity
- Trade-off: More complex than pure CSR
- When to change: If SEO isn't important, go CSR-only
-
Infinite Scroll with Virtualization
- Why: Smooth UX for long feeds
- Implementation: react-window + Intersection Observer
- Trade-off: Complexity vs. memory efficiency
-
Optimistic Updates
- Why: Instant feedback on likes/comments
- Trade-off: Need rollback logic if server fails
- Implementation: React Query mutations
-
Image Strategy
- Why: Images dominate bandwidth
- Approach:
- Lazy load below fold
- WebP with JPEG fallback
- Responsive images (srcset)
- Blur placeholder (LQIP)
-
Caching Strategy
- CDN: Cache images (long TTL)
- React Query: Cache feed data (5 min stale time)
- Service Worker: Offline image viewing
Scaling Considerations:
- 10K users: Single server SSR works
- 1M users:
- Edge SSR (Vercel, Cloudflare)
- Separate API gateway
- Image processing service
- 10M users:
- Feed sharding by user
- Read replicas
- Micro-frontends for features
Example 2: Design a Collaborative Document Editor (Google Docs-like)β
Clarification:
- Real-time collaboration required? (Yes)
- Simultaneous editors expected? (Up to 50)
- Mobile support? (Yes)
- Offline editing? (Nice to have)
High-level Design:
Architecture:
ββ Rendering: CSR (no SEO need, auth-gated)
ββ Real-time: WebSocket with CRDT for conflict resolution
ββ State:
β ββ Local: Prosemirror/Quill editor state
β ββ Sync: Yjs for distributed state
ββ Persistence:
β ββ Auto-save every 2s (debounced)
β ββ Manual save option
ββ Offline: IndexedDB + sync on reconnect
Key Decisions:
-
CSR Only
- Why: Auth-gated, no SEO needed, heavy interactivity
- Trade-off: Slower initial load OK for tool
- Alternative: Could SSR shell for faster perceived load
-
CRDT for Conflict Resolution
- Why: Automatic merge of concurrent edits
- Options: Yjs (recommended), Automerge, operational transforms
- Trade-off: Complexity vs. robust collaboration
- Alternative: Last-write-wins (simpler but loses data)
-
WebSocket over Polling
- Why: Low latency, bidirectional
- Trade-off: Infrastructure complexity, connection management
- Fallback: Long polling for old browsers
-
Editor Choice: Prosemirror
- Why: Flexible, powerful, collaborative-ready
- Trade-off: Steeper learning curve vs. Draft.js/Quill
- Alternative: Quill (simpler, less flexible)
-
Optimistic Updates + Rollback
- Why: Instant typing feedback
- Implementation:
- Apply change locally immediately
- Send to server
- Rollback if conflict/error
-
Cursor Positioning
- Show other users' cursors with names/colors
- Implementation: Relative positioning in CRDT
- Challenge: Performance with 50 users (throttle updates)
Performance Considerations:
-
Document Size:
- Paginate very long docs (100+ pages)
- Lazy load images/embeds
- Virtual scrolling for large docs
-
Collaboration Scale:
- Up to 10 users: Direct WebSocket
- 10-50 users: Throttle cursor updates (200ms)
- 50+ users: Consider room-based splitting
-
Memory Management:
- Cleanup old history (keep 100 operations)
- Garbage collect CRDT tombstones
- Unload off-screen content
Offline Strategy:
- IndexedDB stores document + pending changes
- Service Worker caches app shell
- Sync queue when reconnecting
- Conflict resolution UI if diverged
Example 3: Design an E-commerce Product Listing Pageβ
Requirements:
- 10K products, need filters/search
- SEO critical
- Fast perceived performance
- Mobile-first
High-level Design:
Architecture:
ββ Rendering: ISR (Next.js)
β ββ Static generation for popular products
β ββ On-demand for long tail
ββ Search: Algolia/Elasticsearch
ββ Filters: URL state + React Query
ββ Images: CDN + progressive loading
Key Decisions:
-
ISR over SSR/SSG
- Why:
- SEO (need server rendering)
- Fresh inventory (revalidate every 5 min)
- Scale (10K builds too slow for pure SSG)
- Trade-off: Next.js lock-in
- Alternative: SSR + aggressive CDN caching
- Why:
-
Search: Algolia (managed) vs Elasticsearch (self-hosted)
- Algolia Pros:
- Fast setup
- Great DX
- Built-in typo tolerance
- Algolia Cons:
- Expensive at scale
- Vendor lock-in
- When to switch: Over 1M searches/month β consider Elasticsearch
- Algolia Pros:
-
Filter State in URL
- Why: Shareable links, back button works
- Implementation: URLSearchParams + React Router
- Trade-off: URL can get ugly with many filters
-
Virtualization for Long Lists
- Why: 1000+ products per page possible
- Implementation: react-window
- Alternative: Pagination (simpler, SEO-friendly)
- Decision: Use pagination with "Load More" for SEO
-
Image Strategy
- Blur placeholder (LQIP)
- Lazy load (Intersection Observer)
- Responsive images
- WebP with JPEG fallback
- CDN with automatic optimization (Cloudinary)
Performance Budget:
- LCP < 2.5s (hero image)
- FID < 100ms
- CLS < 0.1 (reserve image space)
Caching Strategy:
- ISR: Revalidate every 5 min
- CDN: Cache static assets 1 year
- Browser: Cache product images 1 week
- API: React Query cache 5 min, stale-while-revalidate
Mobile Optimizations:
- Sticky filters button (mobile)
- Bottom sheet for filters (not sidebar)
- Touch-friendly product cards (min 44px)
- Reduce image quality on slow connections
Key Takeawaysβ
Universal Principlesβ
-
No Silver Bullets: Every decision trades one benefit for another.
-
Start Simple: Complexity should be justified by requirements, not resume building.
-
Measure First: Don't optimize what you can't measure.
-
User-Centric: Performance metrics exist to serve user experience.
-
Evolutionary Architecture: Design for change, not perfection.
Interview Success Formulaβ
Great Answer = Clear Communication
+ Trade-off Awareness
+ Justification
+ Adaptability
Remember:
- Interviewers want to see your thinking process, not just the answer
- There's rarely one "correct" solution
- Asking clarifying questions shows experience
- Acknowledging limitations shows maturity
- Discussing when you'd change approaches shows depth
Red Flags to Avoidβ
β "We should use microservices/GraphQL/etc." (without justification) β "This is the best way to do it" (no trade-off awareness) β Over-engineering for small scale β Ignoring non-functional requirements (security, a11y) β Not considering costs (infrastructure, maintenance, developer time)
Green Flags to Showβ
β Ask clarifying questions first β Start with simple solution, then iterate β Mention trade-offs for every decision β Consider different scales (10K vs 1M vs 10M users) β Think about maintenance and team velocity β Consider total cost of ownership β Show awareness of modern best practices β Be honest about what you don't know
Quick Reference Cardsβ
When to Use Whatβ
Rendering:
- CSR: Dashboards, auth-gated tools
- SSR: E-commerce, social media, content
- SSG: Blogs, docs, marketing
- ISR: Product catalogs, news sites
State Management:
- Local: UI toggles, forms
- Context: Theme, i18n, auth status
- React Query/SWR: Server data
- Redux/Zustand: Complex business logic
Data Fetching:
- REST: Default choice, simple CRUD
- GraphQL: Complex nested data, mobile apps
- tRPC: Full-stack TypeScript monorepos
Styling:
- Tailwind: Fast development, consistency
- CSS Modules: Component libraries
- CSS-in-JS: Heavy theming needs
- Plain CSS: Simple sites, performance-critical
Performance Quick Winsβ
- Images: WebP, lazy load, responsive srcset
- Fonts: System fonts or subset web fonts
- Code Splitting: Route-based, dynamic imports
- Compression: Enable Gzip/Brotli
- Caching: HTTP headers, CDN, React Query
- Bundle: Tree shaking, minification
- Critical CSS: Inline above-fold styles
- Preload: Critical resources
Common Interview Questionsβ
- "Design a news feed (Twitter/Instagram/Facebook)"
- "Design a real-time collaborative editor (Google Docs)"
- "Design a video streaming platform (YouTube)"
- "Design an e-commerce site (Amazon)"
- "Design a chat application (WhatsApp/Slack)"
- "Design a file storage system (Dropbox)"
- "Design a dashboard with real-time data"
- "Design a booking system (Airbnb)"
For each, remember to discuss:
- Rendering strategy
- State management
- Real-time needs (if any)
- Caching strategy
- Performance considerations
- Scaling approach
Additional Resourcesβ
Further Readingβ
- Performance: web.dev, WebPageTest
- React Patterns: patterns.dev
- System Design: Front-End Engineer (book)
- Best Practices: MDN, React docs
Practice Platformsβ
- Frontend Mentor (UI challenges)
- GreatFrontEnd (system design questions)
- LeetCode (algorithmic problems)
- Pramp/Interviewing.io (mock interviews)
Final Note: This guide is a reference, not a script. Use it to develop intuition about trade-offs, then adapt to each unique situation. The best engineers don't memorize solutionsβthey understand principles and apply them contextually.
Good luck with your interviews! π