DEV Community

Cover image for The Optimization That Was Too Good: Why Our Push Notifications Only Worked When You Weren't Looking

The Optimization That Was Too Good: Why Our Push Notifications Only Worked When You Weren't Looking

Dhruv Jani on August 22, 2026

This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry. When I was building the push notification system for ShelfTalk —...
Collapse
 
bryanw profile image
Bryan Williams

This one hit home because I shipped the same shape of bug this week in a totally different place: a CI classifier that read test results from stdout. "No output" meant "test died mid-run" — except when the process crashed at exit and the output just lost the flush race. Same trap as document.hidden: a signal pressed into carrying a meaning it was never designed for. Visible != attentive, and silent != failed. My fix rhymed with the delivery-vs-seen idea from the comments — move the truth to a channel that survives the failure mode (a synchronously-written result file) instead of inferring it from a side effect. Great writeup, and the second-monitor detail is exactly the kind of real-user setup no dev machine ever reproduces.

Collapse
 
dj29 profile image
Dhruv Jani

That's a really good parallel. The “signal carrying a meaning it was never designed for” part is exactly what got me here too.

And yeah, the second-monitor setup was the kind of real-world case I completely missed while testing. Glad the story resonated!

Collapse
 
p0rt profile image
Sergei Parfenov

the office-machine example makes this a distributed-state bug, not only a visibility bug. each device independently suppresses delivery from local state, but no device has proof that the human saw the message. document.hidden can choose a presentation channel; it should never be allowed to delete the event.

deleting the optimization is the right safe fix. the longer-term state machine is created -> delivered per device -> seen per user, with suppression only after the server has a seen acknowledgement. otherwise one technically visible device vetoes every other device without evidence.

does ShelfTalk already have server-side unread state u could use for that, or is notification state local-only today?

Collapse
 
dj29 profile image
Dhruv Jani

Good catch, and yeah, this is basically @anassBld's distributed-state point taken one step further with an actual engineering answer instead of just "combine signals." You're right that document.hidden can only ever be a presentation-channel decision, not a delete-the-event one — that's exactly the trap I fell into, just framed better than I did in the post 😅

Went back and checked, and turns out I already halfway did what you're describing:

// requireHidden is now dead code, never called with true anywhere
// push fires on every connected device regardless of local visibility
Enter fullscreen mode Exit fullscreen mode

So no device gets veto power anymore, that part's fixed.

To your actual question: it's split today.

  • In-app notifications (the bell/inbox) do have real server-side state — isRead on the Notification model in Mongo, per user, only flipped by an explicit markAsRead call. That's already a legit "seen" signal.
  • Desktop push popups have none of that. They're fire-and-forget over the socket — no delivered record, no seen record, nothing persisted.

So the created → delivered per device → seen per user pipeline you're describing doesn't exist for push yet. If I take this further than a demo project, that's the actual next build, not another local heuristic. Appreciate you laying out the model this clearly.

Collapse
 
p0rt profile image
Sergei Parfenov

this is the right split. isRead gives u a user-level fact, while push is only an attempted delivery until each device acknowledges it. the next useful step is a per-device delivery id that folds into one user-level seen state, so presentation choices never decide whether the event exists.

Collapse
 
ofri-peretz profile image
Ofri Peretz

The document.hidden false positive on multi-monitor setups is a clean example of an API whose name implies a stronger guarantee than it actually delivers — "not hidden" means the OS can render it, not that a human is paying attention. I've run into the same semantic gap in security contexts: document.hasFocus(), Notification.permission === 'granted', and similar presence signals all have different failure modes, and combining them doesn't stack the guarantees the way you'd expect. The delete-the-optimization fix is almost always the right call once you've found the ceiling. One thing worth flagging: LLMs generating notification code tend to reproduce this exact assumption because the MDN prose for document.hidden is technically accurate but elides the multi-monitor nuance entirely — so if you're reviewing AI-assisted PRs in this area, it's worth adding it to the checklist.

Collapse
 
dj29 profile image
Dhruv Jani

Thanks @ofri-peretz! for pointing that out! I hadn't considered the LLM-generated code angle, but you're absolutely right. I'll keep that nuance in mind when reviewing AI-assisted code in this area. Really appreciate the insight!

