Field note / patterns

publishedupdated 2026-05-04#frontend#system-design#bff#rendering#caching

Frontend System Design

A senior-to-principal interview guide to frontend system design: scoping, rendering strategy, caching, real-time delivery, and micro-frontend trade-offs.

TL;DR

  • Strong frontend system design answers start by narrowing scope, not by drawing boxes too early.
  • I evaluate the full stack: browser, service worker, CDN, BFF, backend APIs, and observability.
  • The recurring trade-offs are SSR vs CSR vs RSC, cache freshness vs speed, and independence vs consistency in multi-team UI systems.
  • At principal level, I expect the discussion to include failure modes, metrics, and organizational boundaries, not just framework choices.

Context

Frontend system design interviews are rarely about a component tree. They are about how the browser, CDN, BFF, rendering model, and backend contracts work together under product constraints. This is especially relevant in the kind of systems I have worked around: large React and Next.js applications with shared UI infrastructure, experimentation, multiple downstream services, and heavy performance expectations.

The Approach

1. Scope the prompt before solving it

If someone says “design Twitter” or “design a collaborative editor,” I narrow it first:

  • which client? mobile web, desktop web, or native wrapper?
  • read-heavy or write-heavy path?
  • authenticated or SEO-sensitive?
  • real-time or eventual consistency?
  • what scale actually matters?

That turns a vague question into an architecture problem I can reason about.

2. Work the stack top to bottom

I frame frontend system design as a multi-layer system, not as “the UI” in isolation.

The layers I usually cover:

  • browser: memory, storage, hydration, responsiveness
  • edge/CDN: static assets, image optimization, edge routing
  • BFF: aggregation, auth-aware shaping, SSR/RSC decisions
  • backend: REST, GraphQL, gRPC, event-driven dependencies
  • observability: RUM, trace correlation, error tagging, performance budgets

3. Choose rendering strategy explicitly

My rendering decision matrix usually looks like this:

  • CSR for authenticated, interaction-heavy apps where SEO is irrelevant
  • SSR when initial content speed or crawlability matters
  • SSG/ISR for docs, marketing, or content with relaxed freshness
  • RSC when I want server-owned data composition with lower client JS cost

The important point is not to recite the matrix. It is to explain why a route or feature earns one strategy over another.

4. Treat the BFF as a serious design layer

I like a backend-for-frontend when the page needs:

  • fan-out to many downstream services
  • auth/session-aware shaping
  • device-specific response shaping
  • fewer client round trips

I keep orchestration in the BFF, but I avoid putting durable business rules there. Otherwise the BFF turns into a second product backend.

5. Cache at multiple layers with explicit invalidation

I want the design conversation to include:

  • browser memory and client cache
  • service worker / Cache API
  • HTTP cache headers
  • CDN cache
  • BFF in-memory or request cache

The real question is not “should we cache?” It is:

  • what is the TTL?
  • what invalidates it?
  • can I serve stale?
  • what happens on cache miss?

6. Real-time is a transport choice and a product choice

I choose transport by directionality and latency:

  • SSE for server-to-client streams like notifications or AI output
  • WebSocket for bidirectional chat or collaboration
  • long polling as a compatibility fallback
  • WebRTC only when peer-to-peer media or low-latency peer channels really matter

7. Micro-frontends are an organizational decision first

When the design prompt touches multi-team UI ownership, I discuss:

  • route-level vs component-level boundaries
  • shell responsibilities
  • shared design system and auth
  • remote failure isolation
  • versioning and observability

I only recommend micro-frontends when deployment independence and team autonomy clearly outweigh the runtime complexity.

Trade-offs

  • Richer BFF and edge layers improve UX and latency, but they also create more places where logic can drift.
  • SSR and RSC improve first content speed, but they increase server responsibility and debugging complexity.
  • Heavy caching improves performance, but stale data and invalidation bugs become product issues quickly.
  • Micro-frontends can scale organizations well, but they can fragment performance budgets and UX consistency if the platform is weak.

References