A pricing case study · ☕ café au point

The Coffee
Experiment

How should Cafezinho, a cute Brazilian coffee app, price its subscription? We find the profit-maximising price — then learn why measuring it is the hard part.

Why I built this

My pricing class taught me how supply-and-demand curves set prices for physical commodities. I wanted to point those methods at software.

In the microeconomics class of my INSEAD Executive MBA, we worked through the classics — demand curves, elasticity, the profit-maximising price. But every example was a physical thing: oil, aluminium, Apple Watches, physical units shipped. I’m a PM. I wanted to do two things the coursework didn’t: take those methods to a software product, and test out A/B testing — a validation toolkit I’d thought about for years and never actually run.

So I made up an app to do it on. “Cafezinho” is a fictional app, which is exactly the point: with an invented product I can test various scenarios and extend the example with data I create, while basing it in reality.

Q1

How do textbook pricing methods actually apply to a software product?

Q2

How would you design and use an A/B test to validate the price you chose?

Fictional app, illustrative numbers, real method. I’m working in the open — every figure here is one I’d defend or revise out loud. — Sally

The one idea

One price isn't an answer. It's a point on a curve.

Cafezinho sells a monthly subscription. Charge too little and they leave money in the tip jar; charge too much and customers walk. Somewhere between free and absurd sits a price that does the most work. Part 1 finds it. Part 2 asks the harder question: once you have a candidate price, how do you prove it's better (without fooling yourself by developing contrived tests)?

The through-line

One founder's question, answered in three moves

The founder wants to jump the price from R$14.9 to R$24.9. Before I touch a slider or run a test, I work through three questions in order — price first, then whether a premium tier earns its build, then whether any of it can be proven with an experiment. Here's the path, and where each move lives on the page.

1 Part 1
Find the price that does the most work
Fit the demand curve from two measured price points, then use economic first principles to land the profit-maximising price for the paid tier — the one that maximises monthly profit (~R$22), not just revenue. The founder's R$24.9 is directionally right, but overshoots it.
2 Part 1
Weigh whether a premium tier is worth building
The café-pros barely flinch at price, so should we build them a higher "Roaster" tier at ~R$30.6? That's an investment call, not a pricing one — it hinges on whether the ~R$ 212/month it could earn ever repays the engineering to build it.
3 Part 2
Establish whether this case can even be A/B tested
Part 1's demand curve was estimated — in real life you have to measure it, usually with an A/B test — so before trusting one, I check whether Cafezinho's ~150 visitors/arm can even see the effect we're chasing, because a test you can't power is a test that lies.
Meet Cafezinho — the setup behind the numbers

What it is. A cute Brazilian coffee app (iOS & Android). The free Beans Log lets you track the beans you've bought, rate them, and remember what worked. The subscription unlocks the Recipe Suggester — a weekly "dial-in" (grind, dose, water, time) tuned to the exact beans and gear you own.

Free · Beans Log
Track & rate the beans you buy. Fills the funnel; costs almost nothing to run.
R$14.9/mo · Recipe Suggester
Weekly dial-in recipes + everything in Free. This paid upgrade is what we're pricing.
~9,000
free users
~720
paying subscribers
R$14.9
current price / mo
~8%
paywall conversion
~6% / mo
subscribers cancel
one flat price
no tiers or discounts yet

