<?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: mohamed salah</title>
    <description>The latest articles on DEV Community by mohamed salah (@mohamed_salah_63d8b83954d).</description>
    <link>https://dev.to/mohamed_salah_63d8b83954d</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%2F2801152%2F82dc8146-b985-4dd9-a148-9d25ed7fb052.jpg</url>
      <title>DEV Community: mohamed salah</title>
      <link>https://dev.to/mohamed_salah_63d8b83954d</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mohamed_salah_63d8b83954d"/>
    <language>en</language>
    <item>
      <title>It works on my machine, but is it working for my users?</title>
      <dc:creator>mohamed salah</dc:creator>
      <pubDate>Tue, 14 Jul 2026 00:25:09 +0000</pubDate>
      <link>https://dev.to/mohamed_salah_63d8b83954d/it-works-on-my-machine-but-is-it-working-for-my-users-2big</link>
      <guid>https://dev.to/mohamed_salah_63d8b83954d/it-works-on-my-machine-but-is-it-working-for-my-users-2big</guid>
      <description>&lt;p&gt;Every time I shipped something, the same thought hit me a few hours later:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It works on my machine. It works in staging. But is it &lt;em&gt;actually&lt;/em&gt; working for the people using it right now?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I had analytics. I had a green dashboard. And I still had no honest answer to that question. Users would quietly leave, a button would silently break on Safari, a page would crawl on a mid-range Android, and I'd find out days later, if at all.&lt;/p&gt;

&lt;p&gt;That gap is what I ended up building &lt;a href="https://heronsignal.com" rel="noopener noreferrer"&gt;HeronSignal&lt;/a&gt; to close. But before I talk about the tool, let me talk about the pain, because I think you've felt at least one version of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pain, depending on who you are
&lt;/h2&gt;

&lt;h3&gt;
  
  
  If you're a vibe coder / solo builder
&lt;/h3&gt;

&lt;p&gt;You ship fast. Cursor, Claude, v0, a Vercel deploy, and it's live. Beautiful.&lt;/p&gt;

&lt;p&gt;Then… nothing. You have no idea what happens after "Deploy successful." Is the checkout button throwing an error on mobile? Is your landing page slow enough that half your visitors bounce before it paints? You don't know, because setting up "real" monitoring feels like a second job: a Datadog dashboard you'll never look at, a Sentry config you half-finish.&lt;/p&gt;

&lt;p&gt;So you just… hope. And hope is not a monitoring strategy.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you're an engineer
&lt;/h3&gt;

&lt;p&gt;Your problem isn't &lt;em&gt;no&lt;/em&gt; data. It's &lt;em&gt;too much&lt;/em&gt;. Ten dashboards, alert fatigue, a Sentry inbox with 400 issues where 390 are noise. Something's clearly wrong, but &lt;em&gt;which&lt;/em&gt; thing actually matters? You spend your morning triaging instead of fixing. And when you finally pick an error, you get a stack trace with zero context: no idea what page it happened on, what the user was doing, or how to reproduce it.&lt;/p&gt;

&lt;p&gt;Triage is not the job. Fixing is the job. But the tools make you do the triage first.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you're a product person
&lt;/h3&gt;

&lt;p&gt;You can see in your funnel that people drop off at step 3. What you &lt;em&gt;can't&lt;/em&gt; see is &lt;strong&gt;why&lt;/strong&gt;. Was it a JS error? A slow page? A confusing layout? Your analytics tool tells you &lt;em&gt;what&lt;/em&gt; happened but never &lt;em&gt;why&lt;/em&gt;, and the engineering dashboards that might explain it are unreadable walls of numbers.&lt;/p&gt;

&lt;p&gt;So you guess. You open a ticket that says "checkout feels broken?" and hope someone figures it out.&lt;/p&gt;

&lt;h2&gt;
  
  
  What HeronSignal actually is
&lt;/h2&gt;

&lt;p&gt;Short version: it watches your &lt;strong&gt;real visitors&lt;/strong&gt; (not synthetic tests, not your own machine) and tells you &lt;strong&gt;what to fix first, and why it matters.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It captures the stuff that actually breaks experiences: JavaScript errors, unhandled promise rejections, failed network requests, slow pages, Core Web Vitals, sessions, funnels, custom business events. Then an AI reads all of it together and ranks it by &lt;em&gt;user impact&lt;/em&gt;, not by event count.&lt;/p&gt;

&lt;p&gt;That last part is the whole point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's different
&lt;/h2&gt;