Collapse
 
ofri-peretz profile image
Ofri Peretz

The LLM angle is worth naming precisely, because the failure mode is consistent: models pattern-match on the common service worker examples that call showNotification() unconditionally, then bolt on foreground detection as an afterthought. The clients.matchAll() check looks right on inspection but often returns an empty array because the page never registered a controllerchange listener — so the worker concludes there's no active client and falls through to the background path every time. You end up with code that passes review, passes manual testing in a fresh tab, and silently does the wrong thing in production. The concede: this isn't unique to AI-generated code, it's just that the subtle async coordination between page and worker is exactly the kind of thing confidence-calibrated generation gets wrong at higher rates.

Thread Thread
 
dj29 profile image
Dhruv Jani

Yeah, this is exactly the nuance I wish I'd included more explicitly in the post. The bug itself definitely isn't “an AI bug” — it's a subtle page ↔ service-worker coordination issue that can survive both review and casual testing.

What AI changes is the probability of that failure mode: the generated code looks completely reasonable because it's assembled from familiar notification/service-worker patterns, while the missing coordination between those pieces is easy to overlook.

That “looks right on inspection, silently fails in the real environment” distinction is probably the biggest lesson I took from this bug. Thanks for articulating it so precisely.

Thread Thread
 
ofri-peretz profile image
Ofri Peretz

The activation race is the specific mechanism worth naming: navigator.serviceWorker.controller is null until the worker reaches the activated state, so any message dispatched before that resolves drops silently — no error, no console warning. In development the race is nearly always pre-won because the worker is already active from a prior session, which masks it completely during manual testing. You're right that this class of bug predates AI tooling entirely; what shifts is that pattern-assembled code tends to get each component correct in isolation while skipping the state-machine coordination between them, so every individual piece looks exactly as it should under review.

Thread Thread
 
dj29 profile image
Dhruv Jani

That activation-race framing is the missing piece — dev masking it because the worker's already warm explains exactly why this kind of bug survives local testing every time. Makes me think the real guard isn't more careful review, it's testing against a cold registration on purpose (hard refresh / unregister first) instead of trusting a warm dev session. Appreciate you naming the actual mechanism instead of leaving it at "coordination issue" — that's the version I'll actually remember next time I touch a service worker.

Collapse
 
alexshev profile image
Alex Shev

The useful test for “The Optimization That Was Too Good: Why Our Push Notifications Only Worked When You Weren't Looking” is whether the lesson changes a team decision, not just a local implementation. I’d capture the failure signal, the guardrail that caught it, and the smallest regression test that keeps it from returning. That turns a good postmortem into something another team can actually reuse.

Collapse
 
dj29 profile image
Dhruv Jani

That's a great way to frame it! In my case, though, ShelfTalk was originally a college project and I was the only one building it. I revived it later for the GitHub Finish-Up-A-Thon challenge, which is when I dug much deeper into these edge cases. Still, love the point about turning a bug into something reusable.

Collapse
 
anasbuilds997 profile image
anassBld

Great debugging story. Your "Ah-Ha" moment is actually a specific instance of a general distributed-systems trap: inferring client state from a single signal.

document.hidden told you about the OS viewport, but you used it to model human attention — and attention was never the thing being measured.

The fix that usually survives multi-monitor reality without fully reverting is to combine signals: fire the notification, but let the client acknowledge receipt (focus event, service-worker interaction) mark it as seen. That way you optimize for "don't annoy an attentive user" based on observed behavior rather than predicted attention — closer to how push systems treat delivery vs. read receipts.

The deeper lesson matches what we keep hitting building agent infrastructure: any state inferred from one observation ("visible", "returned 200") should stay provisional until confirmed by a second independent signal. Congratulations on the Finish-Up-A-Thon win, by the way.

Collapse
 
dj29 profile image
Dhruv Jani

This is a really interesting way to look at it. I was basically treating document.hidden as “the user isn't looking,” when that's obviously not what it actually tells us. 😅

I really like the idea of separating delivery from the “seen” state instead of trying to predict attention. Definitely something I'd implement differently if I were rebuilding this now. Thanks for the detailed response!

