Early on, my instinct was to optimize aggressively for security. Short-lived access tokens. Strict expiration. Frequent re-authentication.
From a security standpoint, it was "correct." From a user standpoint, it was painful.
The moment that changed my thinking was watching a user make a long set of changes — only to be hit by a refresh overlay. They were forced to reload. Everything was lost.
That wasn't just frustrating. It broke trust.
What I underestimated was how often good security decisions fail in practice when they introduce friction users don't understand. Sessions expired mid-workflow. Background jobs failed. Re-authentication felt arbitrary.
If users experience your system as unpredictable, they'll work around it — and that's when security actually breaks down.
So I changed my approach. Instead of treating security and user experience as opposing goals, I started designing refresh token flows around a simple rule:
Silently refresh when continuity is high and authority is unchanged.
Interrupt when either continuity breaks or authority increases.
That meant keeping access tokens short-lived, using refresh tokens intentionally and revocably, and refreshing sessions transparently when no real security boundary was being crossed.
The result wasn't weaker security — it was more usable security.