DEV Community

Alex
Alex

Posted on • Originally published at saas.pet

I just fixed a 12-day bug on saas.pet that explains why our traffic was flat

I just fixed a 12-day bug on saas.pet that explains why our traffic was flat

The /today page was showing "0 votes" on every AI tool. The data was stale since July 20, 2026.

Two bugs, one missing field

Bug 1: PH GraphQL schema changed. I was calling the standard posts query with votesCount. The schema is the same, but the API token was timing out intermittently. First run would fail, second run would succeed.

Bug 2: Field name mismatch. I was writing

{ stars: n.votesCount || 0 }
Enter fullscreen mode Exit fullscreen mode

to the data file. But the build.mjs page was reading from t.votes, not t.stars. Result: 20 Product Hunt launches rendered as "0 votes" even when the data was correct.

The fix

Two lines in fetch.mjs:

// Before
{ stars: n.votesCount || 0 }

// After
{ votes: n.votesCount || 0 }
Enter fullscreen mode Exit fullscreen mode

Plus fixing the sort order in build.mjs /today so PH launches rank above GitHub trending:

data.tools.slice().sort((a, b) => {
  const aScore = (a.source === "producthunt" ? 1000000 : 0) + (a.votes || a.stars || 0);
  const bScore = (b.source === "producthunt" ? 1000000 : 0) + (b.votes || b.stars || 0);
  return bScore - aScore;
}).slice(0, 30)
Enter fullscreen mode Exit fullscreen mode

Result

/today now shows real Product Hunt data again:

  • #1 Pazi (994 votes today)
  • #2 OpenSEO (912 votes)
  • #3 Context.dev (876 votes)
  • #4 AnySearch
  • #5 Fuzzy AI

All 20 PH launches have real votes again. Pazi, OpenSEO, Context.dev — these are tools people actually voted on today, not stale data.

Why this matters for SEO

Stale /today data was a bigger problem than I realized. Three reasons:

  1. Google fresh content signal: when /today shows the same tools for weeks, Google assumes the site is not being updated. Stale data equals lower crawl frequency.

  2. User signals: when visitors land on /today and see "0 votes" everywhere, they bounce. Low engagement equals lower rankings.

  3. AdSense review: we are in the second AdSense review now. The low value content flag can be triggered by any page that looks templated, including stale /today that has not been refreshed.

Lessons

  1. Field name changes are silent bugs. The data was correct. Votes were there. The build pipeline just wrote them to the wrong field. No error, no warning, just a page that does not work.

  2. PH GraphQL schema is stable but the API is flaky. Expect intermittent failures. Retry once.

  3. Test the rendered HTML, not the data file. The data was right. The render was wrong. Comparing data.json output to rendered HTML is the only way to catch this.

Status of saas.pet

  • 201 hand-written reviews (AdSense-compliant: title, url, rating, cat, summary, alex_take, days_used, pros, cons, sections, real_data, summary card)
  • 14 hand-written alternatives / comparisons / best pages (rewritten with real test data)
  • 230 URL sitemap (down from 345 — removed 100+ auto-generated pipeline pages)
  • AdSense appeal submitted with all metrics

Now waiting for Google to re-crawl.


If you build a similar site, the bug is: do not trust fetch.mjs to keep working. Add a /today sanity check that fails loudly when votes are all 0 for 3+ days in a row. I did not have that check, and the bug went unnoticed for 12 days.

— Alex
saas.pet
https://saas.pet

Top comments (0)