Caching is the fastest way to make a product feel quick and the fastest way to serve someone else’s data to the wrong person. The difference is whether the invalidation rule was designed or improvised.
Cache with a stated rule
Every cached value gets three answers before it exists: what invalidates it, how long it may be stale, and who it belongs to. Anything user-specific is keyed by user, and anything permission-dependent is not cached at the edge at all.
Queues for work users should not wait for
Mail, exports, third-party calls, image processing, recalculation. Each job is idempotent, retried with backoff, and lands in a dead-letter queue with an alert if it keeps failing. Queue depth and oldest-message age are on the dashboard, because those two numbers predict an incident before users notice one.
Rate limiting and locks
Token buckets in front of expensive endpoints, and short-lived locks where two workers must not do the same thing twice. Both are small pieces of code that prevent whole categories of outage.
Sessions, with a caveat
Redis is an excellent session store as long as everyone remembers it is memory. Persistence settings, eviction policy and the maximum memory limit are chosen deliberately — a default eviction policy quietly dropping session keys is a genuinely confusing bug to chase.
Sizing and failure
We plan for the cache being unavailable. The application should get slower, not break. That single design rule turns a Redis incident into a bad afternoon instead of an outage.
Works with Node.js and Python services in front of PostgreSQL.