Disclosure: I make SnapQuizX. This article describes my own work on the product.
I hadn't opened the code for SnapQuizX, a Chrome extension that turns a webpage into a quiz, in months. No marketing, no roadmap, nothing scheduled. When I finally looked at its numbers out of curiosity, they told two different stories at once.
The paradox in the numbers
| Metric (trailing 6 months) | Value |
|---|---|
| New installs | 263 (+39%) |
| Uninstalls | 12 |
| Weekly active users | 27 (down 7.7%) |
Installs rose while weekly active users fell. Those counts do not show whether the same people returned from one week to the next; that would require a cohort comparison.
Installs were 72% ChromeOS, while 94% of weekly active users were on Windows and macOS. These platform mixes differed; the figures alone did not tell me why.
Reading the signal correctly
I started by testing a new user's first session. The counts gave me a reason to investigate, but did not identify the cause.
Finding the suspect
Months earlier, I had touched the backend to support an unreleased mobile app version: deprecating older Cloud Functions and reshaping how usage quota was stored in Firestore. I never re-tested the original web client end to end afterward — nothing looked broken, and existing signed-in users kept working through compatibility paths already present in their data. A brand-new user, hitting the code fresh, would not have that safety net.
To check, I ran the exact path a first-time visitor would take: a clean browser profile, no existing account, straight through first quiz, save, sign-in, and the account page.
Five breakpoints, hiding in plain sight
The walkthrough surfaced five separate, unrelated failures:
- A conversion loop. Several save and notebook features gated access with
isAnonymous || planId === "anonymous". A user who had genuinely signed in with Google, but whose backend record still carried an oldplanIdvalue, was silently treated as a guest and blocked from saving anything. - A blocked second popup. Linking a guest session to an existing Google account sometimes triggered a "credential already in use" error. The fallback tried to open a second sign-in popup — which browsers block by default. The user just saw nothing happen and moved on.
- Silent 401s. Ten separate save and notebook calls had no retry logic for an expired auth token. A session open for more than a few minutes could start failing invisibly.
- A crashing account page. One quiz record's accuracy field was stored in an older shape. Reading a nested property on it crashed the entire account page for anyone with that record.
- A redirect race. The account and notebook pages sometimes decided a user wasn't authenticated before their subscription status had finished loading, and bounced paying users back to the homepage for a split second.
None of these were dramatic. Every one of them was silent — no error banner, no crash report, nothing that would surface unless you went looking for it.
The fix, and why I left the backend alone
The fix touched only the client code. Cloud Functions and Firestore rules stayed unchanged because existing users were already working with the old data shapes.
Save and notebook calls got a shared retry wrapper that refreshes an expired token once before failing. Anonymous-detection gates were narrowed to a single check.
The login fallback stopped opening the blocked second popup. Account pages got safer reads of older records and waited for subscription status before deciding to redirect.
Verification was three layers: a type check, a production build, and the same fresh-account walkthrough that found the bugs in the first place, run end to end until every step worked.
What I'm taking from this
I had not received a report that led me to these failures.
I will watch installs and weekly active users together and repeat the fresh-account walkthrough when they diverge. I have not yet measured whether these fixes change cohort retention.
I'll let the fix run for a few weeks before deciding what, if anything, comes next for SnapQuizX — the same discipline I wrote about when reviewing my whole product portfolio.