Good event tracking naming conventions are the cheapest thing you will ever do and one of the most expensive things to fix later. On day one, naming an event costs a moment of thought. At event 200, spread across your codebase, three dashboards, and two people who have since left, fixing a naming mess costs a migration nobody wants to own. We see the aftermath constantly: a discovered-events catalog fills up with near-duplicate names that each looked reasonable to the person who typed them. This is how to name events so the person building a funnel next quarter can actually find them.
Naming is cheap on day one and expensive at event 200.
The reason naming feels unimportant early is that when you have twelve events and you wrote all of them, you remember every one. You know that the signup event is called SignedUp and the purchase event is called purchase and it does not matter that they do not match, because you are the only person reading them. The convention tax comes due later, when the event count is in the hundreds, when different people instrumented different features, and when the person querying the data is not the person who named it.
By then the cost of a bad name is not aesthetic. A misnamed or inconsistently named event silently produces wrong numbers, and wrong numbers are worse than missing numbers because nobody knows to distrust them. The fix is to decide the rules once, write them down, and hold the line from your first event. None of what follows is complicated. It is just easier to adopt at event 5 than to retrofit at event 200.
Pick one grammar and enforce it.
A grammar is a shape every event name follows. The one that holds up best is object then action, in past tense: signup_completed, invoice_paid, project_created. Object first means related events sort next to each other, so every invoice_ event sits together in an alphabetical list instead of scattering under created, paid, and failed. Past tense signals that the name records something that already happened, which is exactly what an event is. A name like create_project reads like a command; project_created reads like a fact, and a fact is what you are storing.
The specific grammar matters less than the fact that you have exactly one. If your team prefers action then object, fine, but then every event follows that shape and none drift. Write the rule in one sentence at the top of your tracking plan: object then action, past tense, snake_case. That sentence is the whole governance model at small scale, and it does more work than any tool.
Case consistency is not a style preference.
Every analytics tool treats an event name as a case-sensitive exact string. SignUp, sign_up, and signup are not three spellings of one event. They are three different events, three separate rows, three lines that each hold part of the truth. A funnel built on signup_completed will show conversions falling off a cliff the day someone ships a component that fires SignupCompleted instead, and the drop is not real. It is a naming split, and it can take an afternoon to notice because the number looks plausible.
snake_case is the default we recommend because it is the least ambiguous under pressure. It survives being copied from your code into a spreadsheet and back into a dashboard filter without any tool helpfully reformatting it. It has no capital letters to get wrong on a hurried Friday. Apply the same rule to property names while you are at it, so from_plan and to_plan never show up as fromPlan somewhere else. A discovered-events catalog is how you catch a split after the fact, but the goal is to never create one.
Put the variant in a property, not the name.
This is the single mistake that does the most long-term damage, because it feels like precision at the moment you make it. You add upgraded_to_growth, then upgraded_to_pro, then a year later upgraded_to_enterprise. Each one looked descriptive. Together they are a trap. You now have three events that mean the same thing, you can never see total upgrades without manually summing them, and every new plan forces a new event that you have to remember to add to every funnel and every report that already exists.
The rule is that the event name is the thing that happened, and a property carries the details of how. One plan_upgraded event with a to_plan property, and ideally a from_plan property too, gives you everything the three events did plus the total plus any breakdown you did not think to ask for on day one. The test is simple: if you find yourself about to create two events whose names differ only by a noun or an adjective, that difference belongs in a property. Names describe events; properties describe variants.
Name for the reader, not the writer.
The person who names an event is almost never the person who suffers from a bad name. You name it once, in flow, while shipping the feature. Someone else reads it months later while building a retention report under deadline, scanning a list of two hundred names for the one that means what they need. Write the name for that person. It should be guessable from the outside: a colleague who has never seen your code should be able to predict that the event fired when an invoice was paid is called invoice_paid, not billing_success or stripe_ok.
Two habits make names readable. Do not encode implementation details that will not mean anything to the reader, so avoid names tied to a vendor or an internal function name. And do not abbreviate to save characters, because sub_cxl saves nobody any time and costs everyone a guess. The names that survive are the boring, literal ones. That is a feature.
A starter taxonomy for a typical SaaS.
Most SaaS products can start with a small set of events across four areas: authentication, activation, billing, and collaboration. The table below is a concrete starting point you can adapt. It is deliberately small. A tight set of well-named events that everyone understands beats a sprawling catalog that nobody trusts, and you can always add more once these are earning their keep. For the deeper question of which events are worth tracking in the first place, see which events to track for a SaaS.
| Event | Group | When it fires | Key properties |
|---|---|---|---|
signup_started | Auth | User submits the first step of signup | method |
signup_completed | Auth | Account is created and verified | method, plan |
login_succeeded | Auth | User authenticates successfully | method |
password_reset_requested | Auth | User requests a reset link | none |
project_created | Activation | First workspace or project is created | none |
tracking_installed | Activation | The SDK is verified as sending events | source |
first_event_received | Activation | The first tracked event lands | event_name |
onboarding_completed | Activation | User finishes the setup checklist | none |
trial_started | Billing | A free trial begins | plan |
checkout_started | Billing | User opens the checkout flow | plan |
plan_upgraded | Billing | User moves to a higher plan | from_plan, to_plan |
invoice_paid | Billing | A payment succeeds | amount, currency, plan |
payment_failed | Billing | A charge is declined | amount, currency, reason |
subscription_canceled | Billing | User cancels their plan | plan, reason |
invite_sent | Collaboration | User invites a teammate | role |
member_joined | Collaboration | An invited teammate accepts | role |
comment_added | Collaboration | User posts a comment | none |
item_shared | Collaboration | User shares a resource | channel |
Notice what the table does with variants. There is one plan_upgraded event, not one per plan, and one payment_failed event that carries the reason as a property rather than a family of events for each decline type. Notice too that the names are close to the language you would use in a sentence, which is what makes them findable later. These same well-named events are what power downstream work like behavioral segmentation for email and product event tracking that drives email, where a clean event triggers the right message.
When to rename, when to alias, and when to live with it.
Once bad names exist, you have three options and they are not interchangeable. The critical thing to understand is that renaming an event in your code does not rewrite history. The events you already collected keep their old name forever. The new name starts empty on the day you change the code, so a naive rename produces two half-events: the old one that stops on the cutover date and the new one that starts from zero.
Rename when the payoff is worth that break: the name is genuinely misleading, or the mess is causing real reporting mistakes. Do it in a batch, ship it on a known date, and write that date in the tracking plan so anyone reading a chart with a discontinuity knows why. Alias when your tooling lets you map two names to one at the reporting layer, which gives you continuity without touching history. And live with it when the name is merely ugly but unambiguous. A slightly awkward name that everyone already knows the meaning of is not worth a migration. Spend that effort on preventing the next split instead.
Keep the tracking plan lightweight.
The tracking plan is the document that makes all of this stick, and the most common failure is making it too heavy to maintain. At small scale, a single shared spreadsheet or doc that lists every event, its properties, and when it fires beats any dedicated governance tool. It has no learning curve, everyone can read it, and anyone can propose an addition in a pull request or a comment. The one rule that keeps it alive is that a new event is not done until it is in the plan.
Governance tools earn their place later, when you have many events, several teams instrumenting in parallel, and a real risk of two people naming the same thing two ways in the same week. Until then, the overhead of a tool costs more than the drift it prevents. Start with the doc, review your analytics for the events actually arriving every so often to catch splits early, and add tooling only when the doc can no longer keep up.
Event naming conventions, answered.
What is the best naming convention for analytics events?
The most durable convention is object then action, written in snake_case, with the action in past tense: signup_completed, invoice_paid, plan_upgraded. Object first keeps related events sorted next to each other in an alphabetical list, so every billing event groups together. Past tense signals that the name records a fact that already happened, which is what an event is. The specific convention matters less than picking one and enforcing it everywhere, but this one holds up better than most as a team grows.
Should event names use snake_case, camelCase, or Title Case?
Pick one and never mix, because analytics tools treat event names as case-sensitive exact strings. snake_case is the safest default: it is unambiguous, it survives being copied between a codebase, a spreadsheet, and a dashboard without silent transformation, and it reads cleanly in a long list. camelCase works too if your team already uses it everywhere. The failure to avoid is inconsistency, where SignUp, sign_up, and signup all exist and quietly split one real event into three rows.
Should I put a variant in the event name or in a property?
Put it in a property almost every time. One plan_upgraded event with a plan property set to growth or pro is far more useful than separate upgraded_to_growth and upgraded_to_pro events. With the property, you can see the total upgrade count and break it down by plan in the same report. With the separate events, you can never get the total without manually adding them up, and every new plan means a new event you have to remember to instrument and add to every funnel.
When should I rename an event versus leaving it alone?
Renaming is worth it when a name is actively misleading or when a naming mess is causing people to build reports on the wrong event. It is not worth it for cosmetic tidiness, because renaming in your code does not rewrite the events you already collected. The old name keeps its history and the new name starts empty on the cutover date, so you either alias the two together at the reporting layer or accept a visible break in the chart. Rename deliberately, in a batch, and document the date you did it.
Do I need a tracking plan tool or is a document enough?
At small scale a shared document is enough and usually better. A single spreadsheet or doc that lists every event, its grammar, its properties, and when it fires is something the whole team can read and edit without training. Dedicated governance tools earn their keep once you have many events, several teams instrumenting in parallel, and a real risk of drift. Start with the doc, keep it honest, and only add tooling when the doc stops being able to keep up.
Naming is one of the few analytics decisions that is genuinely cheap to get right and genuinely painful to get wrong. Pick object then action, snake_case, past tense. Put variants in properties. Write it in a doc the whole team can read, and hold the line from event one. Do that and the person you hire next year will build their first funnel without asking you what anything means, which is the only real test of a naming convention that worked.