Back to Blog
Engineering

Event Tracking Best Practices: A Naming Convention That Scales

signup_click, SignupClick, user-signed-up: inconsistent event names create analytics chaos. Here's a battle-tested naming convention for clean, queryable data.

MW

Marcus Webb

Head of Engineering

February 12, 20267 min read

Event Tracking Best Practices: A Naming Convention That Scales

Inconsistent event names are the most common analytics failure mode. Use the object_action convention in snake_case (e.g., signup_completed, payment_failed), keep properties flat and consistent, never put PII in events, and maintain a shared tracking plan co-owned by product, engineering, and data teams. Invest one hour in naming conventions now to save hundreds of hours of data cleanup later.

You start with a few events: signup, purchase, page_view. Simple enough. Six months later, your events table looks like this:

signup
sign_up
user_signed_up
SignUp
user.signup
register
registration_complete

All seven events mean the same thing. Your funnel is broken because it's looking for signup but half the team is sending sign_up. Your retention analysis is missing users because their signup event was logged as registration_complete.

This is the most common analytics failure mode, and it's entirely preventable with a clear naming convention established early.

The Convention

After working with dozens of analytics implementations, here's the naming convention we recommend:

Format: object_action

// ✅ Good
sa.track('signup_completed');
sa.track('feature_used');
sa.track('payment_failed');
sa.track('onboarding_step_completed');
sa.track('invite_sent');

// ❌ Bad
sa.track('SignupCompleted');      // PascalCase
sa.track('signup-completed');     // Kebab-case
sa.track('user signed up');       // Spaces
sa.track('completed_signup');     // Action first
sa.track('click');                // Too vague

Rules

  1. snake_case always: It's readable, universal, and works everywhere (SQL queries, JSON, dashboards)
  2. Object first, action second: payment_failed, not failed_payment. This groups related events together when sorted alphabetically
  3. Past tense for completed actions: signup_completed, not signup_complete or signing_up
  4. Present tense for ongoing states: feature_used, page_viewed
  5. No abbreviations: button_clicked, not btn_clkd
  6. No PII in event names: Use properties for variable data

Common Event Patterns

User Lifecycle:

sa.track('signup_started');
sa.track('signup_completed', { method: 'email' });
sa.track('onboarding_step_completed', { step: 1, step_name: 'profile' });
sa.track('onboarding_completed', { steps_completed: 5 });
sa.track('subscription_started', { plan: 'growth', interval: 'annual' });
sa.track('subscription_cancelled', { reason: 'too_expensive' });

Feature Usage:

sa.track('report_generated', { type: 'funnel', format: 'pdf' });
sa.track('dashboard_customized', { widgets_added: 3 });
sa.track('data_exported', { format: 'csv', rows: 1500 });
sa.track('filter_applied', { filter: 'date_range', value: '30d' });

Commerce:

sa.track('product_viewed', { product_id: 'sku_123', category: 'shoes' });
sa.track('cart_item_added', { product_id: 'sku_123', quantity: 1, price: 79 });
sa.track('checkout_started', { items: 3, total: 237 });
sa.track('payment_completed', { method: 'stripe', amount: 237, currency: 'USD' });

Engagement:

sa.track('search_performed', { query: 'funnel analysis', results: 12 });
sa.track('notification_clicked', { type: 'email', campaign: 'weekly_digest' });
sa.track('feedback_submitted', { rating: 5, category: 'feature_request' });
sa.track('help_article_viewed', { article: 'getting-started', source: 'tooltip' });

Property Best Practices

Event properties are where you add context. Follow these rules:

1. Use Consistent Property Names

// ✅ Good: Same property name across events
sa.track('button_clicked', { location: 'hero' });
sa.track('button_clicked', { location: 'pricing' });
sa.track('button_clicked', { location: 'footer' });

// ❌ Bad: Different names for the same concept
sa.track('button_clicked', { location: 'hero' });
sa.track('button_clicked', { position: 'pricing' });  // Different key!
sa.track('button_clicked', { section: 'footer' });     // Another different key!

2. Use Enums, Not Free Text

// ✅ Good: Enumerated values
sa.track('signup_completed', { method: 'email' });
sa.track('signup_completed', { method: 'google' });
sa.track('signup_completed', { method: 'github' });

// ❌ Bad: Free-form text
sa.track('signup_completed', { method: 'Signed up with Google OAuth' });

3. Keep Values Simple

// ✅ Good: Flat, simple values
sa.track('purchase_completed', {
  plan: 'growth',
  amount: 49,
  currency: 'USD',
  annual: true
});

// ❌ Bad: Nested objects
sa.track('purchase_completed', {
  plan: { name: 'growth', tier: 2 },
  pricing: { amount: 49, currency: 'USD', discount: null }
});

4. Don't Put PII in Properties

// ✅ Good: Non-PII identifiers
sa.track('profile_updated', { fields_changed: 3 });

// ❌ Bad: Personal data in properties
sa.track('profile_updated', {
  email: 'jane@example.com',
  phone: '+1-555-0123',
  address: '123 Main St'
});

Use sa.identify() for user traits, and keep event properties focused on the action, not the person.

Building a Tracking Plan

A tracking plan is a document that lists every event your application should track, with its properties and purpose. Here's a template:

| Event Name | Properties | Purpose | Triggered When | |---|---|---|---| | signup_completed | method (string) | Measure signup conversion | User completes registration | | onboarding_step_completed | step (number), step_name (string) | Track onboarding progress | User finishes an onboarding step | | feature_used | feature (string), duration (number) | Measure feature adoption | User interacts with a core feature | | subscription_started | plan (string), interval (string), amount (number) | Track revenue events | User starts a paid subscription |

Who Maintains It?

The tracking plan should be co-owned by:

  • Product: Defines what to track and why
  • Engineering: Implements the tracking
  • Data/Analytics: Validates the data quality

Review the tracking plan quarterly. Remove events no one queries and add events for new features.

Declarative Tracking for Simple Cases

For basic click tracking, use HTML attributes instead of JavaScript. This keeps your tracking plan manageable and reduces engineering overhead:

<button
  data-sa-event="cta_clicked"
  data-sa-location="hero"
  data-sa-variant="blue"
>
  Get Started Free
</button>

This is perfect for:

  • CTA button clicks
  • Navigation link clicks
  • Feature toggle interactions
  • Simple form submissions

Reserve JavaScript sa.track() calls for events that need computed properties or conditional logic.

Validating Your Implementation

After implementing your tracking plan, validate it:

  1. Check the Events tab in SingleAnalytics to see all event names being received
  2. Look for duplicates: Similar events that should be consolidated
  3. Check property consistency: Are the same properties being sent with each occurrence?
  4. Verify counts: Do the event counts make sense? (e.g., you shouldn't have more purchase_completed than checkout_started)
  5. Test edge cases: What happens when a user refreshes mid-flow? What about network errors?

The Payoff

A clean, consistent event taxonomy pays dividends:

  • Funnels work correctly because event names are predictable
  • Retention analysis is accurate because user actions are consistently tracked
  • Team communication improves because everyone speaks the same data language
  • Debugging is faster because you can quickly find the event you're looking for
  • New team members onboard faster because the naming convention is self-documenting

Invest an hour in your naming convention now. It'll save you hundreds of hours of data cleanup later.


SingleAnalytics supports any event naming convention. Start tracking events the right way with a free account.

eventsbest-practicesimplementationdata-quality

Ready to unify your analytics?

Replace GA4 and Mixpanel with one platform. Traffic intelligence, product analytics, and revenue attribution in a single workspace.

Free up to 10K events/month. No credit card required.