A glance is not a reading
PopsicleBoat’s sidebar recently grew a badge: All Posts · 13. Thirteen new things since you last looked, plus a list up top of what they are — because I’d noticed that even I, the person who built the place, couldn’t find my own new content. If the shipwright can’t find the new planks, a stranger has no chance.
Shipping the badge was easy. Defining the one word it depends on took three tries.
The word is new, and my first definition was the obvious one: new means posted since your last visit. One timestamp per user — stamped when you open the board — and everything after it is new. Databases love this. It’s one column, one comparison, and it’s how half the software you use decides what to bold.
Then I used it. I opened the board, clicked the first new post, read it, came back — and the list of new posts was gone. All of it. The badge still insisted there were thirteen, but the list beneath it had emptied, because my visit had stamped the timestamp, and by the time I returned, nothing in the database was “since your last visit” anymore. I had read one post and the system had quietly credited me with all thirteen.
That’s the watermark model, and its flaw is right there in the metaphor: a watermark rises whether or not you looked at what it covered. Arriving at the page claimed I’d seen everything on it. A glance at the harbor became a certified reading of every ship’s manifest.
So, second try: sessions. Don’t let a single visit swallow the list — hold the line steady while you’re actively reading, and only advance it after you’ve been away a while. Now the list survived a click-and-return. Progress. I opened a post, read it, came back, and — the badge still said thirteen. And the post I had just read sat there in the list, marked new, looking at me.
Of course it did. I’d made the line move more politely, but “new” was still defined by time. The system knew when I’d been on the board; it had no idea what I’d actually read. Both models shared the same wrong bone: they tracked the clock, when they should have tracked the items.
Third try, the one that stuck: a read is a fact about you and one post. A little join table — this user read this post — written the moment you open a thread. Now the pieces fall into place without any cleverness. Open a post and the badge counts down by exactly one, live. Come back to the board and that post has left the list; the other twelve are still there, still owed to you. Nothing you didn’t open gets claimed on your behalf. The timestamp survives only as a floor, so ancient history from before the feature existed doesn’t haunt the count — but above that floor, every post is settled item by item.
And because any inbox eventually becomes a chore, there’s a broom: Mark all as read, for the days you’d rather wave at the backlog than shake hands with it. The broom is honest too — it’s you declaring the items read, not the system inferring it from your footsteps.
What strikes me in hindsight is that I knew the right model all along, as a user. Every notification system that has ever annoyed me — the messenger that marks a whole conversation read because I peeked, the changelog bolding things I’ve already seen — annoyed me for exactly this reason: it confused my presence with my attention. I had simply never said the rule out loud. It took a badge refusing to count down, twice, to make me say it:
A glance is not a reading. Presence is not attention. If the system claims I’ve seen something, it should be because I opened that thing — not because I walked past it.
One timestamp is how the database wants to remember it. One row per reading is how it actually happened.