learn-product-analytics-with-phoebe / Leader session 1 of 6
Learn Product Analytics with Phoebe · Leader track · Session 1 of 6

Events are the product's diary

Every chart a product team argues over is built from one kind of thing: a record that somebody did something to something at a moment in time. That record is an event, and a product only knows what it wrote down. Tonight we open one real hour of GitHub's public diary, 279,130 events long, and read it the way a product leader should: what one entry contains, why the names decide what can be counted, what a tracking plan is for, and what no diary will ever see.

🟡 Core Product managers and product leaders No code 45 min
0-3 · Welcome 3-18 · The event, the name, the blind spot 18-40 · Build-along: your first tracking plan 40-45 · Q&A
Part 0

Where we start

Product analytics is often sold as dashboards. It is really a writing discipline. Somebody decides, usually in a sprint and usually in a hurry, which moments of a user's life inside the product get written down, what each one is called, and which details travel with it. Every later question, from "is activation up?" to "did the AI feature help?", can only be answered from what was written. The six leader sessions work up from that diary to the product review; the eight analyst sessions build the same things in Python on real public data.

🎫 Clear all three quiz questions to stamp your passport Live = presented in the 45 minutes Self-study = the full depth, after class ★ Build-along = you make the artifact

★ What you walk out with tonight. A one-page tracking plan for the five moments your product most needs to know about, written in Object Action names with the properties each one must carry, and the one question that stops most analytics arguments before they start: "which event, exactly, and who decided what it means?"

Part 1 · covers what an event is

One entry in the diary

5 min live

GitHub publishes its public activity as a stream of events, and GH Archive keeps every hour of it as a file anyone can download. In the hour starting 15:00 UTC on 4 March 2024 there are 279,130 of them. Pick any one. It has the same five parts as an event in any product, whether it records a push to a repository, a song played or an invoice sent.

ONE REAL EVENT, TAKEN APART GH Archive, hour 2024-03-04 15:00 UTC, one of 279,130 entries "id": "36226160836" "actor": {"id": ..., "login": ...} "type": "PushEvent" "repo": {"id": ..., "name": ...} "public": true "created_at": "2024-03-04T15:00:00Z" "payload": {"size": 1, "ref": ...} plus "org" when the repository has one WHO · the actor an id that never changes DID WHAT · the action the name decides it all TO WHAT · the object here, a repository WHEN · the timestamp always UTC, to the second WITH WHAT · properties commits, branch Swap "PushEvent" for "Song Played" and "repository" for "track": the same five slots describe every product's diary.
🔍 Click to zoom - the gold slot is the one a product team argues about, because the name is where meaning is decided
LiveActor, action, object, time, properties3 min▶

Actor is who did it. In GitHub's diary it is an account with a numeric id and a login; the login can be renamed, the id cannot. In the one hour we opened, 7 accounts appear under more than one login. Anything that counts people must count the id.

Action and object are the verb and the noun. GitHub folds them into one type (PushEvent, WatchEvent, CreateEvent) plus a detail in the payload: a CreateEvent can create a branch, a tag or a whole repository, and only the payload says which. That is the first product-analytics trap you will meet: one name hiding three different moments.

Time is a UTC timestamp. Properties are everything else that travels with the event: how many commits a push carried, which branch a pull request targets. Properties are where the useful questions live, and they are also what gets forgotten when an event is added in a hurry.

Self-studyWhy an event and not a table of users2 min▶

A users table says what is true now: plan, country, signup date. An event stream says what happened, in order. Every product metric that involves time, activation, engagement, adoption, retention, is a question about the order of things, so it can only be answered from events. The users table is still needed, to say who a person is; the events say what they did. Session 2 of the analyst track loads a real hour of the stream and shows how different it looks from a table: most actors appear once, a few appear thousands of times.

Real world

Renamed, and counted twice. A product team counts weekly active users by username because it is readable in the export. A user renames their account on Wednesday and becomes two users that week, one who went quiet and one who arrived. At the scale of one GitHub hour that is 7 accounts; across a month of a large product it is a measurable slice of "new" users who are nobody new. The fix is one decision, written once: people are counted by an id that never changes.

Part 2 · covers Object Action naming and the tracking plan

The name decides what can be counted

6 min live

An event nobody can find is an event nobody counts. The convention most analytics teams settle on is Segment's Object Action framework: choose the objects first, then the actions a customer can take on them, so the diary reads "Repository Created", "Code Pushed", "Issue Opened". The list of those names, with the properties each must carry and who owns it, is the tracking plan.