Costs. ≈ R$2.5 per subscriber each month (payment fees + servers + recipe content), plus ≈ R$ 4,000 / month fixed (hosting, tools, the founder's time). Fixed cost doesn't move the best price — it just decides whether Cafezinho is in the black.

The question. The founder thinks R$14.9 is too low and wants to jump to R$24.9. Is that right — and how would you ever prove it? That's what the rest of this page works out.

All figures illustrative; the pricing results below are computed from this setup.

Part 1
Find the price
1Model the demand curve
2Find the one best price (3 ways)
3Pressure-test with elasticity
4Layer a menu + segment discounts

We're setting the price of Cafezinho's paid tier — the Recipe Suggester — and optimising for one thing: monthly profit. Drag the price. Watch four business metrics move at once, and notice they don't agree on where the peak is.

Total profit (R$/mo)R$0R$2.3kR$4.6kR$6.9kR$0R$14R$28R$42Monthly priceR$22 optimum
Monthly price R$22
Conversion
5.9%
Revenue
R$ 11,623
Profit
R$ 6,302
Profit / sub
R$19.5
Price elasticity here
1.12
elastic
profit-max always sits in the elastic zone

Demand model is illustrative — linear demand, marginal cost R$2.5/sub.

Held constant here: churn. Every figure is per month. The real prize is lifetime value (LTV) — a higher price that nudges up cancellations can win this month yet lose over a customer's life as they get tired of paying. We've kept this to a single-month view on purpose. Including churn → LTV is the next layer of modelling and analysis and beyond the scope of this case study.

Four metrics. Four different "best" prices.

Each metric peaks somewhere else on the same curve. Picking which one to chase is a leadership call — not a math output.

R$0R$14R$28R$42Monthly price →ConversionR$0RevenueR$20.8ProfitR$22Profit / subR$42+
Maximise conversion and you let consumers keep their surplus. Maximise profit-per-sub and you price everyone out. The most interesting for our business case — revenue and total profit — live close together but not at the same place.

Elasticity: how hard does demand push back?

Below the unit-elastic line (E = 1), a price rise grows revenue. Above it, customers flee faster than price climbs. The profit peak always lands in the elastic region — to the right of E = 1.

Elasticity |E|01234+E = 1 · unit elastic · R$20.8InelasticElasticR$0R$14R$28R$42
The brown dot is elasticity at your price — it lands on the red E = 1 line only at the revenue-max price (R$20.8). Now: R$22 → E = 1.12 (elastic)

Step 2: don't pick one price. Build a menu.

Once you've set the single best price (the baseline), layer a tiered menu on top of it. Different drinkers will pay different amounts; the menu lets willingness-to-pay self-select. The thinking is — you design the ladder, customers sort themselves.

Free
Free
Tasters & students. Costs nothing, fills the funnel.
Brew (Paid tier)
✓ recommended
R$22
The everyday drinker — baseline paid tier, set to the profit-max price.
Roaster (Premium paid tier)
not built yet
R$30.6
Café-pros who barely flinch at price — a premium tier that doesn’t exist yet.
Should Cafezinho build the Roaster tier?

It looks like we have consumer surplus to capture with an even higher tier, but should the founder build this right now? The short answer is: on the economics alone, it’s a long shot. The café-pro niche is small, so the extra contribution a premium tier earns is capped at about R$ 212/month — less than even a lean build pays back. (This is a purely economic read; utilising iterative development methods to deliver a lean MVP, fuller go-to-market planning and marketing to increase the funnel for this tier aren’t in the lens of this analysis.)

Discount the elastic. Hold the line on the inelastic.

Not every drinker reacts the same way. The rule of thumb: cut price where demand is springy, keep it where demand is sticky — always measured at the baseline you just set.

Students
Tight budgets, lots of options
stickyspringy
E ≈ 2.8
Discount hard
Hobbyists
Care about coffee, price-aware
stickyspringy
E ≈ 1.13
Hold near optimum
Café-pros
A work tool, not a treat
stickyspringy
E ≈ 0.6
Keep price firm
How do you give just students a lower price? You fence it — verify who qualifies (a student email or ID check, e.g. via a service like SheerID) so the discount reaches students only. Without a fence, a "student price" quietly becomes everyone's price and the segmentation collapses.
Part 2
Estimate that curve

This demand curve was estimated. In real life you have to measure it — usually with an A/B test. Exploring this, I discovered a common trap in the design of these tests.

1Frame the hypothesis & metric
2Power it — enough traffic?
3Validate with an A/A test
4Don't peek — pre-register
5Check the split (SRM)

To A/B test this credibly, aim for ~7× the traffic

An honest test of the lift we care about needs more visitors than Cafezinho has today.

Why we check power first
Power is the chance a test actually spots a real effect when one exists. We treat it as the first gate, because there's no point trusting a result from a test that's blind to the very thing we're measuring: the price move implies a paywall conversion drop from ~8% to ~5% — three percentage points — and if the test can't reliably see a gap that size, every later step is wasted effort.
Traffic today / arm150
Traffic needed to run the test / arm — 80% power1,059
What it told us
At 150 visitors/arm this test reads at only ~18% power — it would miss that effect roughly four times in five. To reach the 80% power we want (a two-sided α of 5%, i.e. 95% confidence), we'd need ~1,059 visitors/arm — about 7× today's traffic. So today: not testable.

Once Cafezinho reaches ~1,059/arm, this exact test becomes trustworthy: 80% power, and a tight confidence interval instead of the noisy one you'd get today. How it grows to that traffic — marketing, growth, funnel work — is a separate plan, out of scope here.

The peeking trap

To expose the trap honestly we run an A/A test — two groups that get the exact same offer (same price, same everything). The true difference between them is zero by construction, so any "significant" result is a false alarm. This matters because peeking — stopping the moment a result looks good — is how you convince yourself a phantom win is real. We could easily ship a change that does nothing (economically), peek at the results too early, and end up burning engineering time and momentum chasing further improvements, only to find that the results from the full test contained different indications. Let's test it out — press run, watch the days tick by, and see how often peeking fools you.

We start with an A/A, not A/B: in an A/B test, group B is a real change. Here both groups are identical — so we know the honest answer is "no difference", and that the instrumentation and test mechanism are honest and will lead to a valuable A/B.

Why run an A/A test first?
Run it before any A/B and it's a dress rehearsal: with no real effect to chase, you find out whether the instrumentation is honest. It catches broken plumbing and sample-ratio problems (groups that should split 50/50 but don't), and it calibrates your real-world false-positive rate so you know what "significant" actually means in this test. An A/A should call "no difference" about 95% of the time. Pass it and a later A/B's "significant" result is a signal you can trust instead of noise you'll chase.
…then watch both stopping rules below judge the same data.
Arm A · same offer
n = 0
Arm B · same offer
n = 0
95% CI for the difference (B − A)
no difference (0)
① Peek & stop
stops the instant it looks significant
② Run to planned N
judged once, at the end
Now run it 200 times — on data with no real effect
Peek & stop on "significant"
false positives — should be 5%
Pre-register, check once at N
false positives — right on target
No real difference exists — every "win" here is a false positive.

