/analytics-tracking-plan — instrumentation before experiments
Build a deployable tracking plan before any experimentation or dashboarding work. Without this, every downstream measurement is phantom — /ab-testing has no events to count, /dashboard visualizes incomplete data, /signup-onboarding-audit can't measure drop-offs.
Core principle — "track for decisions, not data"
Every tracked event must inform a decision someone is making. If no one would change behavior based on a metric, don't track it. Quality > quantity: prioritize 5–10 critical events over 100 vanity metrics.
Per source data (cite-verified, MIT): consistent naming conventions prevent ~40% of implementation bugs. Conversion data needs ~48 hours to stabilize before drawing inferences.
Workflow
Step 1 — Work backward from business questions
Don't start with "what events should we track?". Start with the questions:
| Business question | Required event |
|---|---|
| "Are people signing up?" | signup_completed |
| "Are they activating?" | activation_milestone_hit (per /signup-onboarding-audit definition) |
| "Are paid users using the product?" | core_workflow_completed |
| "Are referrals converting?" | referral_clicked, referral_converted (per /referral-program) |
| "Where is acquisition coming from?" | traffic_source property on every event |
| "Does marketing $X correlate with conversion?" | UTM + signup_completed join |
If a question doesn't have a clear required event, the question isn't decision-shaped — sharpen the question first.
Step 2 — Define 5–10 critical events with Object-Action naming
Naming convention: {object}_{action} in past tense (action already happened when fired).
| Object | Action | Event name |
|---|---|---|
| signup | completed | signup_completed |
| account | created | account_created |
| trial | started | trial_started |
| trial | expired | trial_expired |
| subscription | upgraded | subscription_upgraded |
| subscription | cancelled | subscription_cancelled |
| feature_X | used | feature_X_used |
| referral | clicked | referral_clicked |
| pricing_page | viewed | pricing_page_viewed |
| demo | requested | demo_requested |
Sharp rules:
- Snake_case, past tense, no camelCase.
- Don't pre-empt every possible action — name 5–10 critical ones; add more later when business questions arrive.
- Avoid temporal events that mean different things to different consumers (
page_viewis fine;user_engagedis ambiguous).
Step 3 — Standard properties on every event
Every event carries:
| Category | Properties |
|---|---|
| Page | page_url, page_title, referrer_url |
| User | user_id (anonymous + authenticated), user_email_hash, user_company, user_role |
| Campaign | utm_source, utm_medium, utm_campaign, utm_content, utm_term (capture first-touch + last-touch separately) |
| Product | plan_tier, team_size, subscription_status |
| Environment | device_type, browser, os, app_version |
Standard properties = consistency. Different events with different property names are a debugging nightmare.
Step 4 — UTM strategy
Lock the UTM taxonomy up-front:
| Parameter | Allowed values | Example |
|---|---|---|
utm_source |
The platform: linkedin, google, newsletter, direct, referral |
utm_source=linkedin |
utm_medium |
The channel type: organic_social, paid_social, email, cpc, affiliate |
utm_medium=organic_social |
utm_campaign |
The campaign name (snake_case): q2_launch_campaign |
utm_campaign=q2_launch_campaign |
utm_content |
Variant or asset: hero_button_v2, carousel_post_3 |
utm_content=hero_button_v2 |
utm_term |
Paid keyword only | utm_term=gtm_engineer |
Document this taxonomy in a shared sheet; every marketer reads it before tagging URLs. Drift here destroys attribution.
Step 5 — Implementation + validation
| Tool | Implementation home | Validation surface |
|---|---|---|
| GA4 | GTM container | GA4 DebugView |
| PostHog | Direct SDK or via GTM | PostHog Live Events |
| Mixpanel | Direct SDK or Segment | Mixpanel Live View |
| Amplitude | Direct SDK or Segment | Amplitude User Look-up |
Validation checklist (run before publishing to production):
- Each event fires with correct name + all standard properties.
- Test in tool's preview / debug mode before production deploy.
- Fire each event once per testing pass (don't loop in a test).
- Wait 48h after deploy before drawing inferences — conversion data stabilizes after ~48h.
Worked example — ClientCo
Business questions:
- "Are advisers signing up after our LinkedIn campaign?" →
signup_completed+utm_source=linkedin. - "Are signed-up advisers activating (connecting their first client account)?" →
client_account_connected(activation metric). - "Are connected advisers using the report generator?" →
report_generated. - "Where do trial-to-paid conversions come from?" →
subscription_upgraded+ first-touch + last-touch UTMs.
5 events: signup_completed, client_account_connected, report_generated, subscription_upgraded, subscription_cancelled.
Implementation: GA4 via GTM; PostHog for product-side events. Standard properties enforced on all 5 events.
Validation: GTM Preview run; PostHog Live Events confirms; 48-hour wait before first cohort analysis.
Anti-patterns
- ❌ Track 100 events because "we might need them". 90 become noise; the critical 10 get lost in the list.
- ❌ Inconsistent naming (
signupCompleted,signup-complete,signup_done). Breaks downstream queries. - ❌ Ship to production without preview-mode validation. Bugs are 10× more expensive after deploy.
- ❌ Make decisions inside the 48-hour stabilization window.
- ❌ No UTM taxonomy — every marketer invents their own. Attribution destroyed.
Integration with other skills
- Downstream:
/ab-testingconsumes the events for sample-size math;/dashboardvisualizes;/signup-onboarding-auditand/revopsneed event data to surface findings;/referral-programrequires referral_clicked / referral_converted events. - Upstream:
/icp-research+/funnel-strategydefine which business questions matter (which drives which events to track).
Attribution
This skill adapts patterns from coreyhaines31/marketingskills/analytics/SKILL.md (MIT license, © Corey Haines / Conversion Factory). Adapted to Genesys operator voice. Tool-agnostic across GA4 / PostHog / Mixpanel / Amplitude.
Final ship gate
Run /premortem --output before ship. See /premortem skill for the 5 execution domains (will-it-resonate / will-it-convert / will-it-stay-on-brand / will-stakeholder-push-back / will-it-degrade-over-time) and output template.
Trivial-case escape: ## Premortem\nNo failure modes — trivial change satisfies the contract for genuinely trivial outputs.