
A product analytics tracking plan defines which events a product should record, exactly when they occur, and what information each event needs. Start with a product question, then specify triggers, properties, sources, owners, and validation cases. A list of button names is not enough: an attempted action and a successfully completed action can tell very different stories.
Include the agreed plan in the design handoff before development. Product, design, engineering, and analytics can then resolve ambiguous definitions while the flow is still easy to change.
This guide follows a fictional language-learning app. The team wants to understand where an attempt to start practice breaks down before the learner submits a first answer. Its event names, properties, and test cases are proposed specifications, not collected data or claims about an existing application.
Start with a question that the events can answer
“Track engagement” is too broad to guide implementation. The example team asks: “Of the recorded attempts to start practice, which reach a created session, and which reach an accepted first answer?” That question separates a learner’s intent from the service’s response and the learner’s next action.
Define the unit of analysis before choosing events. Here it is a start attempt, identified by an attempt_id. One learner can make several attempts. Reporting attempts as if they were distinct people would change the meaning of the result.
Agree the observation window and how late-arriving data will be handled before interpreting the funnel. An attempt made moments before a report runs has had less opportunity to reach an answer than one made earlier. Mark unfinished observation windows separately instead of immediately labelling those attempts failures.
Also state what the result cannot answer. An accepted answer shows participation in a practice activity; it does not establish learning, satisfaction, or retention. Those questions need their own definitions and evidence. Keeping this first question narrow avoids collecting every interaction merely because it is available.
Twilio Segment’s data-collection guidance connects tracking plans with business questions and consistent event naming. Use a stable name for the action, with changing details represented as properties. The vocabulary below is one proposed convention, not a requirement to use a particular analytics platform.
Write an event contract, not just an event name
For each event, specify the trigger, the component that can know it happened, and conditions that must not produce it. “Practice Started” is ambiguous: does it mean the learner tapped a button, the server created a session, or the first exercise became usable?
The proposed catalogue separates those meanings. A client event describes an interface occurrence; a server event describes an accepted operation. Assign one authoritative producer for each definition so two teams do not independently emit the same success event.
| Event | Trigger and producer | Do not emit for |
|---|---|---|
Practice Launcher Viewed | Client renders the eligible launcher with an available start action; once per defined view instance | A hidden tab, loading placeholder, or an ineligible account |
Practice Start Requested | Client accepts a valid start action and begins a new logical attempt | A blocked action or a network retry of that same attempt |
Practice Session Created | Server commits a new practice session associated with the attempt | A pending request or a replay that returns an existing session |
Practice Start Rejected | Server makes a terminal rejection for the attempt, with a defined reason code | A client timeout whose server outcome is still unknown |
Practice Answer Accepted | Server accepts a new answer submission for the session | A rejected answer payload or a replay of the same submission |
The first accepted answer in a session can be derived from accepted submissions; the team does not also need a separately implemented “First Answer” event unless there is a clear reason. Specify how that derivation handles repeated submissions and arrival order.
The launcher event provides context but is not automatically the denominator for the start-attempt question. A missing client event might reflect collection failure or the product’s collection policy. It is not proof that a person never saw the screen. Preserve the distinction between unobserved behavior and behavior known not to have occurred.
Define properties with types, meanings, and limits
Segment’s Track specification distinguishes the event name from properties describing that action. Apply that separation to the example: a language choice should be a property, not a new event name for every language.
For each field, record its type, allowed values, which events require it, and who provides it. A property called status with undocumented values such as “done,” “ok,” and “complete” recreates the original ambiguity inside the data.
| Property | Proposed rule | Purpose |
|---|---|---|
attempt_id | Opaque string; required on request, creation, and rejection events | Connect a logical start attempt with its result |
session_id | Opaque string; required after session creation | Associate accepted answers with the created session |
submission_id | Opaque string; required on accepted-answer events | Distinguish a new answer submission from a replay |
practice_mode | String from the agreed set: review or new_material | Compare the two supported practice contexts |
reason_code | Required on rejection; controlled values such as content_unavailable | Explain a known rejection without sending raw error text |
schema_version | Integer; owned by the event-definition maintainer | Identify which property contract applies |
Specify the relationship fields too. An answer’s session_id must refer to the session created for the attempt being analyzed. A session ID that changes after every screen refresh would prevent reliable joining, even if each individual event passes a type check.
Keep event timestamps and transport metadata in the implementation contract. Segment’s common-fields documentation, for example, distinguishes a message identifier from timestamps such as when an event occurred and when it was received. A transport message ID and the product’s attempt_id serve different purposes; do not assume one replaces the other.
Collect only properties needed for the stated analysis and approved collection policy. This example does not need answer text, learner names, email addresses, or free-form support messages. An opaque identifier still needs appropriate handling; calling it an ID does not make its use anonymous or unrestricted.
Decide how retries, failures, and missing events behave
Walk through a timeout before approving the plan. The learner starts practice, the server creates the session, but the response never reaches the device. A retry may retrieve the same session. Counting another creation would overstate success; counting a terminal failure at the first timeout would contradict the server’s actual outcome.
In this proposed design, a retry of the same logical operation keeps its attempt_id. A deliberately new start attempt gets a new ID. Engineering must define when the app offers each action and how the service handles repeats. The analytics specification should reflect that behavior rather than try to repair an undefined operation afterward.
Separate two forms of duplication. The collection pipeline may deliver the same event message more than once. The application may also emit two different messages for one business occurrence. Assign responsibility for both transport deduplication and business-event uniqueness. A pipeline’s message-level protection does not, by itself, settle whether two different messages describe one session creation.
Write the intended success condition into the functional specification: the session exists with an accepted identifier. Record the failure and unknown outcomes separately. A client can show a retry state without asserting that the server rejected the attempt.
Collection gaps also need a policy. Separate development traffic, agreed test accounts, invalid payloads, and unavailable collection from the analytical population. Document exclusions instead of quietly dropping records until a funnel looks plausible. Moving an event to the server does not remove the need to follow the product’s privacy and collection requirements.
Annotate the prototype where meanings diverge
Use a Mockitt interactive prototype to make the proposed states visible: practice launcher, starting, first exercise, and a retry state. Put event labels beside the action or result they describe. Treat the server-created event as a dependency annotation, not something that a successful screen transition proves.

