Build a tracking plan your team can actually maintain

Build a small tracking plan around a product question. Specify events, properties, identity, validation cases, and ownership, with an example event contract.

The ClickClacks team · 6 min read

The ClickClacks events list on sample data: recent events with their names, the person behind each one and the time.

A tracking plan is a shared specification for the events your product records: what each event means, when it fires, which properties it carries, and who keeps it correct. Its purpose is to make a product question answerable without everyone reconstructing the implementation from memory.

You can start with a spreadsheet or a document. The important choice is the scope. Pick one decision your team needs to make, specify the few events required to support it, and test them. A catalog of every clickable element is harder to maintain and may still miss the outcome you care about.

This walkthrough builds a small plan for a fictional file-import workflow. The event names and payload are examples, not a required ClickClacks SDK format. You will need access to the application code, a local or test environment, and someone who can confirm what import success means.

Begin with a question that leads to action

Suppose the team asks, “Where do new workspaces get stuck before their first usable import?” The answer could change file validation, background processing, or the explanation shown after an import finishes.

List the states that distinguish those possibilities. An import was requested. The request was rejected before processing. Processing began. Processing succeeded or failed. The user opened the resulting dataset. Those states are more helpful than a long list of clicks on buttons that happen to appear along the way.

Agree on the outcome. Does “success” mean a worker finished, at least one row was accepted, or the user saw usable data? Those may deserve separate events. Combining them into an ambiguous import_done event would hide the very problem you are trying to investigate.

Write one complete event contract

Start with import_completed. Define it as: “The import service commits the final result of an import job that accepted at least one row.” It fires once per job’s successful completion, after the result is durable. An empty import has a separate outcome and does not satisfy this definition.

For each event, record the name, business meaning, exact trigger, emitting service, identity fields, property types, allowed values, owner, and validation cases. A tracking-plan specification reference shows the role of events and property rules in a maintained schema. The same contract discipline is useful even when your plan lives in a plain document.

Here is an illustrative payload for the completed event:

{
  "event": "import_completed",
  "event_id": "evt_example_01",
  "occurred_at": "2026-09-10T10:15:00Z",
  "workspace_id": "ws_example",
  "import_id": "imp_example",
  "schema_version": 1,
  "properties": {
    "file_type": "csv",
    "rows_accepted": 120,
    "rows_rejected": 3
  }
}

The numbers and identifiers are fictional. Notice what is absent: file contents, customer email addresses, and raw rejected rows. None is needed to locate a workflow failure at this stage.

Infographic: an event schema in a tracking plan. Illustrative import_completed event schema: workspace and import identifiers, accepted and rejected row counts, and file type.

The contract describes behavior. The SDK call is an implementation detail that must match it.

Decide where each event belongs

The browser knows when someone opens the import dialog. The server knows whether an import job commits successfully. Put each event where its meaning can be established reliably.

Do not emit the same success event independently from both places unless you have a deliberate deduplication design. A user retrying a request or reloading a success page should not create a second completed job in the analysis.

Distinguish a logical event from a delivery attempt. A stable event identifier can let a receiving system recognize retries; an import identifier lets you group the workflow’s different events. These identifiers solve different problems. Document whether your chosen analytics pipeline actually deduplicates events, because adding an ID to a payload does not guarantee that behavior.

Use names that survive a redesign

Name events after domain actions, such as import_requested or dataset_opened, rather than visual elements such as blue_button_clicked. A button can move or change color without changing the meaning of the action.

Choose a consistent convention and write it down. The specific capitalization matters less than using one canonical name and definition. Put variable details in properties: file_type: csv is easier to analyze than inventing separate event names for every supported format.

Keep property vocabularies bounded where possible. A failure category like invalid_schema is useful for grouping. An unrestricted exception string can fragment the report into hundreds of values and may accidentally include sensitive input. Preserve detailed diagnostics in an appropriately controlled engineering system.

Make identity part of the plan

Write down which identifier represents a person and which represents a workspace. Keep workspace membership separate from personal identity: someone who belongs to two workspaces remains one person, but their actions may belong to different accounts.

For anonymous activity, define how and when a known identifier becomes available. Do not casually join people by a shared name or by whichever email address appears in an event property. Identity mistakes can make a healthy workflow appear to lose users halfway through.

Also decide how internal users and test data are marked. Apply those exclusions deliberately in reports. Deleting every strange-looking event after the fact makes it difficult to tell instrumentation problems from inconvenient product behavior.

Test the contract with real transitions

For the import example, build a small acceptance checklist: one successful file, one file rejected before processing, one job that fails during processing, and one retried delivery of the same logical success event. Check both event contents and event counts.

The success case should produce the expected completed event with integer row counts and the right workspace identifier. The rejected request must not appear as a completed import. The retry should behave according to the pipeline’s documented deduplication rule. Record the result rather than assuming it.

Then reproduce the product question. Can you follow a test import from request through its final state and into the first dataset view? If a stage cannot be joined, fix the identity fields before building a polished dashboard.

Infographic: validating an event before release. Define, implement, validate, and release an event. Check valid imports, empty imports, and retried deliveries.

Validate the question end to end, not just whether an event reached the collector.

Maintain the plan alongside the code

Assign an owner to each event family, not necessarily each individual event. An engineer can own implementation while a product manager owns the business definition. The important point is that a proposed change has someone accountable for its effect on existing reports.

Include tracking changes in the same review as the feature. Ask whether an event is added, removed, renamed, or redefined. A property added without changing existing semantics may be compatible. Changing import_completed from “durable result” to “processing started” is a breaking definition change even if the JSON structure stays identical.

For a breaking change, introduce a version or a new event, document the transition date, and update dependent reports. Keep old definitions discoverable. Otherwise a future analyst may compare two periods that use the same label but count different behavior.

Keep the first version small

Your first plan is complete when it answers the chosen question reliably, not when it lists every possible event. Review unused events periodically and retire collection that has no justified purpose. A smaller plan is easier to validate when the product changes.

The analytics API page explains the collection interface available in ClickClacks. Use the current docs for implementation details, then check the emitted events against your own contract. This article’s payload is a design example; it is not a tested request against that API.

Before handing the plan to a teammate, ask them to implement or query one event using only the document. Every clarification they need is a place where the plan can become more precise. That small exercise is often a better quality check than adding another column to the template.

All posts

Start hearing your clicks.

Install one script tag and see your first report in minutes.

About 5 KB · pageviews, clicks and scroll depth, automatically