<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Benjian Dai</title>
    <description>The latest articles on DEV Community by Benjian Dai (@benjiandai).</description>
    <link>https://dev.to/benjiandai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3893305%2Fceda5657-6b10-49d6-94b2-4e2a9f99c09e.jpeg</url>
      <title>DEV Community: Benjian Dai</title>
      <link>https://dev.to/benjiandai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/benjiandai"/>
    <language>en</language>
    <item>
      <title>How We Indexed 100,000 Community-Verified Workarounds Across Reddit &amp; Hacker News Without Getting Banned</title>
      <dc:creator>Benjian Dai</dc:creator>
      <pubDate>Mon, 09 Nov 2026 14:02:33 +0000</pubDate>
      <link>https://dev.to/benjiandai/how-we-indexed-100000-community-verified-workarounds-across-reddit-hacker-news-without-getting-6np</link>
      <guid>https://dev.to/benjiandai/how-we-indexed-100000-community-verified-workarounds-across-reddit-hacker-news-without-getting-6np</guid>
      <description>&lt;p&gt;This morning, our knowledge engine crossed a milestone: &lt;strong&gt;100,003 community-verified workarounds&lt;/strong&gt; extracted from &lt;strong&gt;328,420 real discussions&lt;/strong&gt; across Reddit, Hacker News, and StackExchange, cataloged into &lt;strong&gt;6,359 recurring human dilemmas&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When an engineer hits a wall—an obscure Docker bridge networking timeout, a broken Next.js App Router cache invalidation, an undocumented AWS IAM permission quirk, or even everyday frictions like unexpected SaaS price hikes—the real, battle-tested solution is almost never in the official marketing docs.&lt;/p&gt;

&lt;p&gt;It is buried on page 3 of an archived Reddit thread, or in comment #142 of a heated Hacker News debate.&lt;/p&gt;

&lt;p&gt;We built &lt;a href="https://saidithere.com?utm_source=devto&amp;amp;utm_medium=100k_workarounds" rel="noopener noreferrer"&gt;Said It Here&lt;/a&gt; to systematically capture this tribal knowledge: an autonomous engine that continuously listens to engineering and life discussions, clusters recurring friction points, and surfaces the workarounds that humans actually agreed on.&lt;/p&gt;

&lt;p&gt;Building a database of 100,000 consensus solutions in 2026, however, was an absolute engineering minefield.&lt;/p&gt;

&lt;p&gt;Here is the full breakdown of how we built a resilient ingestion pipeline, bypassed Reddit’s modern edge WAF without expensive proxy pools, drastically slashed LLM token costs with local heuristic gating, and clustered solutions using PostgreSQL &lt;code&gt;pgvector&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Problem: Why Web Scraping in 2026 is Broken
&lt;/h3&gt;

&lt;p&gt;If you haven't built a crawler recently, the landscape has radically changed over the past twelve months.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. The Edge WAF Wall
&lt;/h4&gt;

&lt;p&gt;Try making a standard HTTP request to any active forum endpoint today using Python &lt;code&gt;requests&lt;/code&gt; or Node &lt;code&gt;fetch&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="c1"&gt;# This will get you an instant HTTP 403 Forbidden in 2026
&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://www.reddit.com/r/webdev/new.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 403 Forbidden: {"message": "Blocked by network security"}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Platforms like Reddit have deployed aggressive edge-level Web Application Firewalls (Cloudflare/Fastly custom edge rules). They fingerprint TCP/TLS handshakes, check HTTP/2 pseudo-header order, inspect browser entropy, and aggressively drop requests to &lt;code&gt;new.json&lt;/code&gt; or &lt;code&gt;hot.json&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Residential proxy rotators can cost hundreds of dollars a month, and official developer APIs now come with severe rate limits and pricing gates that make community projects impossible.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. The 90% Noise Ratio
&lt;/h4&gt;

&lt;p&gt;Forum discussions are chaotic. For every 100 comments on a post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;60 are jokes, memes, sarcasm, or personal anecdotes.&lt;/li&gt;
&lt;li&gt;25 are people venting about the problem without offering solutions.&lt;/li&gt;
&lt;li&gt;10 are generic advice that doesn't work ("Have you tried rebooting?").&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Only 5 contain actual, verified, step-by-step workarounds.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you naively feed entire comment trees into GPT-4 or Claude 3.5 Sonnet to "summarize solutions", you will burn thousands of dollars in input tokens on pure digital noise:&lt;/p&gt;

&lt;p&gt;$$\text{328,000 comments} \times \text{250 tokens} \times \$3/\text{M tokens} = \text{Unsustainable for an indie engine}$$&lt;/p&gt;

&lt;p&gt;To make this feasible, we had to rethink the ingestion, filtering, and vector clustering architecture from the ground up.&lt;/p&gt;




&lt;h3&gt;
  
  
  The System Architecture
&lt;/h3&gt;

&lt;p&gt;Here is the high-level architecture that powers Said It Here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────────────────────────────────────────────────┐
│                          INGESTION LAYER                               │
│  [Hacker News Algolia]  [StackExchange API]  [Reddit Headed Pool]      │
│            │                     │                     │               │
│            └──────────────┬──────┴─────────────────────┘               │
│                           ▼ (top?t=day + Session Rotation)             │
│                [PostgreSQL: source_posts / source_comments]            │
└───────────────────────────┬────────────────────────────────────────────┘
                            │
┌───────────────────────────▼────────────────────────────────────────────┐
│                    EXTRACTION &amp;amp; CONSENSUS PIPELINE                     │
│  1. Fast Regex &amp;amp; Signal Filter  (Drops 75% meme/chatter noise)         │
│  2. AI Model Router             (Extracts concrete workaround steps)   │
│  3. pgvector Cosine Matcher     (&amp;lt;= 0.18 cosine distance clustering)   │
│  4. Dual-Tier Safety Moderator  (Blocks hallucinated commands / spam)  │
│                           │                                            │
│                           ▼                                            │
│           [PostgreSQL: problems &amp;amp; problem_workarounds]                 │
│                 (6,359 Problems · 100,003 Solutions)                   │
└───────────────────────────┬────────────────────────────────────────────┘
                            │
┌───────────────────────────▼────────────────────────────────────────────┐
│                      DISTRIBUTION &amp;amp; ACCESS                             │
│  • Next.js App Router UI   (Interactive Checklists &amp;amp; /compare)         │
│  • OpenAPI v1 Endpoint     (/api/v1/problems/search)                   │
│  • Remote MCP SSE Server   (/api/mcp for Cursor &amp;amp; Claude)              │
│  • AI Search Optimization  (/llms.txt standard for Perplexity/ChatGPT) │
└────────────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's examine the four critical engineering breakthroughs that made this work.&lt;/p&gt;




&lt;h3&gt;
  
  
  Breakthrough 1: Multi-Account Session Hydration
&lt;/h3&gt;

&lt;p&gt;Instead of relying on unstable headless scrapers or high-risk browser automation that triggers Cloudflare Turnstile challenges, we implemented an interactive session hydrator using Playwright Chromium:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Interactive Windows Launcher]
       │
       ▼ (Launches headed Chromium with proxy support)
[User logs in once to authentic Reddit account]
       │
       ▼ (Extracts storage_state: cookies, tokens, origins)
[Stored into reddit_auth_pool.json]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each account's browser storage state (session cookies, CSRF tokens, local storage) is serialized into an isolated credential pool. When the background worker daemon runs, it loads these pre-authenticated contexts into memory.&lt;/p&gt;

&lt;p&gt;To prevent any single account from hitting Reddit's 429 throttling, we implemented an exhaustive round-robin rotator with persistent state across process cycles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Simplified snippet from our local collector (worker/src/worker/collectors/reddit.py)
&lt;/span&gt;&lt;span class="n"&gt;_LAST_ACCOUNT_INDEX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_with_account_pool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;account_pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;AccountContext&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;_LAST_ACCOUNT_INDEX&lt;/span&gt;
    &lt;span class="n"&gt;total_accounts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account_pool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_accounts&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;account_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_LAST_ACCOUNT_INDEX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;total_accounts&lt;/span&gt;
        &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;account_pool&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;account_idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# If throttled or challenged, rotate immediately to next account
&lt;/span&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Account &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; throttled (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;). Rotating...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="c1"&gt;# Successfully fetched; persist rotation pointer for next task
&lt;/span&gt;            &lt;span class="n"&gt;_LAST_ACCOUNT_INDEX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account_idx&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;total_accounts&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Network error on account &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;All accounts in pool exhausted or rate-limited.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If one account encounters a temporary 429 backoff, the collector seamlessly rotates to the next verified account context without dropping the task.&lt;/p&gt;




&lt;h3&gt;
  
  
  Breakthrough 2: The &lt;code&gt;top?t=day&lt;/code&gt; Inflow Discovery
&lt;/h3&gt;

