İçeriğe geç
wedevit

October 23, 2026 · 9 min read · product

İlhan Buğra Aslan

Plenty of data, no answers: how to set up product analytics and event tracking


Which events should we track? The answer is not in the tool. It is in the questions you want answered. Amplitude's planning guide says a focused product may need around 20 events, while a feature-rich one is better served by 200. Segment's guidance lands in the same place: few events, rich properties. What we usually find in the field is the opposite. GA4 gets installed, enhanced measurement gets switched on, a pile of events fires from the code, and six months later nobody can answer "where do users drop off," because those steps were never defined as events. The data was collected. The question stayed open.

Five questions first, the event list second

Amplitude's taxonomy planning playbook puts the order plainly: business objectives, then the metrics that measure them, then the events that produce those metrics. In most projects the order runs backwards. A developer looks at a screen, decides to fire an event there, and the taxonomy ends up shaped like the code rather than the business.

For a subscription product, five questions might look like this. What share of new accounts completes a real piece of work in the first seven days? Which feature, touched during the trial, separates the accounts that convert from the ones that do not? At which step of the setup wizard do people give up? What share of invited teammates actually signs in? How many times did churned accounts open the product in their last 30 days?

The event list those five questions require rarely runs past fifteen items. Being short is what makes it survive contact with a sprint. The same logic you use when cutting MVP scope applies here: every event you add is a line you have agreed to maintain.

Page views do not build a funnel

In a single-page app, page_view tells you the address bar changed. It does not tell you whether the quote was saved, whether the upload finished, or whether step three threw a validation error. If the funnel steps were never written as explicit events, there is no funnel report to run.

Some of this comes free. On web streams, GA4's enhanced measurement collects page views, scrolls that reach 90% depth, outbound link clicks, site search, file downloads, video engagement, and form start and submit events without any code changes. Effort spent reimplementing those by hand is effort taken away from the events only you can define: Quote Created, Invoice Sent, Order Cancelled.

Naming: object plus action

Segment's data collection guidance suggests a simple pattern. Pick the object first (Product, Order, Report), then the action performed on it (Viewed, Completed, Downloaded). You end up with readable names like Product Viewed and Order Completed. Segment recommends Title Case for event names and snake_case for property names, and warns against three habits: building names dynamically, hiding a value in the name when it belongs in a property, and generating property keys like feature_1 and feature_2.

The tool has its own rules, and those are not up for discussion. GA4's event naming rules require snake_case, cap names at 40 characters, and reserve names such as session_start, first_open, first_visit, user_engagement and error. Parameter names cannot start with an underscore or with the firebase_, ga_, google_ or gtag prefixes. Whichever convention you pick, write it down. Two naming conventions in one product produce the same result as none.

Few events, generous properties

Events should be general, properties specific. Order Completed is one event; channel, currency, amount, payment method, discount code and line count are its properties. Carrying the same property set across every event about the same object is what makes later comparison possible at all.

A practical rule: decide the required properties and their types before the first line of instrumentation. Is the amount a number or a string, is the currency a three-letter code or a symbol, is the timestamp local or UTC? When those decisions go unwritten, the same field fills up in three formats and nobody can clean it a year later. We wrote about the reporting-side version of the same illness in why your reports disagree.

A tracking plan is a contract, not a spreadsheet

Put the plan in the repository, version it, and run changes to it through code review. A table living somewhere in a shared drive goes stale within one sprint.

The real difference shows up in whether the plan is enforced at the collection layer. In Snowplow, every event is bound to a JSON schema, and events that fail validation are filtered out of the pipeline rather than quietly landing in the warehouse. Segment Protocols and PostHog's schema enforcement do similar work. Tools like Avo export the plan as JSON Schema and generate typed wrapper code, so autocomplete does the remembering for you. A small team may not need any of that. The minimum that still works is routing every event through one typed function instead of scattering raw calls across the codebase.

The tool's quiet limits

