Honest Staleness: Stop Telling Users Their Data Is Live When It Isn't
- UX
- realtime
- product design
- React Native
- North
Every realtime app eventually faces the same small, defining choice. An update stops flowing — backgrounded app, dead battery, no signal — and the UI still says "Sharing live." Technically the sharing session is active. Practically, you're showing a three-hour-old location under a label that means "right now."
That's lying. Politely, by omission, with plausible deniability — but lying. I shipped the fix in North and posted about it at the time: showing "Sharing live" when the location is stale is just lying to your users… it's the whole app's promise.
The rule: freshness is a timestamp comparison, not a session state
North's presence logic now has one source of truth: a member is "Sharing live" only if their last update is younger than 10 minutes. One constant (FRESH_MS), one comparison. Older than that, the label switches to the truthful form: "Last seen 25 min ago."
The deeper shift is what the freshness signal is derived from. "Sharing live" used to describe intent — sharing is enabled, the session exists. The new rule describes evidence — data actually arrived recently. Sessions can be active while the phone is dead in a backpack; evidence doesn't have that failure mode. Whenever a status label can be derived from either intent or evidence, pick evidence.
Why 10 minutes? It tracks the app's real update cadence — background updates arrive every few minutes between heartbeats and movement, so inside 10 minutes "live" is defensible; beyond it, something has genuinely stalled. The threshold isn't a magic number, it's your pipeline's honesty budget: as fast as your worst-case healthy update interval, no faster.
Degrade timestamps like a human would
The second half of the fix sounds cosmetic and isn't. The relative-time formatter used to cap at hours, producing labels like "70 hr ago" — technically accurate, cognitively useless, and weirdly alarming. The normalized ladder:
- under an hour → "42 min ago"
- under a day → "5 hr ago"
- under a week → "3 days ago"
- under ~5 weeks → "2 wks ago"
- beyond that → "a while ago"
That last rung is the interesting one. Past a certain age, precision stops being information and starts being noise — nobody needs "847 hr ago," they need "this person hasn't been here in a long time." Vagueness, chosen deliberately, is the honest rendering of old data. (A unit test suite pins the whole ladder down, because timestamp formatters are exactly the code that silently regresses.)
There's also a small de-duplication rule: when the subtitle already says "Last seen 25 min ago," the trailing timestamp chip on the row is hidden. Saying it twice reads like the UI doesn't trust itself.
The same evidence rule governs the activity chips: walking/driving indicators only render while the underlying fix is under 10 minutes old. A "driving" badge from 40 minutes ago isn't a status; it's a ghost.
Why honesty wins commercially, not just morally
For a family app, the case is brutal and concrete. The "Sharing live" lie has a guaranteed discovery moment: "Why does it say you're at school? You got home an hour ago." Each one quietly teaches the family the app can't be trusted — and a location app that can't be trusted has no product left.
"Last seen 1 hr ago" carries the same information plus the meta-information that the app tells the truth. Users forgive stale data — phones die, tunnels exist. What they don't forgive is discovering the app knew and dressed it up. Staleness honesty also composes with the rest of the system: it's the visible face of the dead-man's-switch layer that's simultaneously working to fix the staleness it's admitting to.
The general principle, and it extends well past location apps: a status indicator is a promise, not a decoration. If you can't back the promise with evidence right now, change the label — every dashboard "online" badge, every chat presence dot, every sync checkmark included. Small honest calls are the cheapest trust you'll ever buy.