<?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: Quốc Cường Phạm</title>
    <description>The latest articles on DEV Community by Quốc Cường Phạm (@quc_cngphm_8ed4b1290).</description>
    <link>https://dev.to/quc_cngphm_8ed4b1290</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%2F1617280%2Fa677ddf1-77b8-40be-974d-26c0ae317be0.png</url>
      <title>DEV Community: Quốc Cường Phạm</title>
      <link>https://dev.to/quc_cngphm_8ed4b1290</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/quc_cngphm_8ed4b1290"/>
    <language>en</language>
    <item>
      <title>Your AI-built app works in the demo. But did the signup event actually reach GA4?</title>
      <dc:creator>Quốc Cường Phạm</dc:creator>
      <pubDate>Thu, 15 Oct 2026 17:19:42 +0000</pubDate>
      <link>https://dev.to/quc_cngphm_8ed4b1290/your-ai-built-app-works-in-the-demo-but-did-the-signup-event-actually-reach-ga4-3g96</link>
      <guid>https://dev.to/quc_cngphm_8ed4b1290/your-ai-built-app-works-in-the-demo-but-did-the-signup-event-actually-reach-ga4-3g96</guid>
      <description>&lt;p&gt;You vibe-coded a landing page with a lead form over a weekend. The demo works: you submit, you see the success toast, you move on. Two weeks later you open GA4 and there are three events for a form that was submitted forty times — or worse, zero.&lt;/p&gt;

&lt;p&gt;This keeps happening to AI-built apps for a structural reason, not a careless one. The AI generates the UI and the tracking code in one pass, nobody writes a test for an event that "fires" (the page loads fine either way), and analytics bugs are silent by design: a broken button shows an error, a broken &lt;code&gt;generate_lead&lt;/code&gt; event shows nothing at all. Your funnel just becomes quietly unmeasurable.&lt;/p&gt;

&lt;p&gt;Here is the manual verification process that actually works today, before any tooling — learned the hard way from debugging duplicate conversions and dead DebugViews in production properties.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. DebugView proves almost nothing. Use it anyway — correctly.
&lt;/h2&gt;

&lt;p&gt;GA4 DebugView is the first place everyone looks, and the first place that lies to you. DebugView only shows events from browsers/devices running in debug mode (GTM Preview, &lt;code&gt;debug_mode: true&lt;/code&gt; in gtag, or the GA Debugger extension). Three failure modes bite everyone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Events appear in DebugView but never in standard reports — often consent mode defaulting to denied, or a duplicate property/measurement ID (events going to a test property).&lt;/li&gt;
&lt;li&gt;DebugView shows nothing while events flow fine — your debug flag never got set on the environment you're testing.&lt;/li&gt;
&lt;li&gt;DebugView works in GTM Preview but not from the live site — your trigger conditions differ between preview and production (consent, data layer timing).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So: use DebugView to confirm the event &lt;em&gt;structure&lt;/em&gt; (name, parameters), never to confirm it &lt;em&gt;arrives in production conditions&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Inspect the actual network request
&lt;/h2&gt;

&lt;p&gt;The ground truth is the request itself. Open DevTools → Network, filter for &lt;code&gt;google-analytics.com/g/collect&lt;/code&gt;, submit your form, and read the request:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;en=&lt;/code&gt; — the event name. Typos here are the #1 silent killer: &lt;code&gt;generate_leads&lt;/code&gt; never matches the &lt;code&gt;generate_lead&lt;/code&gt; you built reports on.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cid=&lt;/code&gt; / &lt;code&gt;_ga&lt;/code&gt; cookie — the client ID. If it changes between page loads, every submission looks like a brand new user.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ep.*&lt;/code&gt; — custom parameters. Verify the lead's value/label actually travels.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gtm=&lt;/code&gt; — confirms which GTM container version served the tag.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For SPAs, do this twice: once on a cold load, once after client-side navigation. Route-change page_views and duplicated listeners show up only in the second pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The duplicate-event check (the one that costs real money)
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;generate_lead&lt;/code&gt; firing twice per submission inflates conversions and corrupts every downstream bid strategy. The reliable way to count duplicates is not eyeballing DebugView — it's counting per client ID over a window:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With BigQuery export (free tier of GA4 exports): query &lt;code&gt;events_*&lt;/code&gt; for the last day, group by &lt;code&gt;user_pseudo_id&lt;/code&gt;, &lt;code&gt;event_name&lt;/code&gt;, and a 60-second timestamp bucket, and count events per bucket. Any bucket &amp;gt; 1 for a conversion event is a duplicate candidate.&lt;/li&gt;
&lt;li&gt;Without BigQuery: GA4 Explorations → open a single session → check whether one session shows twice the conversion events you expect. Crude, but it catches the common React StrictMode / double-render / retry duplicates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do this once per tracking release, not once ever. GTM container versions change.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The 10-point release checklist
&lt;/h2&gt;

&lt;p&gt;Before you call tracking "done" on an AI-built app:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every planned event fires with the exact documented name (&lt;code&gt;en=&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Parameters arrive (&lt;code&gt;ep.*&lt;/code&gt;), with correct types (revenue as number, not string).&lt;/li&gt;
&lt;li&gt;The client ID is stable across a full journey (landing → form → thank-you).&lt;/li&gt;
&lt;li&gt;No duplicates on double-click, no duplicates on SPA route re-entry.&lt;/li&gt;
&lt;li&gt;Consent state actually gates what you think it gates.&lt;/li&gt;
&lt;li&gt;Events fire in production, not only in preview/debug.&lt;/li&gt;
&lt;li&gt;The thank-you page fires its conversion exactly once, on the server-confirmed outcome if possible.&lt;/li&gt;
&lt;li&gt;Cross-domain links (checkout, docs) preserve the session.&lt;/li&gt;
&lt;li&gt;No events fire on bot/preview/admin paths.&lt;/li&gt;
&lt;li&gt;You can reproduce steps 1–9 from scratch in under 10 minutes — otherwise it will never be repeated, and it will rot.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The part I'm building
&lt;/h2&gt;

&lt;p&gt;I went through this often enough — including auditing a live commerce deployment where configuration drift produced measurably wrong discount behavior, silent failures again — that I've started building an open-source tool around step 10: you define a user journey once, it drives a real browser through it (Playwright-based), asserts the analytics events that should fire — names, parameters, duplicates, consent state — alongside API and database state checks, and emits an evidence bundle you can attach to a PR or a release.&lt;/p&gt;

&lt;p&gt;It's early — no public release yet, so there's nothing to link and nothing to oversell. If steps 2–3 above describe your week, I'd genuinely like to know which part hurts most: duplicate detection, parameter assertions, or consent-mode verification. That decides what gets built first. Comments or DM.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>analytics</category>
      <category>testing</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
