Loading tutorials…
Loading tutorials…
Bad event tracking is the most common reason Amplitude projects fail. Here is the naming convention, the SDK code, and the Data Guard rules that keep your taxonomy clean for years — not weeks.
Who this is forProduct + engineering teams about to instrument events, or teams whose existing taxonomy is already a mess and want to do a clean-room rebuild. Especially relevant for SaaS teams scaling past their first 1,000 active users where bad taxonomy starts costing real money in eng cleanup.
What you'll need
Step 1
Pick one convention — Amplitude's official recommendation is "Object Verb" in sentence case past-tense — and apply it ruthlessly across web, mobile, and server.
Format: `Object Verb` in sentence case, past tense. Examples: "Signup Submitted," "Subscription Cancelled," "Article Shared," "Workspace Created."
Avoid technical verbs (`clicked`, `fired`, `triggered`) — they describe what the code did, not what the user did. Bad: `cta_button_clicked`. Good: "Free Trial Started."
Property names use `snake_case` inside the event payload. Example: `{ plan_tier: "pro", referrer_source: "blog", feature_area: "dashboard" }`. Mixing camelCase and snake_case across events makes filtering impossible later.
Document the convention in your Tracking Plan and in the README of your repo. If you have 3+ engineers, set up a code-review rule that rejects events not matching the format.
Apply this retroactively in the Tracking Plan: any event already shipped that doesn't match the convention becomes a "Deprecated" alias mapped to the new name. Do not silently rename — every downstream chart depends on the old name.
Step 2
Every event needs a small set of REQUIRED properties (plan, org, feature area) plus event-specific OPTIONAL properties. Define this in the Tracking Plan, enforce in code.
Required properties (on every event): `plan_tier` ("free" | "pro" | "enterprise"), `org_id` (for B2B), `feature_area` ("dashboard" | "onboarding" | "billing"), `app_version`.
Optional properties (event-specific): `referrer_url` for "Page Viewed," `payment_method` for "Subscription Started," `error_code` for "Action Failed."
Forbidden properties: anything PII (`email`, `full_name`, `phone`) goes on the USER profile via `setUserProperties()`, not on every event. Doubles your event payload size and creates GDPR headaches.
Cardinality cap: any property with more than ~50 unique values per week becomes useless for filtering charts. Watch out for `url` (millions of unique values), `timestamp` (infinite), `session_id` (infinite). Use these as identifiers only, not chart dimensions.
Use Amplitude's Data → Tracking Plan → "Required Properties" feature to enforce this at ingestion. Events missing required props get rejected and flagged in the Data Quality tab.
Step 3
Create a single `analytics.track()` wrapper that imports the Amplitude SDK and adds default properties on every call. Never call `amplitude.track()` directly from feature code.
Create `lib/analytics.ts`: a thin wrapper that takes `(eventName, properties)` and calls `amplitude.track(eventName, { ...defaultProps, ...properties })`. Default props include `app_version`, `route`, `feature_area`.
In feature code, import only the wrapper: `import { track } from "lib/analytics"`. This forces a single point of control — adding required props or switching to a new SDK is a one-file change.
Wrap in a try/catch — Amplitude SDK errors should never break a user flow. `try { track(...) } catch { /* swallow */ }`. Use Sentry/Posthog to monitor the swallowed errors separately.
For React, build a `useAnalytics()` hook that injects route + user context automatically: `const { track } = useAnalytics(); track("Feature Used", { feature_area: "billing" })`. Reduces boilerplate by 70% in component code.
For server-side events (Node), use the `@amplitude/analytics-node` SDK and ALWAYS pass `user_id` explicitly — server context has no automatic identity. Forgetting `user_id` produces anonymous events that don't merge with browser sessions.
Step 4
In Amplitude → Data → Data Quality, set up rules that block events with missing required properties, unknown event names, or values out of range.
Open Amplitude → Data → Data Quality. Connect your Tracking Plan if not already linked.
Enable "Block unplanned events" — any event NOT in the Tracking Plan gets quarantined. This catches typos like `Signup Submited` (missing "t") that would otherwise pollute your data.
Enable "Block events missing required properties." If "Signup Submitted" requires `plan_tier` and an event arrives without it, the event is rejected and logged.
Set up Slack/email alerts on Data Quality failures — Settings → Notifications → Data Quality. Your eng team finds out within an hour, not three months later.
Review the Data Quality dashboard weekly for the first 60 days post-launch. Expect to find 5-15 bugs per week initially; tapers off as the codebase stabilizes.
Step 5
If you're moving from GA4, Mixpanel, or PostHog, Amplitude supports CSV import for historical events. The format must match your new Tracking Plan exactly.
Export historical events from the source platform. Mixpanel: Settings → Data Management → Export. GA4: BigQuery export → CSV. PostHog: API export.
Transform the data to Amplitude's CSV schema: columns `user_id`, `event_type`, `time` (Unix ms), then one column per event property. Reference: docs.amplitude.com/data-management/csv-import.
Upload via Settings → Sources → CSV Upload. Files over 100MB need to be split.
Backfilled events do NOT trigger Data Quality rules (Amplitude assumes you cleaned them upstream). Validate sample rows manually before upload — bad backfills produce permanent damage to your historical baseline.
After backfill, expect 24-48 hours for the events to appear in Charts. They do NOT appear in Real-time view; that's for live streaming only.
Step 6
Pick 5 test users (yours + a few teammates) and walk through their event stream in User Lookup. Then build a sample funnel to confirm aggregation works.
Open Amplitude → Data → User Lookup. Search for a known user_id.
Verify events appear in chronological order with the right properties. If `plan_tier` is missing on any event, the SDK or wrapper has a bug.
Trigger an event from your dev environment and check that it appears within 30-60 seconds. Long delays (>5 min) usually mean batching is misconfigured.
Build a 3-step funnel in Charts: "Signup Submitted" → "First Feature Used" → "Subscription Started." Conversion rates should match your gut sense within a factor of 2x. Wild discrepancies mean events are firing on wrong moments.
Cross-check with at least one other source (your DB query for signups, Stripe dashboard for subscriptions). Numbers within 5% are healthy; >10% gap needs investigation.
Common mistakes
Inconsistent event-name casing
What goes wrong: You end up with `Signup Submitted`, `signup_submitted`, and `SIGNUP_SUBMITTED` — three events in Amplitude's Catalog. Funnels split. Charts undercount by 50%+. Eng cleanup costs $3,000-6,000 once the project is mature.
How to avoid: Lock the convention in the Tracking Plan + code-review rules. Use Amplitude's Catalog → Merge to alias old variants → canonical name.
Putting PII in event properties
What goes wrong: Email/name/phone appear on every event payload. GDPR data-subject deletion requires scrubbing every event row — Amplitude charges for that at scale. Engineering cost: $2,000-5,000 in cleanup + ongoing legal risk.
How to avoid: PII goes on user profile via `setUserProperties()`, NEVER on events. If you need PII for a specific event, use a hashed ID instead.
High-cardinality properties used as chart dimensions
What goes wrong: You add `url` as a property and try to filter charts by it. Amplitude either errors out or returns useless top-1000 lists. Analysts give up and stop using the tool, undoing $3K-10K in setup cost.
How to avoid: Strip URLs to route patterns before tracking: `/dashboard/orgs/123/users/456` → `/dashboard/orgs/:orgId/users/:userId`. Keep raw URL only as a user-lookup field.
No try/catch around SDK calls
What goes wrong: A single bad event payload throws an exception and crashes your checkout flow. You lose a real transaction (typical SaaS LTV: $500-2,000) to fix a tracking bug. Real incident pattern.
How to avoid: Wrap every `amplitude.track()` in a try/catch inside your `analytics.ts` wrapper. Errors should go to Sentry/PostHog, never bubble to user flows.
Forgetting user_id on server-side events
What goes wrong: Server SDK events fire as anonymous because no automatic identity context exists. Server events (payments, subscription changes) become orphaned and never merge with browser sessions. Your funnel reports show 30-50% drop-off that does not exist in reality.
How to avoid: On every server-side `track()` call, explicitly pass `user_id`. Use a typed wrapper that requires it. Audit weekly via User Lookup on real test users.
Skipping Data Quality / Data Guard
What goes wrong: Bad events ship to production undetected. You discover a missing `plan_tier` on 100K events three months later, after pricing-page decisions have already been made on bad data. Cleanup cost: $5,000-15,000.
How to avoid: Enable "Block unplanned events" + "Block missing required props" in Data → Data Quality on day one. Slack alerts on failures.
Recap
Done — what's next
How to set up an Amplitude project from scratch
Read the next tutorial
Hand it off
Instrumentation done once is a project. Maintaining clean event taxonomy across a growing eng team is a job. A specialist owns the Tracking Plan, reviews PRs for instrumentation changes, and runs the Data Quality dashboard weekly — typically $300-900/mo at $14-16/hr.
See ongoing rates
Autotrack (defaultTracking in Browser SDK 2) is great for page views, session starts, and form interactions. It is NOT a substitute for manual instrumentation of business events ("Subscription Started," "Workspace Created"). Use both: autotrack for navigation, manual for business outcomes.
For SaaS, 5-30 events per session is healthy. Above 50 events per session, you are usually instrumenting every UI interaction (autotrack overshoot) — costs money on Plus plans and dilutes signal. Below 5 means you are under-tracking and will miss insights.
Yes, but never silently. Use Amplitude → Data → Catalog → "Merge events." This aliases old → new so historical charts keep working. Renaming via SDK alone splits your data and every chart returns half the data.
Amplitude assigns a `device_id` automatically for anonymous users. Continue calling `amplitude.track()` — events get attributed to the device. When the user logs in, call `setUserId()` and Amplitude merges the anonymous + logged-in sessions automatically.
Not necessarily. Event properties should match your analytics questions, not your DB. Example: track `payment_method: "stripe_card"` even if your DB stores it as a Stripe Customer ID — the human-readable string is what analysts will filter on.
Amplitude
Most Amplitude projects break in month three because of decisions made in the first hour. This walks through workspace setup, identity resolution, and the early taxonomy choices that determine whether your data is trustworthy six months from now.
Amplitude
Cohorts and Personas are where Amplitude beats GA4 by a mile. But most teams build sloppy cohorts and end up with overlapping segments that conflict. Here's the discipline that keeps them clean.
Amplitude
Amplitude says you're over your event quota and your bill is about to spike. Don't just disable tracking — that'll create gaps. Here's the systematic diagnosis specialists run when this happens.
Amplitude
DIY Amplitude is a great idea — until your taxonomy gets out of control or your charts disagree with reality. This is the honest framework for when the math flips toward hiring.