&lt;p&gt;Most tools stop at "here's the data, good luck." HeronSignal is built around three steps: &lt;strong&gt;see it, understand it, fix it.&lt;/strong&gt; The first step is table stakes. It's the other two that are different.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. It tells you what to fix &lt;em&gt;first&lt;/em&gt;.&lt;/strong&gt;&lt;br&gt;
Instead of 400 issues sorted by timestamp, you get a ranked, plain-language answer: &lt;em&gt;"Mobile visitors are erroring out on /checkout before they can pay. This is costing you the most, fix it first."&lt;/em&gt; It connects the error to the page, the sessions, and the business impact. A product person can read it. An engineer can act on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. It can open the pull request.&lt;/strong&gt;&lt;br&gt;
This is the part people don't believe until they see it. When HeronSignal spots a real error, its coding agent can spin up a sandbox, clone your repo, make the &lt;strong&gt;smallest correct fix&lt;/strong&gt;, and open a &lt;strong&gt;draft PR&lt;/strong&gt;, with the reproduction context (the page, the browser, the conditions) already in the description. You review it. You merge it or you don't. It never touches your &lt;code&gt;main&lt;/code&gt; on its own. It's not "AI writes your whole app." It's "AI does the boring triage-and-first-draft so you don't have to."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. It plugs into your coding agent.&lt;/strong&gt;&lt;br&gt;
There's a read-only MCP endpoint, so your own agent (Cursor, Claude, whatever) can pull live monitoring context while you're fixing something: &lt;em&gt;"what's actually breaking on production right now?"&lt;/em&gt; No copy-pasting stack traces.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to actually use it
&lt;/h2&gt;

&lt;p&gt;Here's the part that matters to the "setting it up is a second job" fear: it's one script tag. No build step, works with any stack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heronsignalConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_PUBLIC_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://api.heronsignal.com/tracker.js"&lt;/span&gt; &lt;span class="na"&gt;async&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The script is ~16KB, loads &lt;code&gt;async&lt;/code&gt; so it never blocks your page, and batches events in the background with &lt;code&gt;sendBeacon&lt;/code&gt;, so it doesn't slow down the thing it's measuring. (You can literally watch its own footprint in your dashboard.)&lt;/p&gt;

&lt;p&gt;Prefer npm? There's a &lt;code&gt;@heronsignal/web&lt;/code&gt; package with an &lt;code&gt;initHeronSignal()&lt;/code&gt; call for React/Next apps, and a Node SDK for the backend.&lt;/p&gt;

&lt;p&gt;Once it's in, you get real-user errors, performance, and sessions automatically. The interesting part is what you add on top.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom events: track what matters to &lt;em&gt;your&lt;/em&gt; product
&lt;/h3&gt;

&lt;p&gt;Fire an event whenever something you care about happens. First argument is the name, second is an optional payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A visitor started checkout&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heronsignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkout_started&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;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pro&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;49&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// They finished it&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heronsignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkout_completed&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;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pro&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;coupon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LAUNCH20&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="c1"&gt;// A feature got used&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heronsignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;export_clicked&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;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;csv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1280&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Name a few of these along a journey (&lt;code&gt;signup_started&lt;/code&gt;, &lt;code&gt;email_verified&lt;/code&gt;, &lt;code&gt;first_project_created&lt;/code&gt;) and you've got a &lt;strong&gt;funnel&lt;/strong&gt;. Now you can see the exact step people fall off at, &lt;em&gt;and&lt;/em&gt; whether an error or a slow page is what's pushing them out. "People drop at step 3" and "there's a JS error on step 3" finally live in the same place.&lt;/p&gt;

&lt;p&gt;If you're using the npm package instead of the script tag, it's the same call, just imported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@heronsignal/web&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkout_completed&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;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pro&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;49&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom logs: your own breadcrumbs
&lt;/h3&gt;

&lt;p&gt;Sometimes you don't want an event, you want a log line: a note about what happened, with a level. Levels are &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heronsignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;info&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;Coupon applied&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;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LAUNCH20&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;discount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heronsignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;warn&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;Payment retried&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;attempt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stripe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heronsignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&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;Checkout failed to load prices&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;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/prices&lt;/span&gt;&lt;span class="dl"&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;500&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;p&gt;These show up in a searchable feed. You can filter by level, or by any attribute you attached, e.g. search &lt;code&gt;@endpoint:/api/prices&lt;/code&gt; to pull every log tied to that call. It's the context you always wish you had when a user says "it didn't work."&lt;/p&gt;

&lt;h3&gt;
  
  
  Catching errors you handle yourself
&lt;/h3&gt;

