Back to Blog
AnalyticsNovember 28, 20259 min read

The 7 Event Tracking Mistakes We See on Every Audit

The 7 Event Tracking Mistakes We See on Every Audit

The 7 Most Common Event Tracking Mistakes

Bad tracking is worse than no tracking. It gives you false confidence. You make decisions based on numbers that are wrong, and you do not even know they are wrong. Here are the seven mistakes I see most often.

1. Tracking Clicks Instead of Outcomes

Teams track button_clicked on "Start Trial" and call it their signup metric. But a click is not a signup. The user might click, see a form, and leave. Track the outcome instead:

async function handleSignup(formData: FormData) {
  const result = await createAccount(formData);
  if (result.success) {
    posthog.capture("trial_started", { plan: result.plan, signup_method: "email" });
  }
}

Track the click too if you want to measure engagement, but never use it as your conversion metric.

2. Missing Properties on Events

An event without properties is almost useless. When feature_used fires 500 times last week, the first question is: which feature? Every event should carry what happened, where in the product, and relevant context:

// Bad
posthog.capture("export_completed");

// Good
posthog.capture("export_completed", {
  format: "csv",
  row_count: 1250,
  duration_ms: 3400,
  source_page: "analytics_dashboard",
});

3. Inconsistent Naming Conventions

One engineer writes userSignedUp, another writes user_signed_up, a third writes User Signed Up. Your analytics tool treats these as three separate events. Pick snake_case with a noun_verb pattern (trial_started, invoice_paid) and enforce it with a constants file:

const EVENT_NAMES = {
  trialStarted: "trial_started",
  invoicePaid: "invoice_paid",
} as const;

4. Not Tracking Server-Side Events

Ad blockers strip out PostHog on 15-30% of browsers. Any event tied to money or activation should be tracked server-side:

import { PostHog } from "posthog-node";
const posthog = new PostHog(process.env.POSTHOG_API_KEY!);

// Inside your API route after Stripe checkout creation
posthog.capture({
  distinctId: userId,
  event: "checkout_started",
  properties: { plan: "pro", amount_cents: 2900 },
});

Rule of thumb: track interactions on the client, track transactions on the server.

5. Duplicate Events from Re-Renders

If you fire an event inside a React component body (not in a useEffect), it fires on every re-render. Use a ref to ensure it fires once:

function Dashboard() {
  const tracked = useRef(false);
  useEffect(() => {
    if (!tracked.current) {
      posthog.capture("dashboard_viewed");
      tracked.current = true;
    }
  }, []);
  return <div>...</div>;
}

If dashboard_viewed fires exactly 3x per session on average, you probably have this problem.

6. Phantom Conversions from Bots

Bots visit your site, click through flows, and inflate your numbers. I have seen startups celebrate a 40% increase in signups that was entirely from a web scraper. Watch for: conversion rates that seem too good, spikes with no source, and sessions with zero time on page. Filter them:

function isLikelyBot(req: Request): boolean {
  const ua = req.headers.get("user-agent") || "";
  return /bot|crawler|spider|scrapy|headless|phantom/i.test(ua) || !ua;
}

Also flag signups that complete in under two seconds. No human types that fast.

7. Not Validating the Tracking Plan Before Shipping

The worst time to discover broken tracking is two weeks after launch when the PM asks for a funnel report. Validate before production:

  1. Write the tracking plan first (event name, properties, trigger location).
  2. Use PostHog's event debugger while clicking through your app locally.
  3. Wrap tracking calls to log in dev mode so you spot unexpected fires immediately.
  4. Test negative cases: does the event fire on cancel? On network error? On double-click?
  5. Set up alerts if critical event counts drop to zero.
function trackEvent(name: string, properties: Record<string, unknown>) {
  if (process.env.NODE_ENV === "development") {
    console.log(`[Track] ${name}`, properties);
  }
  posthog.capture(name, properties);
}

Tracking is infrastructure. Treat it like your database schema. Getting it right from the start takes a day of planning and saves weeks of confusion later.