A founder showed me his Google Analytics dashboard last month.
He had every right to be proud. Eight months of SEO, blog posts, reels, sharing links in every community he could find. The traffic graph went up and to the right. Over 1,800 sessions a month.
Then he opened his payment dashboard.
Twelve customers.
His conclusion came instantly: "Traffic doesn't convert. I'm stopping all of it."
And that decision was about to hurt him more than the twelve customers ever did.
If you're the developer who gets told "just add Google Analytics" and then watches everyone stare at a number that explains nothing, this post is for you. By the end you'll know exactly what to instrument instead, with code.
The eagle-eye trap
Google Analytics answers one question brilliantly: how many people came, and from where.
It stays completely silent on the questions that actually decide revenue:
- How many of those visitors created an account?
- From which channel did those accounts come?
- How many accounts became paying customers?
- At which exact stage did everyone else give up?
Watching GA alone is like watching your shop from a helicopter. You can count every person who walks through the door. You can't see their face when they look at your shelves. You can't see which aisle they abandon.
That's not a marketing problem. It's a visibility problem. And it quietly breaks founders and side projects alike.
The frustration loop
I've watched this exact loop play out with multiple product owners:
- They work hard on traffic. It grows.
- Sales stay flat.
- They can only see two numbers: big traffic, small revenue.
- They conclude "traffic is useless" and stop all growth effort.
- Now traffic falls too, and the product dies quietly.
Here's the cruel part. The traffic effort was often working. Something downstream was leaking. But from 10,000 feet, a leak in the middle of the funnel looks exactly like a marketing failure.
You can't fix what you can't see. So let's talk about what you need to see.
One funnel. Two ratios. Full diagnosis.
Every product runs on the same three-stage funnel:
Traffic → Account creation → Payment
Which gives you exactly two ratios:
- Visit-to-signup rate: what % of visitors create an account
- Signup-to-paid rate: what % of accounts turn into revenue
These two numbers do something almost magical. They tell you which disease your product has. And the treatments are completely different.
Disease 1: Traffic is high, signups are low
If 100,000 people visit and only 400 sign up, your traffic is fine. Your first impression is not.
Something between the promise and the page is broken:
- The link promised one thing, the landing page talks about another
- The audience is wrong (viral is not the same as relevant)
- The value isn't clear within the first five seconds
- The page asks for too much, too early
This is a content satisfaction problem. The visitor arrived with an expectation and your content didn't fulfill it.
The fix lives in your messaging, your landing pages, your first screen. Not in more traffic. Pouring more visitors on this stage just means more people leaving disappointed, faster.
Rough benchmark for SaaS: 2 to 5% of visitors signing up is healthy. Below 1%, stop buying traffic and start fixing the page.
Disease 2: Signups are fine, payments are not
Now the opposite case. People sign up, then silence. Nobody upgrades. Nobody comes back.
These users believed your promise. They tried the product. They didn't find enough value to stay or pay.
That's not marketing. That's:
- Onboarding that never gets users to their first "oh, this is useful" moment
- A gap between what users expected and what the product delivers
- A retention problem: they use it once and forget you exist
This is the most expensive stage to ignore, for one brutal reason: unsatisfied users don't refer. Every rupee you spend on new traffic lands in a leaking bucket, and word of mouth, the cheapest channel you will ever have, never starts.
When this ratio is low, the honest move is to pause traffic spend and put that energy into the product until existing users are satisfied. Growth built on unsatisfied users is rented, not owned.
The stack: GA for the top, Mixpanel for the truth
You don't need a data team. You need two tools and one habit.
Keep Google Analytics for the top of the funnel. It's still the best free answer to "how much traffic, and from where."
Add a product analytics tool like Mixpanel (Amplitude and PostHog work too) for everything GA can't show you: user-level journeys, funnels, retention, and revenue per channel.
The core idea is identity stitching. Every anonymous visitor gets an ID on first visit. When they sign up, one function call links all their past anonymous visits to that account. When they pay, a server-side event attaches revenue to the same identity.
There's a bonus. Because visits are stitched to real users, you finally see touchpoints. The same person who discovered you on Instagram, came back through your blog, and finally signed up from a WhatsApp link shows up as one journey. Without identity stitching, that's three strangers in your data.
Ship it in one afternoon
Here's the entire implementation. Three snippets.
1. Capture the channel on landing (first touch and last touch, then clean the URL):
mixpanel.init('YOUR_TOKEN', { track_pageview: true });
const params = new URLSearchParams(window.location.search);
const ref = params.get('ref') || params.get('utm_source');
if (ref) {
mixpanel.register_once({ first_ref: ref }); // first touch, never overwritten
mixpanel.register({ last_ref: ref }); // last touch, updates every visit
history.replaceState({}, '', window.location.pathname); // keep the URL clean
}
register_once and register attach these as super properties, so every future event this visitor fires already carries their channel.
2. Stitch identity at signup:
// right after account creation succeeds
mixpanel.identify(user.id);
mixpanel.people.set({ $email: user.email });
mixpanel.track('Account Created');
That single identify() call merges every anonymous visit this device ever made into the new account. The WhatsApp visit from last Tuesday now belongs to a real user.
3. Track revenue server-side (never trust the browser with money). From your Stripe webhook:
// webhook handler for checkout.session.completed
const mixpanel = require('mixpanel').init(process.env.MIXPANEL_TOKEN);
mixpanel.track('Purchase', {
distinct_id: session.client_reference_id, // set this to user.id when creating the Checkout Session
amount: session.amount_total / 100,
currency: session.currency,
});
Done. Now build one funnel report in Mixpanel: Page View → Account Created → Purchase, broken down by first_ref.
That single report shows both ratios, per channel, forever. Retention reports show whether users return on day 1, 7, and 30. Revenue reports show which channel makes money, not just noise.
The link-sharing habit that saves your attribution
One practice. Non-negotiable. Never share a naked link.
Channels like WhatsApp, Telegram, SMS, and most email apps strip the referrer. To your analytics, those visitors arrive as "Direct". A black hole you can never analyze.
So every link you share gets a meaningful tag. At CtrlOps we made this a team rule, and our links look like this:
-
https://ctrlops.io/?ref=whatsapp_devops_groupswhen we share in WhatsApp communities -
https://ctrlops.io/?ref=ig_biositting in our Instagram bio -
https://ctrlops.io/?utm_source=newsletter&utm_campaign=launchfor email campaigns -
[https://ctrlops.io/?ref=devto_funnel_blog](https://ctrlops.io/?ref=dax_dev_7c9c178685c7)for the link inside this very article
That last one is a live demo. If you click it, our funnel records a visitor from this exact post. If you sign up, the account gets attributed to this post too.
And here's the practice, practiced: the Medium version of this article carries ref=medium_funnel_blog instead. Same article, two tags. In a month, our funnel will tell us which platform sends readers who actually convert, not just readers who visit.
Meaningful means readable by a human three months later. ref=c4 tells you nothing. ref=devto_funnel_blog tells you everything.
Do this for every campaign, every channel, every experiment. Now each strategy is measurable end to end: this tag brought this many visits, this many accounts, this much revenue. No guessing.
Back to the founder
Once his funnel was instrumented, the diagnosis took one afternoon.
His visit-to-signup rate was 0.4%. Blog readers were arriving for specific tutorials and landing on a generic homepage. Promise and page didn't match.
He rewrote his landing pages to match what each post promised. Signups quadrupled. Same traffic. Not one new visitor.
The next bottleneck showed itself just as clearly: users dropping at step 2 of onboarding. That's next month's problem. And now he can see it.
He never needed more traffic. He needed to see.
Your cheat sheet
Keep this table somewhere visible:
- Big traffic, low signups → fix content and first impression. Match the page to the promise.
- Good signups, low payments → fix onboarding and product value. Pause traffic spend until this improves.
- Users don't return → retention issue. Talk to the users who left before building anything new.
- Both ratios healthy, traffic low → now scale traffic aggressively. Every visitor you add will convert.
Traffic is fuel. Your funnel is the engine.
More fuel has never fixed a broken engine. Open it, find the leak, fix it, and then buy fuel with total confidence.
So, honest question for the comments: which of your two ratios are you blind to right now? And if you've instrumented a funnel before, what surprised you most when you first saw the real numbers?
If this was useful, follow me here on DEV. I write about building and growing products with data instead of guesses.
Ex mine: As a founder and indie hacker, I am sharing this journey to help fellow founders think through product and AI decisions.






Top comments (0)