Object-action convention for events
The most scalable convention for event names is Object + Action in snake_case or camelCase format. For example: product_viewed, cart_item_added, checkout_completed. The object is the entity the user acts upon (product, cart, profile), the action is the past tense verb (viewed, clicked, submitted).
Why past tense? Because the event already happened when you track it. 'Checkout completed' is better than 'Complete checkout', which sounds like a CTA. Consistency in verb tense eliminates confusion: if you mix 'signup_started' with 'complete_purchase', your team doesn't know which convention to follow.
Avoid generic names like 'button_clicked' or 'page_loaded'. Be specific: 'cta_signup_clicked', 'pricing_page_loaded'. Each event should answer 'what object?' and 'what action?'. If you need additional context, use properties: the event is 'product_viewed', the property 'product_category' tells you if it's clothing or electronics.
Structure of a tracking plan
A tracking plan documents each event: name, description, when it fires, what properties it includes, data types. Without this, your analytics becomes chaos: duplicate events ('user_signup' vs 'signup_completed'), inconsistent properties ('user_id' as string in one event, number in another), or events nobody remembers the meaning of 6 months later.
For each event define: trigger (exactly when it fires), required properties (user_id, timestamp), optional properties (utm_source, device_type), expected values (if 'plan_type' exists, must be 'free'|'pro'|'enterprise'). This is your contract between product, dev, and data.
Tools like Avo, Segment Protocols, or a simple Google Sheet work. What matters is that it's versioned and updated. Every time you add a new event, update the tracking plan BEFORE implementing. If you implement first and document later, you never document.
Properties vs separate events
Deciding between creating separate events or using properties is critical. General rule: if the action is fundamentally different, separate event; if it's the same action with different context, property. Example: 'video_played' with property 'video_type: tutorial|webinar' is better than 'tutorial_played' + 'webinar_played'.
Separate events are useful when you need to track the exact funnel. In ecommerce: 'product_viewed' → 'cart_item_added' → 'checkout_started' → 'payment_info_entered' → 'order_completed'. Each is a distinct step in the funnel, not a variation of the previous one. Use properties to segment analysis within the same step.
The cost of too many events: your list becomes unmanageable. The cost of too few: you have to do complex queries filtering by 50 property combinations. The balance: ~50-200 core events for a medium product. If you reach 500, you're probably creating events that should be properties.
Common mistakes in event naming
Mistake #1: names from system perspective, not user perspective. 'api_call_successful' says nothing about what the user did; 'search_results_loaded' is better. Think about observable behavior, not technical implementation. The event is 'form_submitted', not 'post_request_sent'.
Mistake #2: format inconsistency. If you use snake_case, use it always. Don't mix 'user_signup', 'product-viewed', and 'checkoutStarted' in the same project. Choose a convention (snake_case is standard in analytics) and enforce it with linters or CI validations.
Mistake #3: overly granular events. You don't need 'filter_price_min_changed' and 'filter_price_max_changed'. Use 'filters_applied' with properties 'filter_type: price_min' and 'value: 50'. Consolidate events you'll always analyze together.
Mistake #4: not versioning changes. If you change an event schema (add required property, change data type), that breaks historical analysis. Either version the event ('checkout_completed_v2'), or use optional properties that are backward-compatible.