Redis on the server, some state library in the browser, and a pile of hand-written sync between them. I got tired of writing that layer, so I built a cache engine that compiles to both native and WebAssembly.
Every app I've worked on ends up with the same shape.
There's a cache on the server — Redis, usually. And there's state in the browser — Zustand, Redux, React Query, whatever. Between them sits a layer nobody designed on purpose: fetchedAt timestamps, staleness checks, an invalidation call you have to remember to make after every mutation, and a polling interval you tuned once and never revisited.
That layer is where the bugs live. The cart total that's stale for four seconds. The feature flag that flipped in the dashboard but not in the tab that's already open. The counter that goes backwards because two tabs raced.
The frustrating part is that both halves are caches. They store keys, they expire things, they invalidate. We just built them out of completely different parts and then wrote glue to keep them agreeing.