&lt;p&gt;Runtime errors and unhandled rejections are captured automatically. But the errors you catch in a &lt;code&gt;try/catch&lt;/code&gt; are invisible unless you report them, so report them, with the context that makes them fixable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&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;pay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&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="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Simplest form, just pass the error:&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heronsignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;captureError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Or attach your own context so it's actually fixable:&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heronsignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;captureError&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;PaymentError&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&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;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;showRetryButton&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;p&gt;That &lt;code&gt;metadata&lt;/code&gt; rides along to the dashboard, and if you let HeronSignal's agent open a fix PR, that same context lands in the PR description so it knows &lt;em&gt;what&lt;/em&gt; broke and &lt;em&gt;where&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend too (optional)
&lt;/h3&gt;

&lt;p&gt;There's a Node SDK if you want server-side events and errors in the same timeline as the frontend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;captureError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@heronsignal/node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;subscription_renewed&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;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pro&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mrr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;49&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;warn&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;Webhook signature mismatch&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;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stripe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nf"&gt;captureError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST /api/checkout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real examples: what this looks like day to day
&lt;/h2&gt;

&lt;p&gt;Three situations I actually built this for:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. "Signups dropped this week and I don't know why."&lt;/strong&gt;&lt;br&gt;
You've got &lt;code&gt;signup_started&lt;/code&gt;, &lt;code&gt;email_verified&lt;/code&gt;, &lt;code&gt;first_project_created&lt;/code&gt; as events. HeronSignal shows the funnel, and the drop is at &lt;code&gt;email_verified&lt;/code&gt;. You click in, and it turns out there's a spike of &lt;code&gt;captureError&lt;/code&gt; on the verify page, only on Safari, only on mobile. Analytics would've told you &lt;em&gt;signups are down&lt;/em&gt;. This tells you &lt;em&gt;the verify button throws on mobile Safari&lt;/em&gt;. That's a fix, not a mystery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. "A customer says checkout is broken, but it works for me."&lt;/strong&gt;&lt;br&gt;
You search the log feed for their session and see: &lt;code&gt;warn: Payment retried (attempt 2)&lt;/code&gt;, then &lt;code&gt;error: Checkout failed to load prices (status 500)&lt;/code&gt;, with the &lt;code&gt;endpoint&lt;/code&gt; and &lt;code&gt;orderId&lt;/code&gt; you attached. You didn't need them to screen-record anything. You've got the exact call, the status, and the order id.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. "I just shipped a new feature. Is anyone using it, and is it holding up?"&lt;/strong&gt;&lt;br&gt;
You drop one line: &lt;code&gt;event("new_export_clicked", { format })&lt;/code&gt;. Next morning you can see how many people clicked it, which formats they pick, and whether any &lt;code&gt;captureError&lt;/code&gt; fired around it. Real usage and real reliability, on day one, from a single line of code.&lt;/p&gt;

&lt;p&gt;The pattern is always the same: &lt;strong&gt;a little bit of naming (events plus logs) turns raw traffic into answers you can actually act on.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  A note on privacy, because you should ask
&lt;/h3&gt;

&lt;p&gt;It's not a session-replay tool and it's not trying to hoover up your users' data. Password and payment fields aren't recorded. Sensitive URL params (tokens, emails, secrets) get redacted. IPs are hashed, not stored raw. And you can mark any element with &lt;code&gt;data-heronsignal-mask&lt;/code&gt; or &lt;code&gt;data-heronsignal-ignore&lt;/code&gt; to keep it out entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who it's honestly for
&lt;/h2&gt;

&lt;p&gt;If you have a live website or app with real users and you've ever thought &lt;em&gt;"I hope that's working out there"&lt;/em&gt;, it's for you. Solo builders who want visibility without a PhD in observability. Engineers who are tired of triaging noise. Product people who want the &lt;em&gt;why&lt;/em&gt;, not just the &lt;em&gt;what&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If you have zero traffic and nothing shipped yet, come back when you do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it without installing anything
&lt;/h2&gt;

&lt;p&gt;The fastest way to see what I mean: run a &lt;strong&gt;free scan&lt;/strong&gt;. No signup, no script needed. Paste your URL and you get a report on performance, SEO, AI-crawler readiness, and what real-user monitoring would catch:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://heronsignal.com/scan" rel="noopener noreferrer"&gt;Scan your site free at heronsignal.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you install it and it catches something on day one that you didn't know about, I'd genuinely love to hear what. That's the whole reason I built it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'm the founder of &lt;a href="https://heronsignal.com" rel="noopener noreferrer"&gt;HeronSignal&lt;/a&gt;. Happy to answer anything in the comments.&lt;/em&gt;&lt;/p&gt;

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