&lt;p&gt;One of our most interesting operational discoveries was &lt;strong&gt;endpoint classification&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When querying subreddit feeds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;https://www.reddit.com/r/{sub}/new.json&lt;/code&gt; triggers an immediate &lt;strong&gt;HTTP 403 &lt;code&gt;blocked by network security&lt;/code&gt;&lt;/strong&gt; from Reddit's edge WAF. Reddit heavily monitors &lt;code&gt;/new.json&lt;/code&gt; because almost all brute-force scrapers monitor newly posted items.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://www.reddit.com/r/{sub}/hot.json&lt;/code&gt; frequently triggers dynamic JavaScript challenge walls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, when we shifted our collection query to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /r/{sub}/top.json?t=day&amp;amp;limit=20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference was night and day:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Edge CDN Alignment&lt;/strong&gt;: &lt;code&gt;top.json?t=day&lt;/code&gt; is heavily cached at Reddit's CDN edge. Requesting it looks virtually identical to an active mobile user refreshing their daily feed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signal-to-Noise Multiplier&lt;/strong&gt;: A post that reached the top of a subreddit over the past 24 hours has already undergone human moderation and community upvoting. You capture zero spam posts, and the comments already contain upvoted solutions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Blocks&lt;/strong&gt;: Since deploying this change across 100+ targeted subreddits, our crawler has experienced &lt;strong&gt;zero 403 WAF blocks&lt;/strong&gt; across over 86,000 ingested posts.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Breakthrough 3: Slashing LLM Token Costs with Heuristic Gating
&lt;/h3&gt;

&lt;p&gt;Once comments are ingested into PostgreSQL, how do you extract clean, actionable workarounds without going bankrupt on LLM API calls?&lt;/p&gt;

&lt;p&gt;We built a two-stage consensus extractor:&lt;/p&gt;

&lt;h4&gt;
  
  
  Stage 1: The Fast Signal Filter (Local Python)
&lt;/h4&gt;

&lt;p&gt;Before sending any text to an LLM, a local pre-filter evaluates comment syntax:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discards comments shorter than 20 characters or longer than 2,000 characters.&lt;/li&gt;
&lt;li&gt;Strips automated bot responses (e.g., AutoModerator notices).&lt;/li&gt;
&lt;li&gt;Applies heuristic pattern scoring looking for solution markers:

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;"The fix is to..."&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Workaround that worked for me:..."&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Don't use X, instead configure Y in..."&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Added &lt;code&gt;--disable-gpu&lt;/code&gt; to &lt;code&gt;config.json&lt;/code&gt; and it resolved..."&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This eliminates over &lt;strong&gt;75% of conversational chatter&lt;/strong&gt; instantly at 0 API cost.&lt;/p&gt;

&lt;h4&gt;
  
  
  Stage 2: AI Problem &amp;amp; Workaround Extraction
&lt;/h4&gt;