Collapse
 
articlefeed profile image
Boris Dzhingarov

The second monitor detail is the whole story. Every analytics tool I use has the same blind spot: a tab open on a side screen keeps logging time on page, so a page nobody has glanced at all afternoon looks like the best performing thing on the site. Same boolean pressed into meaning attention when it only ever meant visible.

Deleting your own clever code is the right ending too.

Collapse
 
dj29 profile image
Dhruv Jani

Exactly! That second-monitor scenario was the one I completely missed. document.hidden felt like the right signal until I realized it was measuring visibility, not attention. Thanks for sharing the analytics perspective too!

Collapse
 
lunsy profile image
Hopeful Apprentice

Great story! I've fallen into the same trap with document.hidden – it's so tempting to "optimize" UX, but the assumption that visible tab = attentive user is dangerous. Your point about multi‑monitor setups and multiple devices is spot on. Sometimes the simplest fix is just to delete the clever code. Thanks for sharing this lesson – it's a great reminder that reliability often beats micro‑optimizations. 👏

Collapse
 
dj29 profile image
Dhruv Jani

Exactly!! document.hidden felt like such a clever little optimization until I actually thought about all the ways “visible” ≠ “user is looking”.

And yeah, deleting the clever code was probably my favorite part of the whole bug 😂 Thanks for sharing your experience too, really glad it resonated!

Collapse
 
figsh profile image
FSCSS

Helpful article ❤️

Collapse
 
tanay_dwivedi9098 profile image
Tanay Dwivedi

Nice blog explaining the bug and how you solved it with a good storytelling @dj29

Collapse
 
dj29 profile image
Dhruv Jani

Thanks bro. Actually that's the thing We do a lot of bug fixes, projects in day-to-day life but this is the platform where can talk about it and find people who will vibe with it😅

Collapse
 
syedahmershah profile image
Syed Ahmer Shah

This is a great example of how a seemingly reasonable optimization can encode the wrong assumption. document.hidden tells you visibility, not attention, and the multi-monitor case makes that distinction painfully clear. The delivery-vs-seen separation is a much more robust mental model. Great debugging story! 👏

Collapse
 
dj29 profile image
Dhruv Jani

Exactly! document.hidden looked like the perfect signal until I realized I was using it to answer a question it was never meant to answer. 😅

The delivery-vs-seen distinction is definitely something I'm taking forward from this bug.

Collapse
 
anasbuilds997 profile image
anassBld

Glad it helped! Separating delivery from the 'seen' state makes things a lot more predictable. Let me know if you end up rebuilding it, would love to see what you come up with!

Collapse
 
dj29 profile image
Dhruv Jani

Sure. I'll definitely post if I end up going back to shelftalk again to make it better.

Collapse
 
anasbuilds997 profile image
anassBld

Sounds great, Dhruv. Looking forward to reading about it if you do end up rebuilding it.

If you decide to go the receipts route, I'd love to hear how that changes the architecture for you!

Collapse
 
dj29 profile image
Dhruv Jani

One of those bugs that makes you rethink what “visible” actually means.

I thought I had a clever notification optimization. Turns out document.hidden and “the user is actually paying attention” are two very different things.

Hope this one saves someone else from making the same mistake. 🔔

Collapse
 
mudassirworks profile image
Mudassir Khan

the requireHidden pattern is a classic example of the abstraction leaking. you're assuming visibilityState maps to 'user is reading this' but it actually maps to 'this tab is rendering' — a tab can be visible to the OS but completely out of the user's attention.

we hit the inverse in a chat app. suppressed notification sounds when the tab was focused, but our check ran inside an iframe so document.hidden was always false from the iframe's perspective. spent a week thinking the sound suppression logic was broken before realizing the context was wrong.

curious what finally surfaced it. did Sentry show the visibilityState value in the error payload, or did you catch it some other way?

Collapse
 
dj29 profile image
Dhruv Jani

Thanks for reading! The interesting part was actually how the bug surfaced — I could reproduce the expected behavior locally, but it was a user mentioning their second-monitor setup that finally exposed what was really going wrong. document.hidden was answering “is this page hidden?” while I was treating it as “is the user looking?” That distinction completely got me.