If you are on GA4, do not plan without these numbers. The event collection limits: 25 parameters per event, 40 characters for an event name, 100 characters for a parameter value, 25 user properties per property. The configuration limits: 50 event-scoped custom dimensions, 30 key events, and sampling in explorations once a query crosses 10 million events.

Two limits do most of the damage. The first is cardinality. Once GA4 sees more than 500 distinct values a day in a dimension, it starts rolling the rest into the (other) row, which is why order numbers and user ids do not belong in custom dimensions. The second is retention. On standard properties, data retention defaults to 2 months and tops out at 14. If you plan to compare against the same period last year, move the setting to 14 months and turn on the BigQuery export for raw event data. Year-over-year comparison is not a job for the reporting interface.

Browser-side data arrives incomplete

In Safari's tracking prevention, persistent client-side cookies, meaning the ones written through document.cookie, are capped at a seven day expiry. Session cookies the server sets as Secure and HttpOnly fall outside that rule. A later version extended the same idea to non-cookie storage: after seven days of Safari use without the user interacting with the site, website data including localStorage is deleted.

Add the requests that ad blockers drop entirely. Numbers collected in the browser are a floor, not the truth. For product decisions that is tolerable. For revenue, subscriptions and refunds it is not, and those events belong to your server, which is their system of record anyway.

Sending events from the server has its own rules

Server-side events reach GA4 through the Measurement Protocol, and skipping the details in the documentation gets you events that arrive but never appear where you expect them. client_id is required, and session_id plus engagement_time_msec are what let an event land in session-based reports. A request carries at most 25 events, and the body has to stay under 130 kB.

On the ad platform side, sending the same event from both browser and server is now standard. Meta's Conversions API merges duplicates by matching event_id and event_name, within 48 hours of receiving the first copy. The practical takeaway fits in one sentence: generate the event id once and hand the same value to both sides. If each side invents its own, your conversion count quietly doubles.

Identity: from anonymous visitor to account

Before signing in, a user is tracked under a random anonymous id. After signing in, under a stable user id. If you do not stitch the two at the moment of registration, you lose everything the converting user did before they registered, which is the most valuable stretch of the funnel.

Do not use email addresses or phone numbers as identifiers. Send the internal id from your own database instead. Keeping personal data out of the analytics tool simplifies both the schema and the risk. In many countries analytics tracking also requires explicit consent, and that consent belongs in the code as real state, meaning no consent, no event queued. Showing a banner and sending the events anyway is the worst of both worlds.

Instrumentation breaks quietly

The most common failure is an event falling out of the code path during a refactor. A button component gets rewritten, the tracking call does not come along, nobody notices. Three weeks later the marketing team looks at the funnel and concludes conversion dropped.

The fix is cheap. Track daily volume per event and alert when it leaves the expected range. Give every event an owner. When the meaning of an event changes, add a new version rather than silently redefining it, because stacking the old series and the new one on the same chart misleads everyone who was not in the room. Clean out unused events once a year. A plan still listing events for a feature you removed three years ago is a plan nobody reads.

GA4 will not answer every product question

GA4 is a session and marketing oriented tool. Cohort retention, user-level funnels and feature adoption are easier in a product analytics tool or in your own warehouse. With raw event data in the warehouse, you can ask the question in SQL instead of asking what the interface allows.

This matters more the less traffic you have. When you do not have enough users to run an A/B test, measuring behaviour properly and pairing it with qualitative feedback is the reliable method you are left with.

Four steps that fit in this week

  • Write down the five questions you want answered. No question list, no event list.
  • Derive 10 to 15 events from them, and put the required properties and their types in one table in the repository.
  • Send revenue events from the server and share the event id with the browser, which closes the double counting on ad platforms.
  • Move GA4 retention to 14 months, enable the BigQuery export if you need longer history, and set a daily volume alert on your critical events.

Need help with this topic?

get in touch →← all posts