&lt;p&gt;Only high-signal comment clusters are dispatched to the AI Model Router (&lt;code&gt;web_ai_server&lt;/code&gt;). The prompt instructs the model to return strictly structured JSON:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Canonical Dilemma Title&lt;/strong&gt;: (e.g., &lt;em&gt;"Why does Docker on macOS experience severe I/O degradation on mounted volumes?"&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concrete Workaround&lt;/strong&gt;: (e.g., &lt;em&gt;"Enable VirtioFS in Docker Desktop settings instead of default gRPC FUSE"&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why It Works&lt;/strong&gt;: Technical rationale behind the fix.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Breakthrough 4: Clustering Dilemmas with pgvector
&lt;/h3&gt;

&lt;p&gt;In the real world, different developers phrase the exact same problem in dozens of ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;"Docker for Mac file sync is painfully slow"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"High CPU usage with volume mounts on Docker Desktop"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"Improving volume bind mount performance on OSX"&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you create a new database entry for every variation, your knowledge base becomes fragmented and useless.&lt;/p&gt;

&lt;p&gt;We solve this using &lt;strong&gt;PostgreSQL with &lt;code&gt;pgvector&lt;/code&gt; 0.8.7&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Schema definition for semantic dilemma clustering&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;problems&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;title_embedding&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'published'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;evidence_count&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="nb"&gt;TIME&lt;/span&gt; &lt;span class="k"&gt;ZONE&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Fast cosine distance index&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;problems&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;ivfflat&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title_embedding&lt;/span&gt; &lt;span class="n"&gt;vector_cosine_ops&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a newly extracted dilemma arrives, we compute its embedding and run a cosine similarity query against existing published problems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title_embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;problems&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'published'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;title_embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If &lt;code&gt;distance &amp;lt;= 0.18&lt;/code&gt;&lt;/strong&gt;: The dilemma is semantically identical to an existing problem. The new post and its verified workaround are appended to the existing record as fresh evidence, increasing its confidence score.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If &lt;code&gt;distance &amp;gt; 0.18&lt;/code&gt;&lt;/strong&gt;: The problem represents a novel dilemma. A new canonical slug is generated and registered in the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This clustering engine allows our database to compact &lt;strong&gt;328,420 messy discussion comments into 6,359 clean, canonical problems&lt;/strong&gt; and &lt;strong&gt;100,003 specific workarounds&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Opening the Knowledge Base to Developers &amp;amp; Agents
&lt;/h3&gt;

&lt;p&gt;Data trapped in a silo is useless. Once we reached 100,000 solutions, we focused on making this knowledge immediately accessible to both human developers and autonomous AI agents.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Public JSON API v1 (Zero-Auth, Open CORS)
&lt;/h4&gt;

&lt;p&gt;Developers can query the entire consensus database programmatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://saidithere.com/api/v1/problems/search?q=docker"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"results"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"slug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fixing-slow-file-sharing-performance-in-docker-on-macos"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Why is file sharing so slow in Docker on macOS?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"evidence_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;94&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"workarounds_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://saidithere.com/problems/fixing-slow-file-sharing-performance-in-docker-on-macos"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Zero-Install Remote MCP for Cursor &amp;amp; Claude
&lt;/h4&gt;

&lt;p&gt;Instead of forcing developers to context-switch to a search engine when an IDE error occurs, we deployed a hosted &lt;strong&gt;Remote Model Context Protocol (MCP)&lt;/strong&gt; server over Server-Sent Events (SSE).&lt;/p&gt;

&lt;p&gt;In Cursor or Claude Desktop, you just add one URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"saidithere"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://saidithere.com/api/mcp"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when you ask your coding assistant: &lt;em&gt;"I'm getting this weird Postgres connection pooling error in Next.js 15, has anyone found a workaround?"&lt;/em&gt;, your AI automatically queries Said It Here and cites verified community consensus.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. AI Search Optimization with &lt;code&gt;llms.txt&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;To ensure answer engines like Perplexity, ChatGPT Search, and Claude can discover and cite these solutions cleanly, we implemented the open &lt;a href="https://saidithere.com/llms.txt" rel="noopener noreferrer"&gt;&lt;code&gt;/llms.txt&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://saidithere.com/llms-full.txt" rel="noopener noreferrer"&gt;&lt;code&gt;/llms-full.txt&lt;/code&gt;&lt;/a&gt; specification.&lt;/p&gt;

&lt;p&gt;It provides a concise markdown index of all 6,000+ problems, 40 software entity hubs (Docker, AWS, Git, Stripe, Kubernetes), and comparison matchups, with zero HTML bloat.&lt;/p&gt;




&lt;h3&gt;
  
  
  What 100,000 Workarounds Taught Us
&lt;/h3&gt;

&lt;p&gt;Building this system taught us a few enduring lessons about developer data:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The best technical documentation is conversational.&lt;/strong&gt; Official docs tell you how a system was &lt;em&gt;intended&lt;/em&gt; to work; forum discussions tell you how it &lt;em&gt;actually fails in production&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never fight the WAF head-on.&lt;/strong&gt; If a scraper is getting 403 errors, rotating proxies is often the wrong fix. Look at your query endpoint: switching from &lt;code&gt;/new.json&lt;/code&gt; to &lt;code&gt;/top.json?t=day&lt;/code&gt; was 100x more effective than buying expensive proxy IPs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector embeddings are the ultimate deduplication tool.&lt;/strong&gt; String matching or keyword search will fail when developers describe the same bug using completely different jargon. Cosine similarity over vector space solves this effortlessly.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Try It Out
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Explore the 100k database: &lt;a href="https://saidithere.com?utm_source=devto&amp;amp;utm_medium=100k_workarounds" rel="noopener noreferrer"&gt;saidithere.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Check out the API documentation: &lt;a href="https://saidithere.com/developers" rel="noopener noreferrer"&gt;/developers&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Read our machine-readable index: &lt;a href="https://saidithere.com/llms.txt" rel="noopener noreferrer"&gt;/llms.txt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Connect to Remote MCP in Cursor: &lt;code&gt;https://saidithere.com/api/mcp&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're building automated crawlers, LLM extraction pipelines, or MCP servers in 2026, I’d love to hear how you're handling anti-bot systems and token economics in the comments!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>How I built a zero-install Remote MCP Server for Cursor &amp; Claude (and why local npm packages suck)</title>
      <dc:creator>Benjian Dai</dc:creator>
      <pubDate>Tue, 27 Oct 2026 13:00:48 +0000</pubDate>
      <link>https://dev.to/benjiandai/how-i-built-a-zero-install-remote-mcp-server-for-cursor-claude-and-why-local-npm-packages-suck-fel</link>
      <guid>https://dev.to/benjiandai/how-i-built-a-zero-install-remote-mcp-server-for-cursor-claude-and-why-local-npm-packages-suck-fel</guid>
      <description>&lt;p&gt;Anthropic’s &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; is arguably the most exciting architectural standard to hit developer tooling in the last twelve months. It gives AI coding assistants—like Cursor, Claude Desktop, Windsurf, and Cline—a standardized way to reach into databases, APIs, and tools.&lt;/p&gt;

&lt;p&gt;Yet, if you’ve actually tried installing third-party MCP servers, you’ve probably hit the dirty secret of the current ecosystem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The developer experience of local stdio packages is surprisingly painful.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To install a typical community MCP server today, you must:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have Node.js or Python pre-configured on your host machine.&lt;/li&gt;
&lt;li&gt;Manually edit an obscure JSON config file (&lt;code&gt;claude_desktop_config.json&lt;/code&gt; or Cursor settings).&lt;/li&gt;
&lt;li&gt;Spawn processes via &lt;code&gt;npx -y @some/package&lt;/code&gt; that hang on slow connections or Windows shell quirks.&lt;/li&gt;
&lt;li&gt;Deal with zombie processes running in the background when your IDE restarts.&lt;/li&gt;
&lt;li&gt;Manually remember to update package versions when upstream APIs change.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is a much better way: &lt;strong&gt;Remote MCP over Server-Sent Events (SSE)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead of running an arbitrary executable on your local laptop, you give your IDE a single URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"saidithere"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://saidithere.com/api/mcp"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zero installs. No Node runtime required. Instant updates. It just works across macOS, Linux, and Windows.&lt;/p&gt;

&lt;p&gt;Here is why I built a Remote MCP server for &lt;a href="https://saidithere.com?utm_source=devto&amp;amp;utm_medium=mcp_showdev" rel="noopener noreferrer"&gt;Said It Here&lt;/a&gt;, how the SSE protocol actually works under the hood, and how you can implement one in TypeScript with Next.js App Router.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Motivation: Solving AI Coding Hallucinations with Human Consensus
&lt;/h3&gt;

&lt;p&gt;When your AI assistant hits an edge case—an obscure Next.js cache bug, an AWS IAM permission quirk, or a broken dependency update—LLMs love to hallucinate. They confidently invent parameters that don't exist, recommend deprecated packages, or send you down a 3-hour rabbit hole.&lt;/p&gt;

&lt;p&gt;Meanwhile, hundreds of real engineers on Reddit (r/experienceddevs, r/webdev, r/sysadmin) and Hacker News have usually banged their heads against the exact same wall and posted battle-tested workarounds.&lt;/p&gt;

&lt;p&gt;I created &lt;a href="https://saidithere.com?utm_source=devto&amp;amp;utm_medium=mcp_showdev" rel="noopener noreferrer"&gt;Said It Here&lt;/a&gt; to aggregate these recurring engineering difficulties across developer forums and index the workarounds that humans actually tried.&lt;/p&gt;

&lt;p&gt;Instead of forcing developers to context-switch to a browser and sift through Google search results (which are now polluted with SEO spam), I wanted &lt;strong&gt;Cursor and Claude to query human consensus directly from inside the IDE&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To do that, we needed an MCP server that any developer could connect to in 10 seconds.&lt;/p&gt;




&lt;h3&gt;
  
  
  How Remote MCP Works Under the Hood
&lt;/h3&gt;

&lt;p&gt;Under the official MCP specification, an SSE transport works through a bi-directional handshake:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Cursor / Claude IDE]                      [Next.js Remote MCP Server]
         |                                               |
         | ---- 1. GET /api/mcp (text/event-stream) ---&amp;gt; | (Establishes persistent SSE)
         | &amp;lt;--- 2. event: endpoint, data: /api/mcp?... - | (Returns unique sessionId)
         |                                               |
         | ---- 3. POST /api/mcp?sessionId=... --------&amp;gt; | (Sends JSON-RPC "initialize")
         | &amp;lt;--- 4. event: message, data: {result: ...} - | (Streamed back over SSE)
         |                                               |
         | ---- 5. POST /api/mcp?sessionId=... --------&amp;gt; | (Sends JSON-RPC "tools/call")
         | &amp;lt;--- 6. event: message, data: {result: ...} - | (Streamed back over SSE)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Persistent SSE Stream&lt;/strong&gt;: The client opens an HTTP &lt;code&gt;GET&lt;/code&gt; request with &lt;code&gt;Accept: text/event-stream&lt;/code&gt;. The server keeps this connection open indefinitely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endpoint Discovery&lt;/strong&gt;: As soon as the connection opens, the server generates a cryptographically random &lt;code&gt;sessionId&lt;/code&gt; and sends an initial &lt;code&gt;endpoint&lt;/code&gt; event:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   event: endpoint
   data: /api/mcp?sessionId=3a8f1b...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;JSON-RPC Over HTTP POST&lt;/strong&gt;: The client uses standard HTTP &lt;code&gt;POST&lt;/code&gt; requests to send JSON-RPC 2.0 payloads (&lt;code&gt;initialize&lt;/code&gt;, &lt;code&gt;tools/list&lt;/code&gt;, &lt;code&gt;tools/call&lt;/code&gt;, &lt;code&gt;ping&lt;/code&gt;) to the endpoint provided.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous Streaming&lt;/strong&gt;: The server processes the request and streams the response back to the client over the already-open SSE pipe.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Implementing Remote MCP in Next.js App Router
&lt;/h3&gt;

&lt;p&gt;Here is a simplified look at how we implemented this in Next.js (&lt;code&gt;web/app/api/mcp/route.ts&lt;/code&gt;):&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Managing Active SSE Sessions
&lt;/h4&gt;

&lt;p&gt;We use a simple in-memory session registry with automated TTL cleanup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Active session tracking&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;McpSession&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WritableStreamDefaultWriter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sessions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;McpSession&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;McpSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`event: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\ndata: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;\n\n`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. The &lt;code&gt;GET&lt;/code&gt; Handler (Establishing the Stream)
&lt;/h4&gt;

&lt;p&gt;When Cursor connects, we create a &lt;code&gt;TransformStream&lt;/code&gt;, emit the &lt;code&gt;endpoint&lt;/code&gt; URI, and launch a periodic keepalive loop to prevent Cloudflare or reverse proxies from killing the connection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;writable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TransformStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getWriter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;McpSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Send the required endpoint URI event&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;endpointUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`/api/mcp?sessionId=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;endpoint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;endpointUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 15-second keepalive comment to defeat proxy idle timeouts&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keepAliveTimer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;: keepalive&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keepAliveTimer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;15000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;readable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/event-stream&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-cache, no-transform&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Connection&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keep-alive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. The &lt;code&gt;POST&lt;/code&gt; Handler (Dispatching Tool Calls)
&lt;/h4&gt;

&lt;p&gt;When Cursor calls a tool—for instance, &lt;code&gt;search_problems&lt;/code&gt;—it sends a standard JSON-RPC 2.0 message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sessionId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Session not found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rpc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tools/list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;search_problems&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Search recurring software dilemmas and human-verified workarounds&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;q&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_problem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Get full community consensus and workarounds by slug or URL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;slug&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tools/call&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Execute search logic against read-pool...&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resultText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;executeTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resultText&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;202&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Real-World Proof: Live Third-Party Integration
&lt;/h3&gt;

&lt;p&gt;The beauty of exposing a standard Remote MCP &amp;amp; REST API is how quickly other developers can build on it.&lt;/p&gt;

&lt;p&gt;Earlier this week, a fellow developer—&lt;strong&gt;omniuni&lt;/strong&gt; (creator of &lt;code&gt;tinai.online&lt;/code&gt;, with over 310k karma on Reddit)—integrated our consensus API into production. Their AI assistant platform now features a dedicated &lt;strong&gt;"Consensus"&lt;/strong&gt; tool that lets users across Claude 3.5, Gemini 1.5, and DeepSeek cross-reference code solutions against real Reddit and Hacker News discussions, complete with direct backlink citations.&lt;/p&gt;

&lt;p&gt;When you remove the friction of downloading node packages and compiling binaries, integrations go from "maybe next weekend" to "deployed before lunch."&lt;/p&gt;




&lt;h3&gt;
  
  
  Try It in Cursor in 30 Seconds
&lt;/h3&gt;

&lt;p&gt;You don’t need an API key, you don’t need an account, and you don’t need to &lt;code&gt;npm install&lt;/code&gt; anything.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;Cursor Settings&lt;/strong&gt; (&lt;code&gt;Cmd + ,&lt;/code&gt; or &lt;code&gt;Ctrl + ,&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Features&lt;/strong&gt; -&amp;gt; &lt;strong&gt;MCP Servers&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Add New MCP Server&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Fill in:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt;: &lt;code&gt;saidithere&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type&lt;/strong&gt;: &lt;code&gt;sse&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server URL&lt;/strong&gt;: &lt;code&gt;https://saidithere.com/api/mcp&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Or paste directly into your &lt;code&gt;.cursor/mcp.json&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="nl"&gt;"saidithere"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://saidithere.com/api/mcp"&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, open your Cursor Composer or Chat and ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"What are the most common community-verified workarounds for handling unsustainable developer workloads?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cursor will reach out to the remote MCP server, pull human consensus from real forum discussions, and cite the actual sources.&lt;/p&gt;




&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Remote MCP is the future of assistant tooling&lt;/strong&gt;: Local stdio servers are great for reading local files, but for data services, search, and knowledge bases, SSE endpoints eliminate 95% of user friction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keepalives are non-negotiable&lt;/strong&gt;: If your remote MCP server runs behind Cloudflare, AWS ALB, or Nginx, you must stream periodic &lt;code&gt;: keepalive\n\n&lt;/code&gt; comments, or your connection will silently drop every 60 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open APIs + MCP creates viral loops&lt;/strong&gt;: By pairing our public &lt;a href="https://saidithere.com/developers?utm_source=devto&amp;amp;utm_medium=mcp_showdev" rel="noopener noreferrer"&gt;REST API and Remote MCP&lt;/a&gt; with our recent release of &lt;a href="https://saidithere.com/problems/keeping-up-with-a-growing-workload?utm_source=devto&amp;amp;utm_medium=mcp_showdev" rel="noopener noreferrer"&gt;Reader-Contributed Workarounds&lt;/a&gt;, developers can both consume consensus and contribute their own solutions directly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What MCP servers have you connected to your IDE? Are you hosting them locally or remotely? Drop your thoughts below!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>ai</category>
      <category>cursor</category>
      <category>typescript</category>
    </item>
    <item>
      <title>I read 500+ developer threads on Reddit and Hacker News. Here is what engineers are actually exhausted by in 2026.</title>
      <dc:creator>Benjian Dai</dc:creator>
      <pubDate>Wed, 14 Oct 2026 06:27:45 +0000</pubDate>
      <link>https://dev.to/benjiandai/i-read-500-developer-threads-on-reddit-and-hacker-news-here-is-what-engineers-are-actually-25ec</link>
      <guid>https://dev.to/benjiandai/i-read-500-developer-threads-on-reddit-and-hacker-news-here-is-what-engineers-are-actually-25ec</guid>
      <description>&lt;p&gt;Every tech podcast and conference keynote in 2026 is talking about AI agents, autonomous coding, and brand-new JavaScript frameworks.&lt;/p&gt;

&lt;p&gt;Yet, if you spend an hour reading where working engineers actually hang out—places like r/experienceddevs, Hacker News, r/sysadmin, and Stack Exchange—the conversation looks completely different. &lt;/p&gt;

&lt;p&gt;Nobody is complaining that their linter is 5% too slow. They are exhausted by something much deeper.&lt;/p&gt;

&lt;p&gt;Over the past few months, I built a public scraper and indexing pipeline to collect recurring difficulties that real people bring up repeatedly across public technical forums. After analyzing over 500+ active discussion threads across half a dozen communities, four distinct patterns kept resurfacing.&lt;/p&gt;

&lt;p&gt;Here is what working engineers and tech leads are actually struggling with right now.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. The Administrative Tracker Trap
&lt;/h3&gt;

&lt;p&gt;One of the most upvoted confessions on engineering forums this year came from a senior technical lead:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"I spend 2/5 of my work week just doing reporting busywork. Not creating anything of value, not talking to customers, not unblocking my team… Just updating trackers so leadership can feel like things are under control."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is an escalating rift between engineering teams and modern management tooling. As corporate dashboards get prettier, the burden of feeding them multiplies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filling out daily Jira velocity forecasts&lt;/li&gt;
&lt;li&gt;Duplicating updates across Slack, Confluence, and weekly slide decks&lt;/li&gt;
&lt;li&gt;Explaining why a ticket took 4 story points instead of 3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Engineers aren't burned out by complex algorithms or tricky distributed systems bugs. They are burned out by &lt;strong&gt;ceremony replacing engineering&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. "Rock the boat, just don't make waves"
&lt;/h3&gt;

&lt;p&gt;Another recurring theme with hundreds of community upvotes centers on the illusion of engineering autonomy:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"When I started, I was told I had full control to modernize our stack and clean up technical debt. When I actually started righting wrongs and fixing obvious inefficiencies, it made people uncomfortable. I was literally pulled into a 1:1 and told: 'You can rock the boat, just don't make waves.'"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Many senior engineers find themselves trapped in a political paradox: hired specifically to "innovate," but penalized the moment they touch legacy decisions or call out wasteful vendor contracts. &lt;/p&gt;

&lt;p&gt;When organizations claim they want proactive problem-solvers but actually reward passive compliance, high-performers either quiet quit or leave entirely.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. The Cognitive Cost of "Concurrent Multitasking"
&lt;/h3&gt;

&lt;p&gt;In our database, one of the single most widely discussed topics—spanning 6 distinct forums and over 32 separate in-depth discussions—is &lt;a href="https://saidithere.com/problems/managing-an-unsustainable-workload?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=dev_exhaustion" rel="noopener noreferrer"&gt;managing unsustainable workloads&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The issue is rarely having &lt;em&gt;one&lt;/em&gt; hard project. It’s having four medium projects simultaneously:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context switching between completely different codebases every 45 minutes&lt;/li&gt;
&lt;li&gt;Attending 4 different daily standups&lt;/li&gt;
&lt;li&gt;Getting pinged on Slack about an urgent hotfix for Project A while deep in debugging Project C&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The consensus workaround tested by senior engineers isn't "work faster"—it's &lt;strong&gt;deliberately establishing a visible single-queue policy&lt;/strong&gt; or forcing management to explicitly decide which project is allowed to slip. When everything is priority zero, nothing gets finished.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. The Degradation of Vendor Support into "AI Chatbot Purgatory"
&lt;/h3&gt;

&lt;p&gt;Perhaps the most visceral frustration of 2026 is how impossible it has become to reach a human who actually understands the platform when something breaks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"$33k a year for an enterprise support tier, and I still have to fight through a useless AI chatbot that hallucinates docs and gives me solutions I already tried an hour ago."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Software developers and sysadmins have always tolerated price hikes if the tooling delivered reliability. But as enterprise vendors replace knowledgeable Tier-1 and Tier-2 engineers with generic LLM customer service bots, debugging upstream outages has turned into an infuriating guessing game.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why I Started Indexing This
&lt;/h3&gt;

&lt;p&gt;Google Search used to be great for finding people who had the exact same technical struggle. Today, searching for a real workplace dilemma or software glitch gives you 10 pages of AI-generated content marketing and sponsored affiliate lists that answer nothing.&lt;/p&gt;

&lt;p&gt;That’s why I started building &lt;a href="https://saidithere.com?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=dev_exhaustion" rel="noopener noreferrer"&gt;Said It Here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The premise is deliberately simple and retro:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We read public technical and workplace forums every day.&lt;/li&gt;
&lt;li&gt;A difficulty only gets a page if &lt;strong&gt;multiple different people&lt;/strong&gt; across different venues describe having the exact same issue.&lt;/li&gt;
&lt;li&gt;Every page lists their exact words with links back to the original threads, plus every workaround that people in those threads actually attempted.&lt;/li&gt;
&lt;li&gt;No sponsored placements, no affiliate links, no fake reviews.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We aren't claiming those workarounds are silver bullets—we just count who tried what, so you don't feel like you're the only one dealing with a broken system.&lt;/p&gt;




&lt;h3&gt;
  
  
  How about you?
&lt;/h3&gt;

&lt;p&gt;If you're an engineer, manager, or builder: &lt;strong&gt;Which of these hits closest to home right now?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Are you spending more time writing software or feeding tracking spreadsheets? Drop your thoughts below—I'd love to hear how your team is handling it.&lt;/p&gt;

</description>
      <category>career</category>
      <category>productivity</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>I shipped three fixes to my product in seven days. All three came from readers.</title>
      <dc:creator>Benjian Dai</dc:creator>
      <pubDate>Sun, 31 May 2026 05:08:02 +0000</pubDate>
      <link>https://dev.to/benjiandai/i-shipped-three-fixes-to-my-product-in-seven-days-all-three-came-from-readers-2n8f</link>
      <guid>https://dev.to/benjiandai/i-shipped-three-fixes-to-my-product-in-seven-days-all-three-came-from-readers-2n8f</guid>
      <description>&lt;p&gt;Last week I wrote about running my own idea-validation product (MonetScope) through its own validator. The verdict came back PIVOT, 68% confidence. The post is &lt;a href="https://dev.to/benjiandai/i-ran-my-idea-validation-product-through-its-own-validator-the-verdict-was-pivot-dea"&gt;here&lt;/a&gt; if you missed it.&lt;/p&gt;

&lt;p&gt;In the seven days since, four readers gave substantive feedback across IH, X, and email. Three of them produced shippable product fixes inside that same week. None of them came from a feature spec. They came from people who read the verdict, sat with it, and pointed at things I had not seen.&lt;/p&gt;

&lt;p&gt;This is the writeup of those three ships. What broke, what changed, and the one pattern that ties them together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 1: A reader said my measurement was wrong
&lt;/h2&gt;

&lt;p&gt;Two days after the post, a founder named Shota left this comment on the IH version:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The "zero direct WTP mentions" critique is the one I'd take most seriously. In my experience scraping Reddit/HN pain points, people complain about workflows in granular detail but almost never volunteer price anchors unprompted. Absence of WTP language is more a corpus artifact than a demand signal. One way to recover that signal without changing your scrape: look for adjacent commercial intent phrases ("I'd pay for", "would buy", mentions of existing paid tools they're frustrated with) and treat those as proxy WTP evidence. Pivot is a strong word for a 68% confidence score with that kind of measurement gap.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He was right about the methodology. The original Pro Validate WTP score scanned the corpus for five explicit phrases like "I'd pay for" and "shut up and take my money." Those phrases almost never surface unprompted on forums. A score that returns 0/34 quotes in those terms is mostly telling you about forum etiquette, not about willingness to pay.&lt;/p&gt;

&lt;p&gt;I shipped the fix on day three: added an adjacent commercial intent layer with 23 proxy phrases like "currently paying", "would buy", "switched from", "I'm paying $X for". Updated the AI prompt to treat WTP as a strong negative signal only when both channels (direct + adjacent) come back low. If direct = 0 but adjacent &amp;gt; 0, that is corpus artifact, not absence of demand.&lt;/p&gt;

&lt;p&gt;Then I re-ran the same MonetScope self-validation with the corrected methodology.&lt;/p&gt;

&lt;p&gt;The verdict went from PIVOT 68% to &lt;strong&gt;PIVOT 75%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That was not what Shota or I expected. The hypothesis behind the fix was that adjacent intent would recover hidden demand signal and the verdict might soften. Instead the corrected scoring confirmed that both channels were sparse in our corpus, which ruled out the corpus-artifact explanation entirely. The PIVOT got more confident, not less.&lt;/p&gt;

&lt;p&gt;This is the result I actually want from a methodology fix. The day a fix flips PIVOT to PROCEED on the same input is the day the tool is too eager to please. A fix that strengthens a negative verdict is how you start trusting the measurement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 2: A reader said the verdict needed glue
&lt;/h2&gt;

&lt;p&gt;On X, around the time of the post, a founder named Niki replied to one of the build-in-public threads about the case study:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;33% activation is a useful signal. Now the question is whether the activated users reached enough value to justify paying, or just tried the feature once.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I answered with the actual numbers (3 of 9 users had touched a paid feature, exactly once each), she came back with this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sometimes the leak isn't the result itself, it's whether the result makes the next step feel obvious enough.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sentence named the bug I had been staring at without seeing.&lt;/p&gt;

&lt;p&gt;Pro Validate produces a verdict, four key reasons, four card-level analyses, and a Validation Playbook with six to nine prioritized actions. The user reads it all in one sitting. Then they close the tab. The 33% activation rate in our data isn't an activation problem in the normal funnel sense. It's a problem of the Playbook being trapped on a web page nobody returns to.&lt;/p&gt;

&lt;p&gt;Three days from her reply to ship. The fix is a button that emails the full report to the user's inbox: verdict, key reasons, card highlights, and the entire Playbook with priority colors and linked assumptions. Backend renders HTML (not PDF, mobile compatibility matters more than print fidelity). Frontend has a four-state button with idle/sending/sent/error transitions. Rate limit 5 per hour per user.&lt;/p&gt;

&lt;p&gt;Why email and not the more obvious answers like push notifications or in-app reminders: email lives in an inbox the user already returns to. The web report dies when the tab closes. The cheapest fix that survives the "user closes tab and never comes back" failure mode wins, and I was not going to build a notification system to find out whether the underlying intuition was right.&lt;/p&gt;

&lt;p&gt;The total cycle was about 65 hours from her X reply to ship and a follow-up reply on the same thread confirming what had landed. That cycle time matters more than the polish of the fix. Readers who see their feedback turn into shipped code inside three days give sharper feedback the next round.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 3: A reader said my landing copy was being misread
&lt;/h2&gt;

&lt;p&gt;Days before the original case study post went out, a new signup had emailed me back after I sent the standard founder check-in. She had been on the platform less than 24 hours and had completely misread what it does. Her first reply:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I actually joined the platform as a founder/seller. I'm currently exploring opportunities to sell or present my AI governance project and connect with potential buyers or interested partners.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I asked what specifically had made her think MonetScope was a marketplace. Her reply was the cleanest landing-copy audit I have ever received from a stranger:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the wording around "opportunities" and the overall presentation gave me the impression that the platform could also help founders connect with potential buyers, partners, or commercialization opportunities for their projects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The word "opportunities" was doing the wrong work in the buyer's head. Buyers parsed it as "commercialization opportunities for my project." We meant it as "validated user pain signals." Same word, completely different semantic register.&lt;/p&gt;

&lt;p&gt;But the real surprise was what the fix exposed. MonetScope actually has two product modes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Validate one idea, get a verdict. Snapshot. Single moment.&lt;/li&gt;
&lt;li&gt;Monitor an opportunity for 30 days, get daily trend deltas and weekly competitor refresh. Stream. Continuous.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Mode 2 had been running in production for months. The landing page only showed Mode 1.&lt;/p&gt;

&lt;p&gt;The ship took most of a morning. Replaced "12,000+ opportunities" with "12,000+ validated pain signals" across the hero and Validate section. Added a new Monitor section: headline "Validate is the snapshot. Monitor is the stream." Reframed the hero badge from "AI-Curated Startup Opportunities" to "Continuous Market Signal Intelligence." Updated the Pricing compare table so 30-day continuous tracking is now a visible product line, not a footnote.&lt;/p&gt;

&lt;p&gt;The fix was almost entirely copy. The underlying product was already there. What was missing was the buyer being able to see it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this taught me about build-in-public
&lt;/h2&gt;

&lt;p&gt;Three readers, three completely different bug types:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Reader&lt;/th&gt;
&lt;th&gt;Bug type&lt;/th&gt;
&lt;th&gt;Reader profile&lt;/th&gt;
&lt;th&gt;Where they caught it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shota&lt;/td&gt;
&lt;td&gt;Methodology&lt;/td&gt;
&lt;td&gt;Builder peer running adjacent product&lt;/td&gt;
&lt;td&gt;IH comment on the original post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Niki&lt;/td&gt;
&lt;td&gt;UX / activation&lt;/td&gt;
&lt;td&gt;Founder fluent in product mechanics&lt;/td&gt;
&lt;td&gt;X reply during the case study week&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The new signup&lt;/td&gt;
&lt;td&gt;Positioning&lt;/td&gt;
&lt;td&gt;Actual user, less than 24 hours into the product&lt;/td&gt;
&lt;td&gt;Direct email after she misread the landing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;None of these three readers could have caught the other two bugs. Shota would not have flagged the "opportunities" misread because he understood the product too well. The new signup would not have audited the WTP scoring because she does not look at the code. Niki would not have written the landing copy audit because that is not the lens she reads with.&lt;/p&gt;

&lt;p&gt;The reason build-in-public actually works, in my experience, is not audience growth or marketing reach. It is that external readers, in aggregate, catch bugs that no internal team can, partitioned by what kind of reader each person is. You cannot manufacture this internally because you are exactly one reader type.&lt;/p&gt;

&lt;p&gt;One last data point. A separate user (running parallel work on her own product, not these three) summarized the week back to me in a DM:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The verdict was the conversation starter, not the conclusion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;She had taken a PIVOT verdict on her own idea and spent the following eight days running a structured distribution experiment that disproved her own targeting assumption. She did not kill the idea. She corrected the underlying hypothesis using data she generated herself.&lt;/p&gt;

&lt;p&gt;Verdicts are conversation starters only when the conversation stays open. The three fixes above only happened because three readers treated the original post as something to push back on, not something to congratulate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I am doing for the next four weeks
&lt;/h2&gt;

&lt;p&gt;The PIVOT verdict still stands at 75%. The three ships moved positioning, methodology, and UX. They did not change the underlying market truth about WTP and category density. Those are the next thing.&lt;/p&gt;

&lt;p&gt;What is continuing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WTP interviews queued for the founders who touched the paid tier. Up to fifteen. Starting next week, will run through mid-June.&lt;/li&gt;
&lt;li&gt;A/B test of "validated pain signals" versus "opportunities" framing on landing variants. About two weeks of data needed.&lt;/li&gt;
&lt;li&gt;The eight active builder-peer threads from this cycle. Most of them have not yet given me what they are going to give me.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What is intentionally not changing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No new feature roadmap until the WTP interviews finish.&lt;/li&gt;
&lt;li&gt;No paid acquisition spend until the activation to retention story is clearer in real data, not in framework.&lt;/li&gt;
&lt;li&gt;No declaration that anything is fixed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will be back here in three to four weeks with what the WTP interviews actually surfaced. If you have been on either side of a concierge pilot for an enterprise AI product, I would love to hear about it before then. The replies on the first post taught me more than the post itself did.&lt;/p&gt;

&lt;p&gt;If you want to try the validator (or push back on the verdict logic the way Shota did), it is at &lt;a href="https://monetscope.com/validate/pro" rel="noopener noreferrer"&gt;monetscope.com/validate/pro&lt;/a&gt;. Same honest disclosure as last time: the AI verdict feature is paid, the basic idea validator is free, and pushback in the comments still ships.&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>startup</category>
      <category>saas</category>
      <category>indiehackers</category>
    </item>
    <item>
      <title>I ran my idea-validation product through its own validator. The verdict was PIVOT.</title>
      <dc:creator>Benjian Dai</dc:creator>
      <pubDate>Sat, 23 May 2026 06:57:18 +0000</pubDate>
      <link>https://dev.to/benjiandai/i-ran-my-idea-validation-product-through-its-own-validator-the-verdict-was-pivot-dea</link>
      <guid>https://dev.to/benjiandai/i-ran-my-idea-validation-product-through-its-own-validator-the-verdict-was-pivot-dea</guid>
      <description>&lt;p&gt;Last week I ran a user's idea through Pro Validate, the AI validator I built into MonetScope. It came back PIVOT, 65% confidence. Not PROCEED, not PAUSE. PIVOT.&lt;/p&gt;

&lt;p&gt;I sat with that for about ten seconds. Then a more uncomfortable question showed up: if my validator says PIVOT to her idea, what would it say to my own product?&lt;/p&gt;

&lt;p&gt;So I ran MonetScope through MonetScope.&lt;/p&gt;

&lt;p&gt;Verdict: PIVOT, 68% confidence.&lt;/p&gt;

&lt;p&gt;The result was both reassuring and brutal. Reassuring because it proved the tool isn't built to flatter. Brutal because the three reasons it gave me were exactly the parts I'd been quietly avoiding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Did This
&lt;/h2&gt;

&lt;p&gt;Most "AI idea validator" tools have a credibility problem. You feed them an idea, you get back something that sounds smart, and you have no idea whether the answer would be different if you'd typed something completely different. The same input doesn't always give the same output. The output doesn't disclose its evidence. It just confidently says yes.&lt;/p&gt;

&lt;p&gt;That's why I built Pro Validate the way I did. Every signal in the report links back to actual Reddit, Hacker News, or X posts that inform it. But there's a deeper test I hadn't run on myself: would the tool tell ME no?&lt;/p&gt;

&lt;p&gt;That question is the only thing that matters. Because if your validator gives PROCEED to every input that sounds plausible (including your own product description), it's not a validator. It's a mirror.&lt;/p&gt;

&lt;p&gt;So I wrote the most honest version of MonetScope's pitch I could, pasted it into the form, and hit Validate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Idea&lt;/strong&gt;: A web platform that mines Reddit, Hacker News, and X for validated user pain points, scores them across 11 dimensions, and gives founders a PROCEED / PIVOT / PAUSE verdict on ideas they submit. 12,000+ pre-validated opportunities in the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Target user&lt;/strong&gt;: Indie founders and SaaS builders looking to either find their next idea or validate one they already have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monetization&lt;/strong&gt;: Subscription. Free tier limited, paid tier unlocks the AI verdict, deep analysis reports, and opportunity monitoring.&lt;/p&gt;

&lt;p&gt;Before clicking submit, I wrote down my prediction: PIVOT, somewhere between 60-70% confidence. The space had at least 5 competitors I could name from memory. The product naming ("MonetScope") isn't self-explanatory. WTP signals from indie founders are notoriously weak.&lt;/p&gt;

&lt;p&gt;I expected pushback. I got more than pushback.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2y0phyovwhkrv2f1w1tv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2y0phyovwhkrv2f1w1tv.png" alt="PIVOT 68% verdict with three critique bullets" width="799" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;68% confidence. My prediction was on the dot. But that was the only comforting part of the report.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Critiques
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Critique 1: "15 highly similar matched opportunities (top 82.8% similarity)"
&lt;/h3&gt;

&lt;p&gt;My first reaction: "fifteen direct competitors? I knew of five."&lt;/p&gt;

&lt;p&gt;Then I read it more carefully. These weren't 15 existing competitors. They were 15 entries in my own database. Independent clusters of pain signals from Reddit, HN, and X, all describing the same shape: "founders need a way to extract validated startup ideas from forum signals."&lt;/p&gt;

&lt;p&gt;In other words: my own product had surfaced 15 separate groups of people on the internet asking, in slightly different words, for what MonetScope does.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzp18orzbzecca36yfmwd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzp18orzbzecca36yfmwd.png" alt="Top 3 related opportunities at 83/82/81% match" width="800" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That changes the read of the data entirely. 15 highly similar matches isn't a saturation signal. It's a demand signal. The market is real and is being articulated by independent voices.&lt;/p&gt;

&lt;p&gt;What it doesn't change is the second critique.&lt;/p&gt;

&lt;h3&gt;
  
  
  Critique 2: "Zero direct WTP mentions"
&lt;/h3&gt;

&lt;p&gt;This one was harder to sit with.&lt;/p&gt;

&lt;p&gt;Across 34 evidence quotes from matched opportunities, zero of them contained phrases like "I'd pay for" or "shut up and take my money." Pain is everywhere. Willingness-to-pay is invisible. And the existing direct competitors in this space (Product Hunt, Indie Hackers, Starter Story, ChatGPT) are all free.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fohdvf0saz66sy0ph3qol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fohdvf0saz66sy0ph3qol.png" alt="Monetization Fit showing pricing collision with free competitors" width="547" height="702"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The pricing band Pro Validate assigned me ("Free to $20") puts MonetScope in direct head-to-head with established free incumbents. That's not a winning position. That's a position where users compare you to free and shrug.&lt;/p&gt;

&lt;p&gt;Reading this, I realized I'd been silently treating "monthly subscription" as the obvious answer. The data was telling me to stop treating it as obvious and actually validate whether the founder ICP would pay, at what point, and for what specific output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Critique 3: "Without sharp differentiation"
&lt;/h3&gt;

&lt;p&gt;This is the one that should have been easiest to argue with. I have an 11-dimension scoring model. Evidence trails linking to source posts. A B2B API. A pre-validated database. There's plenty of differentiation, technically.&lt;/p&gt;

&lt;p&gt;But "technically differentiated" and "differentiation that lands in the buyer's head" are different things.&lt;/p&gt;

&lt;p&gt;And here's where the case study stops being about Pro Validate and starts being about a stranger pattern.&lt;/p&gt;

&lt;p&gt;In the same week I ran this self-test, two other independent signals arrived saying the same thing in different words:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signal 1 (Pro Validate)&lt;/strong&gt;: "Many free/alternative tools make paid conversion challenging without sharp differentiation."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signal 2 (a positioning consultant who cold-emailed me out of nowhere)&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"On the first screen, there are several trust-building claims at once. AI-curated, real pain, validated commercial potential, 11-dimension scoring. But I think one concrete opportunity example with a crisp 'why trust this score' explanation would do more work than the stack of abstractions."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Signal 3 (an actual user who'd signed up and was confused)&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The wording around 'opportunities' and the overall presentation gave me the impression that the platform could also help founders connect with potential buyers, partners, or commercialization opportunities for their projects."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Three independent paths. My own tool. A stranger consultant. A real user. Different audiences, different language, same diagnosis: the differentiation isn't sharp enough, and the positioning leaks in ways I hadn't seen.&lt;/p&gt;

&lt;p&gt;When external sources start saying the same thing through different channels, it's not feedback anymore. It's the diagnosis.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Playbook
&lt;/h2&gt;

&lt;p&gt;Pro Validate doesn't just give a verdict. It gives a Validation Playbook with specific actions ranked by priority.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qwbae0a26300r4ewfmf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qwbae0a26300r4ewfmf.png" alt="Validation Playbook with 4 P0 actions across Validate, Build, Acquire" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The four P0 items it surfaced for MonetScope:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run 50 test validations on real user-submitted ideas&lt;/strong&gt; and measure verdict usefulness (1 week)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interview 15 recent users about willingness to pay&lt;/strong&gt; for deeper analysis (2 days)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit top 5 competitors' free tiers&lt;/strong&gt; to map exact feature gaps vs your paid offering (3 days)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Track conversion rate&lt;/strong&gt; from free to paid within the first 30 users (ongoing)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice what's missing from this list: "ship more features." The verdict isn't telling me to build. It's telling me to talk to people, audit incumbents, and measure what's already happening before adding anything new.&lt;/p&gt;

&lt;p&gt;That's what a PIVOT verdict actually means. Not "kill it." Not "rebuild it." It means: validate adoption friction and WTP before building any more.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Teaches Me About Idea Validators
&lt;/h2&gt;

&lt;p&gt;Three things I think matter, beyond MonetScope specifically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The honesty test&lt;/strong&gt;: If a validator gives PROCEED 95% to every product description that sounds plausible, the tool is broken. PIVOT, calibrated against actual evidence, is the only result that proves the validator is doing real work. The day I get a PROCEED verdict on something I know is a bad idea, I have to retire the tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The depth test&lt;/strong&gt;: A vague "needs work" verdict is useless. The reason Pro Validate's PIVOT was actionable is that it gave me four specific P0 actions, each with a hypothesis to test and an estimated effort. That's the difference between a horoscope and a diagnosis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The blind-spot test&lt;/strong&gt;: My own product surfaced a problem I'd been avoiding. A stranger consultant independently pointed at the same problem. An actual user, in a completely different context, pointed at the same problem through a totally different angle (the word "opportunities" being read as "commercialization opportunities"). External signals stack. They override the founder's ego eventually.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm Doing About It
&lt;/h2&gt;

&lt;p&gt;Two things in motion, neither of them "ship more features."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First&lt;/strong&gt;: I'm running the WTP interviews this week. 15 of them, focused on the founders who've already touched the paid tier. The verdict was right that indie founder WTP is the question I haven't actually answered. I've been defending the current price. Time to find out what the actual ladder should be.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second&lt;/strong&gt;: I'm auditing the landing page copy this weekend. When a real user reads "opportunities" as "commercialization opportunities for my project," that's not a language nitpick. It's a positioning leak. The word is doing the wrong work in the buyer's head.&lt;/p&gt;

&lt;p&gt;I'll write another post in 4 weeks with what came back.&lt;/p&gt;

&lt;p&gt;If you want to try Pro Validate on your own idea (and see whether it tells you PIVOT or PROCEED), it's at &lt;a href="https://monetscope.com/validate/pro" rel="noopener noreferrer"&gt;monetscope.com/validate/pro&lt;/a&gt;. Honest disclosure: the AI verdict feature is paid. The basic idea validator is free.&lt;/p&gt;

&lt;p&gt;The most useful thing I can promise is that it won't tell you what you want to hear unless the data actually says you should hear it.&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>startup</category>
      <category>saas</category>
      <category>indiehackers</category>
    </item>
    <item>
      <title>I shipped an AI pipeline in a month that reads Reddit, HN, and X for startup ideas. The hardest part wasn't the AI.</title>
      <dc:creator>Benjian Dai</dc:creator>
      <pubDate>Tue, 28 Apr 2026 13:30:23 +0000</pubDate>
      <link>https://dev.to/benjiandai/i-shipped-an-ai-pipeline-in-a-month-that-reads-reddit-hn-and-x-for-startup-ideas-the-hardest-2fkb</link>
      <guid>https://dev.to/benjiandai/i-shipped-an-ai-pipeline-in-a-month-that-reads-reddit-hn-and-x-for-startup-ideas-the-hardest-2fkb</guid>
      <description>&lt;p&gt;For the last month I've been building MonetScope — a pipeline that crawls Reddit, Hacker News, and X, reads what real people are complaining about, and surfaces the complaints as scored startup opportunities.&lt;/p&gt;

&lt;p&gt;Going in, I assumed the hard part would be the AI layer. You know the story: prompt engineering, structured output, temperature tuning. That's where the demos happen and where most of the blog posts get written.&lt;/p&gt;

&lt;p&gt;It wasn't. The LLM layer landed roughly on schedule. What took real engineering time were four other things — each one taught me something I'll carry into the next pipeline I build. Plus one deeply dumb cross-language serialization bug that nearly corrupted my data for a week without me noticing.&lt;/p&gt;

&lt;p&gt;This is a write-up of those things.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline, very roughly
&lt;/h2&gt;

&lt;p&gt;Before we dig in, the mental model. I'm keeping this deliberately abstract because the interesting part is the categories, not my specific libraries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   [Reddit]    [Hacker News]    [X]
       \            |            /
        \           |           /
         +---&amp;gt; crawler layer &amp;lt;---+
                    |
             message queue
                    |
          deterministic filters
                    |
           multi-stage LLM layer
                    |
            grounding + storage
                    |
                 product UI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two runtimes: one for crawlers (good at "get data out of places"), one for orchestration plus API (good at "stay up under load"). A message queue between them. Boring, intentionally.&lt;/p&gt;

&lt;p&gt;What's on that diagram today is three public platforms. What's on my whiteboard is more — additional communities, niche forums, and eventually a user-submitted channel where a founder can drop in their own support tickets, a competitor's review stream, or a CSV dump from a private Slack. Each new source is just another input box on the diagram, which is the whole point of having the diagram at this level of abstraction. The cost of adding source N+1 is not in the pipeline; it's in the per-platform quality heuristics, and that's a problem I enjoy having.&lt;/p&gt;

&lt;p&gt;Now — the four things that took more time than the AI. Starting with the most embarrassing.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The scraping tool was wrong for two of three platforms
&lt;/h2&gt;

&lt;p&gt;Every scraping tutorial you'll find online opens with the same heavyweight toolkit — headless browser, stealth plugins, proxy rotation, the works. I started there too.&lt;/p&gt;

&lt;p&gt;This turned out to be wrong for one platform, unnecessary for another, and actively dumb to avoid on the third.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform A:&lt;/strong&gt; I had a headless browser dutifully rendering pages for three weeks before I realized the same data was available through a much thinner path that didn't require rendering a single pixel. When I rewrote that spider it went from "needs a beefy worker" to "runs on a potato." I want those three weeks back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform B:&lt;/strong&gt; There was a developer-focused API available the entire time. Not just scrape-able, officially supported. I was reinventing its existence in the browser layer. Pure hubris — I had assumed "if the tutorial uses a browser for it, that must be the right tool."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform C:&lt;/strong&gt; No shortcut. Actively hostile to scraping, continuous cat-and-mouse, and my time is worth more than the subscription. Paid for access. Never looked back.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The generalizable lesson: &lt;strong&gt;don't start with a tool and hunt for problems to solve with it. Start with how the source actually serves data to its own frontend.&lt;/strong&gt; If it serves structured data, there's usually a path that isn't a browser. If it only renders via JS, that's your answer. If it's hostile, pay or skip.&lt;/p&gt;

&lt;p&gt;The heavyweight-browser default isn't wrong — it's a good fallback when nothing else works. The failure mode is treating it as the starting point.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The cheapest filter is the one that runs before the LLM
&lt;/h2&gt;

&lt;p&gt;Naive version of the pipeline: crawled post arrives → LLM processes it → structured output goes to the database.&lt;/p&gt;

&lt;p&gt;Two problems at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's expensive.&lt;/strong&gt; Every post is tokens, and token cost on a content-scale pipeline dominates the bill within a week.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The output is worse.&lt;/strong&gt; "Why is no one building X?" posts waste tokens producing confident-sounding opportunity cards that don't survive human review. A model asked to extract an opportunity from a substance-free rant will dutifully hallucinate one.&lt;/p&gt;

&lt;p&gt;The fix is philosophically simple: &lt;strong&gt;put a deterministic filter in front of the LLM that rejects content the LLM would have rejected anyway, but for free.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What "deterministic" means varies per platform — what counts as a substantive post in one community is a throwaway in another. The thresholds are per-platform, and they drift as community norms change. I'm not going to publish the current values; they're part of the product. But the interesting thing about tuning them isn't the numbers. It's that I ended up building a small tuning harness that was more work than picking any individual threshold.&lt;/p&gt;

&lt;p&gt;Ordering matters too. The deterministic filter runs &lt;em&gt;before&lt;/em&gt; the stateful dedup layer, not after. Posts rejected today can be reconsidered tomorrow if they accumulate engagement — which they sometimes do, especially on HN.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Generalizable rule:&lt;/strong&gt; before every LLM call, ask "can I cheaply reject this input first?" The answer is usually yes, and the win compounds: less cost per document, better signal on the documents that make it through, fewer false positives to clean up downstream.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Shipping an "AI-grounded" feature without lying to users
&lt;/h2&gt;

&lt;p&gt;This is the section I most want a reader to take away, so I'll be careful about the level I pitch it at.&lt;/p&gt;

&lt;p&gt;The product makes a specific promise: every claim it generates is backed by a quote from an identifiable, real user. You see "users complain that X breaks every Tuesday" and you can click through to the exact comment where someone said exactly that.&lt;/p&gt;

&lt;p&gt;If that promise leaks — if even a small percentage of the quotes are paraphrased, massaged, or invented — the product has no reason to exist. "We summarize Reddit with AI" is a commodity. "We show you the literal thing the person said" is not.&lt;/p&gt;

&lt;p&gt;LLMs in their default configuration &lt;em&gt;will&lt;/em&gt; break that promise. Paraphrasing is what they're good at. Making up a plausible-sounding quote is easier for them than surfacing the specific boring one that matters. This isn't a flaw of any particular model; it's a property of optimized-for-fluency generation.&lt;/p&gt;

&lt;p&gt;The approach I landed on, at the pattern level:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Don't ask the LLM to find sources.&lt;/strong&gt; Do extraction of candidate source material deterministically, &lt;em&gt;before&lt;/em&gt; the generation step. The LLM sees a curated candidate pool, not the raw corpus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Constrain generation to operate on those candidates.&lt;/strong&gt; The LLM's job is to synthesize and structure. It is not the layer that decides what's citable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mechanically verify every output claim against a specific source record.&lt;/strong&gt; If it doesn't match a real record, it doesn't ship. This is the step most pipelines skip, and it's the one that determines whether users still trust the output six months in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail closed.&lt;/strong&gt; If verification can't find the source for a generated claim, drop the claim. If dropping claims leaves the output empty, drop the whole output. Empty is fine. Phantom is not.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I won't walk through the matching algorithm, what the candidate pool looks like in this codebase, or how I decide "drop claim vs drop whole output" — those are product-specific tuning and they're where the moat actually lives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pattern itself is freely available, and I wish I'd seen it articulated when I started:&lt;/strong&gt; for any LLM feature where hallucination is a &lt;em&gt;correctness&lt;/em&gt; bug rather than a style flaw, the pattern is &lt;strong&gt;pre-extract → constrain → verify → fail closed&lt;/strong&gt;. Half-measures ship, but they don't keep trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. One score is almost never enough
&lt;/h2&gt;

&lt;p&gt;The hardest non-infrastructure problem turned out to be teaching the pipeline the difference between "people are angry" and "people will pay."&lt;/p&gt;

&lt;p&gt;The naive move is one score per item — how good is this opportunity, 0-10. This doesn't work. It tries to answer two reader questions at once ("is there real pain here?" and "would anyone actually buy a solution?") and those are orthogonal.&lt;/p&gt;

&lt;p&gt;A thread full of "someone should build X" is high on the first and near-zero on the second. A thread where one person has duct-taped three tools together and is actively shopping for a replacement is moderate on the first and very high on the second. A single composite score collapses those into noise.&lt;/p&gt;

&lt;p&gt;The fix isn't clever math. It's the recognition that &lt;strong&gt;any time a single number is answering more than one question, it ends up answering none of them well.&lt;/strong&gt; Split the signal into the questions you actually want answered, score those separately, and &lt;em&gt;compose&lt;/em&gt; them deliberately — not average them. A lot of weak signal does not beat a little strong signal, even when the means come out similar.&lt;/p&gt;

&lt;p&gt;One non-obvious finding I'll share because it's directional-only: the "obvious" communities (the big generalist ones) produce noisier signal than niche ones. Volume is a weak proxy for signal strength. Source &lt;em&gt;diversity&lt;/em&gt; turned out to matter as much as source &lt;em&gt;volume&lt;/em&gt; — an opportunity drawn from three different niche communities beats one drawn from thirty posts in one big general community, even though the raw evidence count is an order of magnitude lower.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dev-applicable version:&lt;/strong&gt; when a ranking algorithm isn't working, check whether you're averaging two signals that are answering different questions. You'll be surprised how often the answer is yes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The dumb bug that almost invalidated everything
&lt;/h2&gt;

&lt;p&gt;This one isn't a product moat — it's a general cross-language gotcha — so I'll share it in detail because the lesson is broadly useful to anyone building a polyglot system.&lt;/p&gt;

&lt;p&gt;Two services in two languages, both writing to the same database column. The column stores vectors as text, like &lt;code&gt;[0.123,0.456,...]&lt;/code&gt;. The database happily accepts whatever either language produces.&lt;/p&gt;

&lt;p&gt;The trap: each language's default float-to-string produces &lt;em&gt;slightly&lt;/em&gt; different output. Different widths. Different rounding at the edge case. To the human eye they look identical. To byte-wise comparison they aren't. To cosine similarity on the resulting vector, they're close but not the same vector.&lt;/p&gt;

&lt;p&gt;Nothing broke. No exception. No type error. No failing test. What happened was that semantic similarity rankings drifted depending on which service had last written the record. Results were good, then mysteriously slightly bad, then good again. I chased this for most of a week before I realized what I was looking at.&lt;/p&gt;

&lt;p&gt;The fix, in pseudocode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;One&lt;/span&gt; &lt;span class="n"&gt;canonical&lt;/span&gt; &lt;span class="n"&gt;formatter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shared&lt;/span&gt; &lt;span class="n"&gt;definition&lt;/span&gt; &lt;span class="n"&gt;across&lt;/span&gt; &lt;span class="n"&gt;both&lt;/span&gt; &lt;span class="n"&gt;runtimes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;Verified&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt; &lt;span class="n"&gt;golden&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="nf"&gt;format_vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;floats&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;to_string_exact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;to_string_exact&lt;/code&gt; is explicitly pinned to the widest-precision, culture-invariant format available in each language — not whatever the default &lt;code&gt;toString()&lt;/code&gt; happens to do. And the test is a literal string equality check against a hand-written golden output, run from both sides.&lt;/p&gt;

&lt;p&gt;The broader lesson: &lt;strong&gt;"both sides are using the default" is a dangerous sentence in a polyglot system.&lt;/strong&gt; Default serialization isn't a contract. If two runtimes are going to share a serialized format, write the format exactly once as a pure function, and verify its output byte-for-byte from both languages. Repeat for every format that crosses the runtime boundary — JSON casing was the other one that bit me, in passing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;p&gt;Nothing kills a launch post faster than "everything went great." So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I picked the wrong primary data store early, for the wrong reason — "flexibility." Future me didn't want flexibility. Future me wanted fewer moving parts. Moved to a boring relational DB with a JSON column type and things got better immediately. Lost about three weeks.&lt;/li&gt;
&lt;li&gt;I wrote my own rate-limit layer before realizing a standard caching-server primitive plus ten lines of script would have done the same job in an afternoon. Lost a week on that one.&lt;/li&gt;
&lt;li&gt;I underestimated observability. The liveness vs. readiness healthcheck split only happened after the third production incident. It should have been there on day one — you don't need it until you need it, and then you need it immediately.&lt;/li&gt;
&lt;li&gt;The grounding / verification layer shipped too late. Weeks of early data had to be re-processed once I added it. It should have been part of the first LLM call, not the twelfth.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If there's a theme: &lt;strong&gt;my worst decisions were the ones where I picked the more flexible option so future-me would have more options.&lt;/strong&gt; Future-me didn't want options. Future-me wanted something that worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;That's four things that turned out harder than the AI, plus the serialization bug for flavor.&lt;/p&gt;

&lt;p&gt;The product all this plumbing is in service of is at &lt;a href="https://monetscope.com" rel="noopener noreferrer"&gt;monetscope.com&lt;/a&gt; — free 14-day trial, no card. If you'd rather see what it produces before committing, this week's top 10 opportunities are at &lt;a href="//monetscope.com/this-week"&gt;monetscope.com/this-week&lt;/a&gt; (just email, no card). The output is what the pipeline exists for, but to be honest the feedback I most want right now is on the engineering choices above, not the landing page.&lt;/p&gt;

&lt;p&gt;If you've shipped an LLM-scored content pipeline yourself, I'd genuinely like to hear: &lt;strong&gt;how do you version and regression-test your prompts?&lt;/strong&gt; That's the layer I feel weakest on, and I haven't found a tool I love. Current setup is git commit plus a hand-maintained regression set, and it's starting to creak as the prompt surface grows.&lt;/p&gt;

&lt;p&gt;Also: if you maintain a community, newsletter, or data source you'd be interested in seeing indexed — that's on my roadmap, and I'm actively looking for source-expansion partnerships. DM me.&lt;/p&gt;

&lt;p&gt;Thanks for reading this far.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>ai</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
