2 April 2026 · Helena Voss
When cohort charts disagree with finance
Product counts people who did a thing. Finance counts money that stayed after refunds, chargebacks, tax, and the invoice that landed in a different month from the event. Both rooms use the word “retention.” They are not speaking the same language. App Analytics that pretends otherwise will lose the room the first time a CFO asks why recognised revenue fell while D30 rose.
A common split: product includes trial users in a cohort because they “activated.” Finance excludes them until the first non-zero invoice. Another: product timestamps on the client. Finance timestamps on the settlement file. A third: product de-duplicates by user id. Finance de-duplicates by billing account, and a family plan is one row. None of these is sabotage. They are different grains.
In the atelier we ask for a translation table, one page, three rows: metric name, product definition, finance definition, join key. If the join key is missing, stop the debate. You are not looking at a disagreement. You are looking at two datasets that cannot meet. Building the join is unglamorous warehouse work. It is also the only way a weekly readout can hold both numbers without a fight that wastes the hour.
When the numbers still diverge after a clean join, write the residual. “£41k of March recognised revenue sits in accounts that product would not call retained, because those accounts paid and did not open the app.” That sentence is more useful than a smoothed chart. It may imply a billing leak, a family-plan ghost, or a product nobody opens. Those are different projects. The cohort cannot choose among them until the residual is named.
I have sat with teams that wanted analytics to “align with finance” by quietly adopting the ledger grain in every product chart. That produces peace in the board pack and blindness in the app. Keep both. Label both. Let the argument be about the residual, not about who is allowed to have a dashboard.