FSD Frontend System Design

Part III β€” The Speed Β· Lesson 6.1

Database & Caching Overview

In one lineThe browser has five storage tiers β€” pick by size, lifetime, and who else needs to read it.

Where you've seen itA mail client holding a theme preference, an unsent draft, a session token, and a full offline mailbox β€” four different stores, deliberately.

  • Time14 min
  • DiagramStorage tier stack with size, lifetime, and access cost
  • Chapter6 Β· Database & Caching

Diagram

diagram
THE TIERS β€” fastest and smallest at the top

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Memory (JS variables, store)   0ms   dies on reload      β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚ sessionStorage      ~5MB       sync   dies with the tab  β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚ localStorage        ~5MB       sync   forever, sync API  β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚ Cookies             4KB        sync   SENT TO SERVER     β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚ IndexedDB           GBs        async  forever, structuredβ”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
  β”‚ Cache Storage (SW)  GBs        async  Request/Response   β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚
                        β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ HTTP cache (browser-managed)   ← you control it with     β”‚
  β”‚ CDN edge cache                    headers, not with code β”‚
  β”‚ Origin / real database                                    β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
diagram
THREE QUESTIONS THAT PICK THE TIER

  1. How big?        > 5MB          ─► IndexedDB / Cache Storage
  2. How long?       this tab only  ─► sessionStorage
                     across visits  ─► localStorage / IndexedDB
  3. Who reads it?   the server     ─► cookie (it's the only one sent)
                     only JS        ─► anything else
diagram
CACHING'S TWO HARD PARTS

  1. WHAT to cache        cheap to decide
  2. WHEN to invalidate   the actual problem

     stale data ◄──────────────► extra requests
       (wrong)                     (slow)

  Every strategy in this chapter is a position on that line.

How it works

  1. Start from lifetime. Data that must not outlive the tab goes in sessionStorage; everything longer-lived needs a deliberate invalidation story.
  2. Never put secrets in web storage β€” any XSS reads all of it instantly (see 3.5).
  3. Prefer async storage for anything sizeable. localStorage is synchronous and blocks the main thread, which shows up directly in INP.
  4. Version your stored shapes. Old data in a new app is a crash waiting to happen β€” store a schema version and migrate or discard.
  5. Assume it can vanish. Storage is evictable under pressure and users clear it; treat every cache as an optimisation, never as the source of truth.

Trade-offs

StoreSizeSync?Sent to serverGood for
MemoryRAMsyncnoDerived/session state
sessionStorage~5MBsyncnoPer-tab drafts, wizards
localStorage~5MBsyncnoSmall prefs, flags
Cookies4KBsyncyesAuth, server-read state
IndexedDBGBsasyncnoOffline data, big lists
Cache StorageGBsasyncnoRequests/responses (SW)

Interview angle

"Where would you cache the user's feed?"

  • Three layers: HTTP cache for the assets, an in-memory client cache for the current session, and IndexedDB for offline availability.
  • Normalise entities (6.6) so one updated post updates everywhere rather than in three copies.
  • Then state the invalidation rule explicitly β€” TTL, ETag revalidation, or event-driven purge β€” because that's the part that actually gets asked about.

Recap

  • Size, lifetime, and audience pick the tier; nothing else matters much.
  • Cookies are the only store the server sees, and they cost bytes on every request.
  • Caching is easy; invalidation is the design decision worth defending.