✗ Whatever the first engineer typed click_btn_3 pushed Repo-create repoCreated new_repo_v2 three of these might be the same moment, and nobody can say which without the code ✓ A plan: Object Action, Proper Case Repository Created repo_id · owner_id · is_org Code Pushed repo_id · actor_id · commits Pull Request Opened repo_id · actor_id · base_branch Repository Starred repo_id · actor_id Issue Opened repo_id · actor_id · issue_number 67.9% of one real hour tracked by five names Five well-named events cover two thirds of GitHub's public diary. The other third is not noise: it is branches, deletions and comments, and a plan says out loud that it chose not to track them.
🔍 Click to zoom - the right panel is short on purpose: a plan is a decision about what not to track as much as what to track
LiveObject Action, and the three rules that come with it3 min▶

Segment's guidance, which most tools and teams have converged on, is to pick objects first (Product, Application, Repository) and then the actions a customer performs on them (Viewed, Installed, Created), so that an event reads "Product Viewed". Their house style is Proper Case for event names and snake_case for properties, and they are explicit that the style matters less than the consistency: "The only thing that really matters is that you keep it consistent!"

Three rules follow. Past tense, because an event records something that happened. No variables in the name: "Plan Upgraded" with a property plan = "pro", never "Upgraded To Pro", or every new plan becomes a new event nobody remembers to add to the chart. One moment, one name: if two names might mean the same thing, one of them is a bug.

LiveWhat a tracking plan is for2 min▶

A tracking plan is a table: event name, the moment that triggers it, the properties it must carry with their types, and an owner. It looks like documentation and works like a contract. Engineers implement against it, analysts query against it, and a validator can check the live stream against it. Run against the real hour, the five-line plan above finds zero type violations, and still catches 234 "Code Pushed" events with commits = 0: valid by type, wrong by meaning. That is the kind of thing only a written plan lets you notice, because only a written plan says what a push is supposed to mean.

Self-studyOne name hiding three moments2 min▶

GitHub's own CreateEvent is the cautionary example. In the hour we opened, 39,497 CreateEvents are split 27,168 branches, 10,384 repositories and 1,945 tags. A chart titled "Creates per hour" would be 69 percent branches and read as if it counted new projects. The payload knows the difference; the name does not. When you inherit a diary, the first job is to find the names that hide more than one moment and split them in the plan.

Part 3 · covers what the diary cannot see

The pages that were never written

4 min live

A diary is also defined by its blank pages. GitHub's public stream never records an account being created, never records anything in a private repository, and never records a person reading code without touching it. Every product's diary has blanks like these, and the dangerous ones are the blanks nobody wrote down.

ONE ACCOUNT'S LIFE, AND WHAT THE PUBLIC DIARY KEEPS signs up never recorded private work never recorded public push PushEvent reads code never recorded creates a repository a visible birth gives a star WatchEvent "first seen" in a window is not "new": the account is years old A repository's birth is written down; an account's is not. That one fact decides how the activation bench is built.
🔍 Click to zoom - the gold dot is the only birth the public diary sees, so the bench counts new repositories, not new people
LiveFirst seen is not new2 min▶

The tempting shortcut, when a data source has no signup event, is to call the first time an account appears in your window its start. On GitHub that is almost always wrong: most accounts active in any given hour have existed for years and were simply quiet before the window opened. A "new users" chart built that way counts people who took a holiday.

What the public diary does record is the birth of a repository: a CreateEvent whose payload says repository happens exactly once, at the moment it is created. In our hour, 10,384 public repositories were born. That is why the activation bench in analyst session 8 treats a new repository the way a SaaS product treats a new workspace, and why it says so on the widget.

Self-studyWrite the blanks into the plan2 min▶

Every tracking plan should carry a short section called "what we do not see": logged-out browsing, activity inside an integration, offline use, consent opt-outs, anything behind an ad blocker. It costs five lines and it prevents the most common executive misreading, which is treating the absence of an event as the absence of the behaviour. A product review that asks "is this a real drop, or a drop in what we can see?" is only possible if someone wrote the blanks down.

Build-along

Your first tracking plan, on one page

★ 22 min · everyone builds

No tools and no data access. A blank sheet, your product, and five decisions per row. This is the artifact every later leader session leans on: session 2 picks your activation candidate from it, session 4 adds the features you want adopted, session 5 adds the AI feature.

EVENT (OBJECT ACTION) FIRES WHEN PROPERTIES : TYPE OWNER ANSWERS Workspace Created first workspace saved server side, once workspace_id : str plan : str · is_invited : bool growth PM new workspaces a week, by plan four more rows, yours What we do not see: five lines Five rows, five columns, one blank-pages tab. Nothing more tonight.
🔍 Click to zoom - the example row is constructed; the five columns are the part to copy

