June 19, 2026 · build log
I asked Reddit what developers are quitting.
Not what they mention — what they switch off, cancel, and walk away from. I built a momentum engine that reads behavior instead of buzz, pointed it at 21 subreddits where builders, devs, data scientists and founders actually talk, and let it rank what's moving. 494 fresh signals in about two minutes, one laptop, one browser session. The lesson is the whole point: mentions lie; behavior doesn't.
Background on the system: Signals — an early-warning system for internet momentum. This post is the day I gave it a working set of eyes on Reddit.
The question
A trending dashboard counts how many times a word appears. That tells you what's loud, not what's changing. I wanted the other thing: the moment a few thousand people quietly decide to leave a tool, swap a workflow, or admit a job stopped working. So I asked a concrete question: can I read a community by its behavior changes instead of its noise — and surface the shift before it's a headline?
Not as a vibe. As a ranked list, with receipts, that I could regenerate any morning in two minutes.
Mentions lie. Behavior doesn't.
The engine doesn't score how often something is named. It scores the verbs of a decision — switched, cancelled, gave up, replaced, burned out, going broke, laid off, done with — and it weights the conversation, not the headline. Three rules carry the whole thing:
- Heat is comments, not upvotes. An upvote is a nod. A comment is someone who had to say something back. The score weights replies 3× — discussion intensity is the tell that a nerve got hit.
- Recency is a multiplier, not a filter. A surging two-day-old thread outranks a bigger one from three weeks ago. I'm hunting the shift while it's still forming, not after it's consensus.
- Behavior-change language is a bonus. A post that says someone left a tool beats a post that merely discusses it. That single rule pushes the “people are actually moving” signals to the top and drops the hot-take threads.
Final rank = discussion-heat × recency × behavior-signal, bucketed by audience (AI / tooling, developers, data, indie founders). Loud-but-static dies. Quiet-but-moving rises.
The fix nobody mentions
First run, the Reddit leg returned almost nothing. The honest reason: the public, no-login JSON that every scraper quietly depends on now answers 403 from a plain request. A lot of “social listening” is silently running on this dead pipe and falling back to whatever else it can reach — so it looks like it's working while it sees a fraction of the room.
I didn't route around it. I already run authenticated browser sessions for other work, so I pointed the engine through one of those — a real, logged-in context fetching the same endpoints. The block evaporated and the volume showed up: 494 fresh signals across 21 subreddits in one pass. The capability was never “can't reach Reddit.” It was “use the door you already have a key to.”
How it's built
The whole thing is one script and three ideas. There's no model in the loop, no API bill, no dashboard — just a logged-in browser session and an opinion about what counts.
- One authed session, reused. Open a real logged-in browser context once (saved
storage_statecookies), then fire every request through it. Sameold.reddit.com.jsonendpoints the public pipe answers with403— inside a session they answer200. ~30 calls in a single browser, about two minutes, instead of a fresh launch per query. - Two channels, not one. Pull each sub's
hot.jsonfor what's surging, and run a fixed list of behavior-change search terms (switched from,cancelled,gave up on,burned out,replaced by AI…) for what people are deciding. Momentum and demand are different reads; you want both. - Score is an opinion in three terms. Discussion-heat × recency × behavior-signal — then dedupe by permalink, drop anything older than 21 days, and bucket by sub into audiences. That's the entire ranking.
// one logged-in browser session, reused for every query
const page = await openAuthedSession(account); // saved cookies -> 200, not 403
const feed = [], pain = [];
for (const sub of SUBS) // 21 communities
feed.push(...await get(page, "/r/" + sub + "/hot.json")); // momentum
for (const verb of BEHAVIOR_QUERIES) // "switched from", "cancelled",
pain.push(...await search(page, verb)); // "gave up on", "burned out"...
// rank = discussion-heat x recency x behavior-signal
const ageDays = p => (NOW - p.created) / 86400;
const score = p =>
(p.ups + 3 * p.comments) // a reply > a nod
* Math.max(0.3, 1 - ageDays(p) / 30) // surging > settled
* (BEHAVIOR_RX.test(p.title + p.body) ? 1.6 : 1); // "I left X" > "X is..."
const signals = dedupe([...feed, ...pain])
.filter(p => ageDays(p) <= 21)
.sort((a, b) => score(b) - score(a));That's the engine. Everything clever lives in those three multipliers — weight a reply over an upvote, decay by age so a forming shift beats a settled one, and lift anything written in the language of a decision. Swap the sub list and the verb list and it reads any market you want; the ranking logic never changes.
The scoreboard
Same morning, same machine. The top of each bucket wasn't the loudest thread — it was the one where the most people were arguing about a decision:
| the signal | heat | what it's really saying |
|---|---|---|
| “Fable 5 is a preview of AI inequality” r/ClaudeAI | 6,071↑ 1,007💬 | A model launch read as a class line. 1,000+ comments of anxiety about a two-tier AI world — the highest-discussion AI take of the week. |
| “They gave Claude Code to non-coders — it broke” r/cscareerquestions | 643↑ +3,107↑ tooling | The “anyone can build now” myth hitting reality the same week a Claude Code update surged. Expertise didn't die — it got exposed. |
| “Income, not outcome” r/ExperiencedDevs + 3 more | 873↑ 284💬 ×4 | Not one post — a cluster. A 20-year dev's survival mantra, CS grads who can't break in, “why don't we unionize.” Same mood, five threads, one week. |
Read the third row again: a single viral post is an event; five at once is a trend. No keyword search hands you that — you only see it when you rank the whole room by where the heat clusters. That cluster is the signal a dashboard would have reported as three unrelated “developer” mentions.
Why I build it this way
Everyone is selling more reach — scrape more sources, count more mentions, ship a bigger dashboard. That's loudness, rented by the month, and it tells you what already happened. The cheaper, better bet is to put the intelligence in the read: a small set of behavior rules that turn a wall of posts into a ranked list of decisions people are making right now. It runs on a laptop, it costs nothing per query, and it gets sharper every time I add a rule.
Same pattern I keep coming back to: the win isn't a bigger model or a wider net — it's a smarter harness around the data you can already reach. Honest by construction: every signal here links to the live thread with its real numbers, so you can check the machine instead of trusting it.
Next, it stops being a morning read and becomes a standing producer: wire the authenticated feed in natively, run it on a cadence, and let it open a thread the moment a behavior shift crosses a bar — so the signal reaches me before it reaches the timeline.