DocsGuides

Guides

Create a tracking plan and test it

4 min readReviewed July 2026

A tracking plan connects a decision to the events and properties required to make it. It prevents two expensive outcomes: collecting a large event stream that cannot answer the question, and discovering after launch that a key identifier or property was never sent.

The plan can live in a spreadsheet or Markdown file. Its value comes from review and ownership, not the tool that stores it.

Once the written plan is stable, turn its highest-value rows into executable event contracts so producers, fixtures, and CI enforce the same rules.

Begin with the decision#

Do not start by listing every button. Start with a small set of questions:

Question: Which accounts reach first value within seven days?
Decision: Improve or shorten the onboarding step with the largest loss.
Metric: Accounts that create a project and publish a report within seven days.

That definition implies events:

account_created
project_created
report_published

and properties:

account_id
user_id
plan
source
schema_version

If the team cannot state what decision a proposed event supports, defer it until there is a concrete use.

Use one row per event#

Download the tracking-plan CSV template, or begin with this structure:

Column Purpose
Event name Stable past-tense name
Description Exact action represented
Trigger Where and when code emits it
Owner Person or team responsible
Producer Service, job, or integration
Identifiers user_id, account_id, and object IDs
Properties Name, type, unit, and allowed values
Sensitive fields PII or confidential-data review
Retention Intended collection and lifecycle
Version Meaning or type version
Status Proposed, implemented, verified, or deprecated

Keep the plan in the same review flow as the code. An event added in a pull request should update the plan in that pull request.

Write a complete contract#

Example:

event: report_published
description: A report became visible to its intended audience.
trigger: After the database transaction commits successfully.
owner: Reporting team
producer: reports-api
collection: product_events
identifiers:
  user_id: string, required
  account_id: string, required
  report_id: string, required
properties:
  visibility: enum[private, account, public]
  template: string, optional
  duration_ms: integer, required
sensitive_fields: none
schema_version: 1

The trigger should distinguish attempts from outcomes. report_publish_clicked and report_published answer different questions. Prefer the completed business fact when measuring conversion or value.

Define every property#

For each field, record:

  • type
  • required or optional
  • unit
  • allowed values when bounded
  • whether it can change
  • sensitivity
  • example

For money:

amount: integer minor units
currency: lowercase ISO currency code

For duration:

duration_ms: non-negative integer milliseconds

This is enough detail for another engineer to implement the event without guessing.

Plan identity and account scope#

State whether the event belongs to:

  • an anonymous visitor
  • an authenticated user
  • an account
  • a background system
  • more than one of these

For B2B products, include both user_id and account_id when a person acts inside a customer account. Read users, accounts, and identity before finalizing those fields.

Review privacy before implementation#

Mark fields that can contain:

  • names or contact information
  • network and device identifiers
  • user-authored text
  • URLs with query parameters
  • financial or health information
  • secrets or credentials

Remove fields that do not support a defined use. Prefer opaque internal IDs. Set the destination collection and retention deliberately.

Never approve passwords, authentication tokens, API keys, payment card data, session cookies, or raw private documents for analytics.

Version breaking changes#

Increment schema_version when:

  • a field changes type or unit
  • an event changes meaning
  • required identifiers change
  • one action is split into several
  • previously distinct actions are combined

Additive optional properties usually do not need a version.

Do not silently reuse an existing field name with a new meaning. Historical events keep the old value, so a query across the change would mix two contracts.

Test before release#

Use a non-production collection or workspace and verify one event for every meaningful branch:

success
expected failure
optional field absent
each bounded enum value
anonymous and authenticated path
each relevant account role

In Samples, confirm:

  • event and property names
  • identifiers
  • types
  • Unix-second timestamp
  • no unexpected sensitive fields
  • payload size
  • source event_id

Then run a query that answers the original question. A valid JSON object is not necessarily a valid analytics contract.

Reconcile a known scenario#

Create a deterministic test:

3 accounts created
2 projects created
1 report published

The chart or SQL query should return those exact values. Test unique users and unique accounts separately. Check the boundary of the selected time zone and date range.

For a migration or backfill, validate a small date window before importing the full history.

Release with an owner#

Before merging:

  • the producing team approves the trigger
  • the analytics owner approves the metric definition
  • a security or privacy owner reviews sensitive fields when needed
  • the event plan and implementation use the same names and types
  • the dashboard or query is tested against known data
  • failure handling is observable

After release, watch event volume, missing identifiers, unknown enum values, and type drift for at least one complete reporting period.

Deprecate instead of forgetting#

When an event is replaced:

  1. mark it deprecated in the plan
  2. record the replacement and date
  3. update saved queries and dashboards
  4. stop emitting it
  5. keep its definition while historical data remains

Deleting the row from the plan erases context that old dashboards still need.

Minimal plan for a new product#

Start with five to ten events around one journey:

account_created
member_invited
project_created
report_published
subscription_started

Prove that those events support activation, retention, and revenue questions. Expand from real decisions rather than instrumenting the entire interface in advance.

Use event schema design for naming and type conventions, and environments and testing for the release setup.

After release, turn the critical rows in the plan into ongoing checks with Monitor instrumentation health.

Need a hand?

Tell us what you’re building and we’ll point you in the right direction.

Contact support