During review, follow one attempt across the screens. Selecting Start corresponds to the request event. The waiting state does not establish creation. The first exercise represents a successful response, while the server event is tied to the committed session. The first accepted answer is a later occurrence, even if the interface makes it easy to reach.
Then follow the timeout path. The annotation should say “outcome unknown; retry the same attempt,” rather than attach the rejection event to every error message. This is where an interface review can reveal a misleading analytics definition before code is written.
Keep the plan beside the prototype with event owners and unresolved questions. Mockitt supplies the visual reference for the discussion; the application and analytics systems must implement collection. Do not add tracking controls to the illustration or present a simulated flow as proof that events were sent.
Validate the contract, then maintain its meaning
Prepare test cases before implementation so QA can examine both the interface and the recorded events. The following are expected behaviors to verify, not results of tests performed for this article.
| Case | Expected evidence |
|---|---|
| Normal start, then one accepted answer | One attempt links to one created session and one accepted submission. |
| Timeout after the server commits, then retry | The retry resolves the same attempt; no second business creation is counted. |
| Content unavailable | A terminal rejection has an allowed reason; no session-created event belongs to that rejected attempt. |
| Replay of an accepted answer | The existing submission is recognized; it does not become a second accepted answer. |
| Client collection unavailable | Any server-only records remain identifiable; the report does not infer a complete client funnel. |
Validate required fields, types, allowed values, joins, and event timing. A valid payload can still be emitted at the wrong moment. Compare what actually happened in the test with both the event record and the implemented screen. Use design QA for visible behavior, while engineering and analytics verify collection and interpretation.
Finally, name the person who maintains each definition. If “session created” later includes a different kind of session, review the change with report owners before treating old and new data as comparable. Record effective versions and any migration rule. The tracking plan is useful only while the meaning of its events remains aligned with the product people are using.