List the five moments your product most needs to know about. Not the five easiest to track. Start from the decisions your team makes every quarter: whether onboarding works, whether a feature is used, whether people come back. Each decision needs at least one moment, and one of the five should be the birth of the thing your product is organised around: an account, a workspace, a project.

Name each one Object Action, past tense, Proper Case. Choose the objects first, then the verbs. If a name needs a variable in it, move the variable into a property.

★ Five names, checked against three rulesWorkspace Created past tense, object first, one moment Invite Sent not "Invited User To Workspace" Report Exported format goes in a property: format = "csv" Plan Upgraded plan goes in a property: plan = "pro" Integration Connected provider goes in a property: provider = "slack"

Write the trigger precisely. "When the user creates a workspace" is not a trigger. "When the first workspace is saved, server side, fired once" is. The difference is whether a double click, a retry or a page reload fires it twice, and that difference decides whether the count is true.

Give each event two to four properties, with types, and an owner. An id for the object, an id for the actor, and the one or two details a question will need. The owner is the person who says what it means when two dashboards disagree.

★ One row, completeEvent: Workspace Created Fires when: first workspace saved, server side, once per workspace Properties: workspace_id : str owner_id : str plan : str is_invited : bool Owner: growth PM Answers: how many new workspaces a week, by plan, and how many arrived by invite

Write the blank-pages tab. Five lines on what the plan cannot see, and why. Logged-out use, activity inside partner integrations, users who declined tracking. Put the date on the page. You will come back to it in session 6.

✗ The plan that becomes three dashboards and an argumenttrack clicks on all buttons, page views, and anything else useful name events whatever makes sense to the engineer we can clean it up later in the warehouse

Later never comes cheaper. An event that shipped with the wrong name has history under that name. Renaming it splits the chart at the rename date or needs a mapping forever. Twenty minutes on the plan before the sprint is the cheapest analytics work there is.

Homework

Before session 2

30 min
  1. Find your product's current event list, in whatever tool holds it, and count the names. Mark the ones that break a rule: present tense, a variable in the name, two names for one moment.
  2. Find one name hiding several moments, the way GitHub's CreateEvent hides branches, tags and repositories. Write down what share of that event is each moment, or who could tell you.
  3. Check how people are counted. Ask the analyst which field defines a user in the weekly active chart. If the answer is an email or a username, you have found a renaming problem.
  4. Bring the one-page plan. Session 2 asks which of its five moments predicts that someone stays, and the answer is usually not the one you would guess.
Check yourself

Three questions

Clear all three and leader session 1 gets stamped in your passport.

1 · In one real GitHub hour, 7 accounts appear under more than one login. What does that mean for a weekly active users chart?

A login is a label; an id is an identity. Renames are ordinary behaviour, and a count keyed on the label quietly inflates both "churned" and "new". The decision belongs in the tracking plan, written once.

2 · Which event name follows Object Action and the no-variables rule?

Object first, past-tense action second, and the thing that varies travels as a property. "UpgradedToPro" becomes a new event for every new plan, and nobody remembers to add it to the chart.

3 · GitHub's public stream has no signup event. Why does the activation bench count new repositories rather than accounts first seen in a window?

A cohort has to start at a moment the diary actually records. When the birth you care about is a blank page, pick a birth that is written down and say plainly what it stands for.

Sources covered

What this session teaches, and where it came from

✓The GitHub event object: id, type, actor, repo, payload, public, created_atGitHub Docs, "GitHub event types"; every count on this page measured on the GH Archive hour 2024-03-04 15:00 UTC in this build
✓Object Action naming, Proper Case events, snake_case propertiesTwilio Segment, "Naming conventions for clean data", read at source
✓The tracking plan as a contract, and the validator resultStandard practice; the five-row plan run against the real hour: 67.9 percent tracked, zero type violations, 234 zero-commit pushes
✓What GH Archive can and cannot seeGH Archive records the public timeline only; no signups, no private repositories
◐The Workspace Created example rowConstructed for the build-along; not from any real product

Every fact on this page, and its verification tier, is recorded in the course's source map.

Leader session 1 cheat sheet · pin this

Five parts of an eventWho, did what, to what, when, with which properties.
Count people by idLogins and emails change. 7 renames in one GitHub hour.
Object Action, past tenseRepository Created, Plan Upgraded. Variables go in properties.
The plan is a contractName, trigger, typed properties, owner, the question it answers.
One name, one momentCreateEvent hides branches, tags and repositories. Split them.
Write the blank pagesWhat the diary cannot see. First seen is not new.