Where we start
Six sessions ran on real GitHub rows. This one cannot: no AI-feature event stream is public, so tonight's data is constructed. A short numpy script, seed 2022, generates one working week for two versions of a code assistant, 200 developers each: "short" suggests a line, "long" suggests a whole block. Every rate inside it is an assumption written in the script, and every number on this page is what that script prints. The definitions, and the findings about what the metrics mean, come from a real study read at source: Ziegler et al. (2022), "Productivity Assessment of Neural Code Completion".
★ What you walk out with tonight. An event schema for an AI suggestion, a pandas table that computes three competing metrics per version, the habit of reporting the per-user spread next to any average, and a sentence you can say in a product review: "which of these metrics matches the decision we are making?"
One suggestion, followed for ten minutes
6 min liveA normal product event records something that happened once. An AI suggestion is a small story: it is shown, then accepted or not, then, if accepted, it survives, gets changed, or disappears. The schema has to carry the whole story on one row, or the metrics that read its later chapters cannot be computed. Tonight's stream is constructed, and its row is the minimum that makes three metrics possible.
LiveThe row, field by field3 min▶
The event is suggestion_shown, one row per offer. arm says which version of the assistant made it, dev says who saw it. accepted is the moment the developer took it. kept_30s and kept_600s say whether the accepted code was still there, unchanged, 30 seconds and 10 minutes later; deleted_600s says it was gone by 10 minutes. chars is the size of the offer. Every later flag is only meaningful when accepted is true, which is why the metrics divide by accepted rows, not by shown ones.
In a real product the later flags arrive later, as their own events joined back to the suggestion by an id. Storing them as columns on one row, as tonight's stream does, is the shape you end up with after that join. Plan the suggestion id before the feature ships: without it, "kept at 600 s" cannot be tied to the offer it describes.
Self-studyZiegler's definitions, read at source2 min▶
Ziegler et al. define acceptance rate as "the fraction of completions shown to the developer that are subsequently accepted for inclusion in the source file". In code that is accepted.sum() / shown, or in pandas the mean of a boolean column. For what happens after acceptance they measure persistence at four checkpoints, 30, 120, 300 and 600 seconds. Tonight's stream keeps two of them, 30 and 600, which is enough to see the difference between a quick check and a lasting one.
Their productivity outcome came from a survey built on the SPACE dimensions (they used S, P, C and E), matched to each respondent's usage: 17,420 users were emailed and 2,047 responses were matched to usage data, between 10 February and 6 March 2022.
Same name, different fraction. In the Ziegler study Copilot's acceptance rate was 27 percent, and the paper mentions a click-through rate of 10 percent for IntelliCode Compose. Those two numbers look comparable and are not: different products, different ways of showing a suggestion, different denominators. Before putting two AI features' "acceptance" side by side, read what each one divides by. The definition is part of the number.
Three metrics, two answers
5 min liveRun the constructed week and ask which version of the assistant is better. Acceptance says "long": 0.309 against 0.262. Characters kept per suggestion shown says "long" by a wide margin: 20.183 against 6.486. Persistence says "short": 0.618 of its accepted suggestions are still there, unchanged, at 600 seconds, against 0.408 for "long". None of the three is wrong. They answer different questions.
LiveWhat each metric is a proxy for3 min▶
Acceptance rate measures the moment of the offer: did the developer think it was worth taking. Persistence measures what the code turned out to be worth: of the accepted suggestions, how many were still there, unchanged, ten minutes later. "Long" is accepted more (0.309) and survives less (0.408 kept, 0.190 deleted by 600 seconds); "short" is accepted less (0.262) and survives more (0.618 kept, 0.078 deleted). Characters kept per shown multiplies survival by size, and a block that survives less often still leaves more text behind than a line that survives more often: 20.183 against 6.486.
So the question is never "which metric is right". It is "which decision are we making". Choosing a default for developers who hate cleaning up after the tool points at persistence. Estimating how much typing the tool saves points at characters kept. Neither table proves that switching versions would change anything for real users: that needs an experiment (see Learn Experimentation with Phoebe).
Self-studyThe 30-second check is kinder than the 600-second one2 min▶
At 30 seconds "long" keeps 0.693 of its accepted suggestions and "short" keeps 0.800. At 600 seconds the figures are 0.408 and 0.618. The early check flatters both arms, and in this constructed stream it flatters the block more, because the script assumes a big suggestion is more often rewritten after the first glance. Pick the checkpoint before you look at the results, and report it with the number: "kept" without a time is not a definition.
The metric that tracks perception. Ziegler et al. found that "the rate with which shown suggestions are accepted, rather than more specific metrics regarding the persistence of completions in the code over time, drives developers' perception of productivity". Acceptance rate correlated best with aggregate perceived productivity, rho = 0.24, P < 0.0001, and every persistence measure correlated less well. The authors add that "there is still notable unexplained variance". Read it carefully: acceptance predicts how productive developers feel, modestly, in one study. It is a correlation with a survey answer, not proof of value, and it does not make persistence useless for the decisions persistence is built for.
The average is not a developer
4 min liveAn arm's acceptance rate is one number made of 200 developers. In the constructed stream, the middle 80 percent of "long" developers accept between 0.177 and 0.467 of what they are shown; for "short" the range is 0.127 to 0.390. The two ranges overlap over most of their width. Many individual developers on the "worse" arm accept more than many on the "better" one, and a single average says none of that.
LiveReport the spread with the average2 min▶
The fix is one extra line of pandas: group by developer first, take each developer's acceptance rate, then describe that distribution with its 10th and 90th percentiles. Put the band next to the arm average in every review. It changes the conversation from "long is better" to "long shifts the whole band up a little, and the band is wide", which is the honest reading.
Two cautions. First, 200 developers per arm is a small group: percentiles from groups that size move noticeably with the seed, so treat the band's edges as rough. Second, the average of per-developer rates and the pooled rate are different numbers when developers see different amounts; a heavy user counts once in the first and many times in the second. Say which one you report.
Self-studyThe same metric moves with the clock2 min▶
Ziegler et al. report that acceptance also varies with time: 23.5 percent at weekends, 23 percent on weekdays outside working hours and 21.2 percent in working hours. So a change in the mix of when people use the tool can move the headline rate with no change in the tool at all. A launch that happens to land near a holiday, or a team that shifts its hours, is enough. Break acceptance down by time of use before reading a week-on-week movement as the feature getting better or worse.
The vendor says it too. GitHub's own documentation describes acceptance rate as a measure of "how often developers accept Copilot's suggestions", and advises readers to "look for patterns across these signals rather than focusing on any single number". That is the spread rule and the three-metrics rule in one sentence, from the people who ship the tool. When a dashboard shows one acceptance number for a whole organisation, ask for the distribution and the other signals before anyone draws a conclusion.
Build the constructed stream and compute all three metrics
★ 22 min · everyone buildsnumpy and pandas, no downloads. The script below generates the constructed week from seed 2022 and prints every number on this page. Run it exactly as written and you get the outputs in the comments; change the seed or any assumed rate and your numbers will differ, which is the fastest way to feel how much a metric depends on what you assumed.
Write the assumptions down. Two arms, and for each the rates the generator will use. On constructed data this block is the whole truth; everything later only reveals it.
Generate a week per developer. Each developer gets their own acceptance probability from a beta distribution, so developers differ, and a Poisson number of suggestions. One uniform draw per suggestion decides what happens after acceptance, so "kept at 600 s" implies "kept at 30 s".
One table. Concatenate and count the rows: one per suggestion shown.
Three metrics, grouped by arm. Acceptance divides by shown; the kept and deleted rates divide by accepted; the character metrics divide by shown, so they combine how often, how much and how long.
Look under the average. One rate per developer, then the 10th and 90th percentiles per arm.
The comments are shortened column names. pandas prints the full names from the dictionary and its own spacing; the values are the ones above. If your run disagrees in the third decimal, check your numpy version before your code: numpy does not promise identical distribution draws across releases, even from the same seed.
Before session 8
30 min- Add the missing checkpoints. Ziegler et al. measured persistence at 30, 120, 300 and 600 seconds. Add
kept_120sandkept_300sto the generator with rates you choose, write them down as assumptions, and plot kept-of-accepted against time for both arms. - Flip the verdict on purpose. Change one assumed rate in
ARMSuntil characters kept per shown favours "short". Write one sentence on what that rate would have to mean about real developers for the flip to be believable. - Pooled versus per-developer. Compute each arm's mean of per-developer acceptance rates and compare it with the pooled acceptance rate from step 4. Explain in one line why they can differ.
- Draft the review line. For a product review, write the three metrics, the per-developer band and the word "constructed" on one slide, and one sentence saying which metric you would pick for a default-setting decision, and why.
Three questions
Clear all three and analyst session 7 gets stamped in your passport.
1 · On the constructed stream, "long" has the higher acceptance rate (0.309) and "short" the higher kept-at-600-seconds rate (0.618). What is the right conclusion?
Acceptance reads the moment of the offer, persistence reads what survived. Disagreement between them is information, and on constructed data it only shows how the metrics behave, never which product is better.
2 · What did Ziegler et al. find about acceptance rate and perceived productivity?
A modest correlation with a survey answer, in one study. It says acceptance tracks how productive developers feel, not that it measures the value of the code.
3 · Why report the per-developer 10th to 90th percentile next to an arm's acceptance rate?
The band shows whether a difference between arms is large next to the difference between people. With groups this small, treat the band's edges as rough too.
What this session teaches, and where it came from
Every fact on this page, and its verification tier, is recorded in the course's source map.
Analyst session 7 cheat sheet · pin this
Back to real rows. Session 8 is the activation bench: 8,348 new GitHub repositories, eight candidate definitions of "activated", their lifts and the returners each one catches, and a break button that shuffles the link away so you can see what no signal looks like at every group size.