Should I vibe code
Subscription metrics, revenue reporting, segmentation, and billing data
Two billing systems, one customer, two email addresses. That is not a charting bug, it is revenue counted twice.
?
Their verdict, the Starter price and the build-time estimate come from their entry, MIT-licensed. Checked 2026-08-04.
?
Our verdict, the regret score and everything below it. Editorial and unsponsored — nobody can pay to be moved.
The honest answer
why the verdict is what it is
Baremetrics' entry covers the metric definitions and it covers them well — take the annual-plan trap, the churn denominator and the FX-rate question as read. ChartMogul's separate problem starts one step earlier, at ingestion. Its actual job is to take Stripe, an App Store subscription, a Recurly account from before the migration, and the four enterprise deals that live in a spreadsheet, and decide which of those records are the same customer. That is entity resolution, it is genuinely hard, and getting it wrong does not produce a chart that looks wrong — it produces one customer counted twice, a migration that reads as a wave of churn plus a wave of new business in the same month, and a segment breakdown where the same logo appears in two countries. Add the customer attributes you sync in to make segmentation useful, and the fun Stripe dashboard has quietly become a small CRM full of other people's data, sitting next to a live API key.
What actually breaks
not "if". the specific failures.
- Identity across sources. The same company subscribes on Stripe under billing@, renews on the App Store under a personal Apple ID, and is invoiced manually as a PO number. Merging them is a judgement call; not merging them is double-counting, and both are invisible in the total
- Billing migrations, which look exactly like a catastrophe. Move providers and every subscription cancels in one system and appears in another, so a clean migration renders as total churn followed by record new business unless you taught the pipeline that they are the same contracts
- In-app purchases, where Apple and Google report net of their cut, in their own currency, on their own schedule, with no email address attached. Reconciling that against a Stripe-shaped model is most of the work and none of the demo
- Manual and CSV imports, which are the only way enterprise contracts get in and the one path with no schema enforcement. One duplicated row and last quarter grew
- Restatement. Refunds, backdated cancellations and late invoices rewrite periods you already reported, so the honest system stores immutable events and recomputes, and the quick one queries live state and silently changes history
- The customer attribute store. Segmentation by plan, country, company size and CRM field means names, emails and firmographics in your database, which is a disclosure obligation rather than an analytics feature
- The API key. This needs read access to billing and nothing else, but the setup path of least resistance is a full secret key in an .env on the same box as the dashboard — one server-side request forgery away from being able to issue refunds
- Connector rot, forever. Every billing provider changes an API version on their own schedule, and each one you add is another thing that can silently stop syncing while the chart keeps drawing yesterday's line
Is that you?
the verdict is a default, not a law
- There is exactly one billing source and there always will be, which removes the entire argument of this entry
- The number is for you, on a laptop, to answer "is it going up" rather than to be quoted at anyone
- It reads from a restricted, read-only billing key and stores no customer attributes beyond an opaque id
- You are computing it from immutable events, so recomputing last March produces last March
- Two billing systems feed it and nobody has written down the rule for deciding that two records are one customer
- The output goes into a board deck, an investor update or a data room, where the follow-up question is how you defined it and the answer needs to survive somebody else's spreadsheet
- It holds names, emails and company data for every customer with the security posture of a side project
- The Stripe key in it is a full secret key, because at that point the risk stopped being wrong numbers
If you build it anyway
the checklist, then the prompt that enforces it
- Ingest immutable events, never live state. Store the billing provider's webhook payloads as an append-only log and compute every metric as a function of that log, so history is reproducible and a restatement is visible rather than silent.
- Make identity resolution explicit and reviewable: one customers table, a deterministic match rule you wrote down, and a queue of ambiguous matches a human confirms. Never merge on display name.
- Snapshot every reported period. When you publish a number, freeze the inputs that produced it, so "the chart changed" becomes an answerable question.
- Use a restricted, read-only billing key scoped to the resources you need, and keep it out of the process that serves the dashboard. Analytics never needs the ability to refund.
- Store the minimum customer data that makes segmentation work, keep a deletion path, and remember that this database is now in scope for every privacy promise your product makes.
- Reconcile against cash monthly: your revenue model versus actual payouts. A recurring reconciliation catches connector rot and double-counting long before a board meeting does.
- Label every chart with the definition and the version of it. "MRR (annual plans amortised, net of discounts, gross of fees, v3)" is not clutter, it is the thing that makes the number arguable.
I am building subscription revenue analytics over more than one billing
source. The metric definitions are a known trap and I have read that argument
elsewhere; the risk you are guarding against here is ingestion — duplicated
customers, silently rewritten history, and a live billing key. Work in this
order.
1. Before any chart, design the event store: raw provider payloads, appended,
never updated, with provider, source id and received time. Every metric is a
pure function over that log.
2. Ask me for a restricted read-only API key. If I hand you a full secret key,
refuse it and tell me what a request-forgery bug in the dashboard would then
be able to do.
3. Build identity resolution second and make it visible: a customers table,
external identities attached to it, a written deterministic match rule, and
a review queue for anything ambiguous. Never auto-merge on name or company.
4. Write the migration test now: same customer cancelling in provider A and
appearing in provider B in the same month must not read as churn plus new
business. Show me that test failing before you make it pass.
5. Handle App Store and Play Store separately and honestly — net of platform
fee, in the reported currency, with no email address — rather than forcing
them into a Stripe-shaped schema.
6. Any CSV import path validates, dedupes and shows me a diff before writing.
Manual import is where the phantom quarter comes from.
7. Every published number gets a snapshot of its inputs and a definition
string, including whether annual plans are amortised and whether figures are
gross or net of fees and tax.
8. Add a monthly reconciliation report: modelled revenue against actual payouts
per provider, with the difference explained. Do not skip this.
9. Keep customer attributes to the minimum that makes segmentation useful, and
give me a delete-a-customer path from day one.
10. Out of scope until I ask: dunning, emails to customers, anything that
writes back to the billing provider. This is a read-only system and it
stays one.That one keeps you out of trouble. For the prompt that actually builds it, canivibecodeit.com has one.
their build prompt ↗Or don’t build it
the boring option, and the way back out
The moment a second billing source exists, or the moment the number leaves your laptop. Sixty-nine dollars a month buys connectors that already know what an App Store subscription is, a merge workflow for customers who signed up twice, and — the part that matters in a diligence call — a methodology someone else published, which your investor has seen before. Note the pricing shape too: it is free below $10K MRR and scales with what you track, so it is cheapest precisely while your homemade version would still be fine, and costs real money exactly when the numbers become load-bearing.
$69/mo is cheaper than your weekend.
Good, if the event log is real. An append-only store of provider payloads plus SQL that turns it into metrics is portable by construction: point Metabase at it, or hand the same log to a vendor and let them recompute. Where it goes wrong is the version where the dashboard queries Stripe live and the "database" is a cache — then there is nothing to export, the historical numbers cannot be reproduced, and switching to a real tool means starting the history from the day you signed up. Keep the log, keep the definitions in version control, and the exit is a weekend.
Open-source connectors for Stripe, Recurly, Chargebee and friends — the ingestion half, already written.
Version-controlled, tested SQL transformations, which is the honest shape for metric definitions that have to be defended.
Open-source BI layer over a dbt project, if what you want at the end is charts rather than a product.
Questions
Baremetrics is already here and it is also subscription analytics. Which entry applies to me?
If you bill through one provider, read Baremetrics — its argument is about how MRR is defined, which is the trap that catches everyone with a single Stripe account. Read this one when there are two sources: a billing migration, an App Store subscription, an old Recurly account, or enterprise contracts invoiced by hand. At that point the definitions are still hard, and a second, sharper problem sits in front of them, which is deciding who is the same customer.
Why is a duplicated customer worse than a wrong formula?
Because a wrong formula is consistent. If your churn denominator is unconventional, everything moves together and someone can convert your number once they know the rule. A duplicate is noise: it inflates counts, splits one account's expansion across two rows, produces churn events that never happened, and shows up differently in every segment cut. There is no correction factor for it, only a reconciliation.
Can I skip the event log and just query Stripe when the page loads?
You can, and it demos beautifully, but you have built a viewer rather than an analytics tool. Live queries mean March's chart is recomputed from today's state, so a refund issued this morning quietly edits a number you reported to your board in April, and you have no record that it changed. Storing raw events and recomputing costs an afternoon and buys you a history that reproduces.
Is the customer data really a risk? It is just names and plans.
It is names, email addresses, company, country and whatever CRM fields you synced in to make segmentation interesting — for every customer you have. That is squarely personal data, it is now in a database whose backup strategy is whatever your agent chose by default, and your privacy policy already made promises about it. Segmentation is the feature that turns a chart into a personal-data store, and it does so without anyone deciding to.
Every week, someone ships something they shouldn’t have.
New verdicts, the worst thing that landed in the trap, and the occasional incident report. No other email, ever.
MRR isn't a number you read off Stripe. It's a dozen accounting decisions, and you'll make all of them by accident.
Attribution is not a measurement, it is an opinion. Yours will be wrong in a way that looks fine.
June gave customers 30 days to export their history. That is the dependency you were calling "buying".
last reviewed 2026-08-04 · verdict is editorial and unsponsored · shared entry data from canivibecodeit under MIT · not legal advice