Event Tracking Best Practices: A Naming Convention That Scales
Inconsistent event names are the most common analytics failure mode. Use the
object_actionconvention 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
- snake_case always: It's readable, universal, and works everywhere (SQL queries, JSON, dashboards)
- Object first, action second:
payment_failed, notfailed_payment. This groups related events together when sorted alphabetically - Past tense for completed actions:
signup_completed, notsignup_completeorsigning_up - Present tense for ongoing states:
feature_used,page_viewed - No abbreviations:
button_clicked, notbtn_clkd - 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:
- Check the Events tab in SingleAnalytics to see all event names being received
- Look for duplicates: Similar events that should be consolidated
- Check property consistency: Are the same properties being sent with each occurrence?
- Verify counts: Do the event counts make sense? (e.g., you shouldn't have more
purchase_completedthancheckout_started) - 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.