And check the split before you trust anything

Before you read a result, check the split it's built on.

Why the split is the gate
Sample-ratio mismatch (SRM) is when a test you designed to split 50/50 arrives lopsided. We check it because it's the gate that protects every other result: if the wrong people landed in the wrong arm, any "effect" you measure could just be an artefact of broken randomisation or logging — not the price at all.
Healthy✓ 50 / 50
Broken⚠ 58 / 42
What it told us
A healthy ~50/50 split (left) means the plumbing held — read on. A skew like 58/42 on a 50/50 design (right) means something's broken: stop, fix the randomisation or logging, and re-run before you diagnose a single number.

What each testing step bought us

Five checks, in order — each one either earns the right to run the test or tells us to stop. Here's the ledger: what I assessed, and what it told me for Cafezinho.

1 Frame the hypothesis & metric
Checked
Named the one effect the test must see, and pinned it to a single primary metric before looking at any data — so "did it work?" has one honest answer, not four.
It told us
The move from R$14.9 towards R$24.9 implies paywall conversion falls ~8% → ~5%: that three-percentage-point drop is the bar everything else is measured against.
2 Power it — enough traffic?
Checked
Asked whether Cafezinho even has the traffic to detect that three-point drop at 80% power and 95% confidence — the check that decides whether a test is worth running at all.
It told us
Not close — at 150/arm the test reads at ~18% power and needs ~1,059/arm (~7× today's traffic). Today the test is effectively blind.
3 Validate with an A/A test
Checked
Ran a dress rehearsal on a true null — two identical arms, where "no difference" is the answer by construction — to prove the instrumentation before a real A/B rides on it.
It told us
The 5% false-alarm rate we allow is the flip side of correctly calling "no difference" ~95% of the time at planned N. A clean A/A means a later A/B's "significant" is signal we can trust, not noise.
4 Don't peek — pre-register
Checked
Compared stopping the moment a result looks significant against reading once at a pre-declared N — measured live across 200 identical-arm trials.
It told us
Peeking inflates the 5% false-alarm rate to ~22% (watch it happen in the sim above); read once at planned N and it stays ~5%. The fix is free — pre-register N and the metric, then read once.
5 Check the split (SRM)
Checked
Checked the 50/50 assignment actually arrived 50/50 — a test of the randomisation itself, not of the outcome.
It told us
A healthy ~50/50 means read on; a 58/42 skew means the randomisation or logging is broken and any "effect" is an artefact of who landed where. Clear this gate first, always.

So can Cafezinho just A/B its way to the price?

Not at today's size. Power says the test is blind until traffic grows ~7×, and peeking before then would manufacture false wins. The honest playbook: start from the modelled price (Part 1), roll it out to new sign-ups rather than secretly charging identical users two prices, pre-register what you'll measure, clear the SRM gate, and watch the guardrails — cancellations, refunds, lifetime value — over time. An experiment supports the decision; it rarely makes it for you.

Recommendations for Cafezinho
  1. 1 Raise the price now. Move the paid tier off R$14.9 and towards the profit-max R$22 — the “Brew” baseline. The current price is simply leaving money on the table.
  2. 2 Roll it out to new sign-ups carefully. Don’t charge existing users two different prices. Pre-register what you’ll watch, then track the guardrails — churn, refunds, lifetime value — for a few months before you call it.
  3. 3 Hold Brew as the core tier; shelve Roaster. The build can’t pay for itself at today’s café-pro numbers — see the go/no-go above. It’s a someday-tier, not the main event.
  4. 4 Don’t trust an A/B test yet. At today’s traffic it’s underpowered — it can’t see the effect. Decide from the model now, and revisit experimentation once growth gets you roughly 7× more traffic.
The Coffee Experiment · a Cafezinho case study · oisally.com All figures illustrative · purple & teal politely declined ☕