Three days ago I published a post arguing that my launch metrics were mostly machines. Forty-six views, zero sales, and — after I put UTM parameters on the only two links I had published — exactly two clicks that I could attribute to anything I wrote.
The post ended with advice I was pleased with: when two stories explain the same data equally well, stop writing the third version of the argument and go add a sensor.
The sensor is now installed. It has been running across thirteen links. And it turns out it was lying to me in a different direction, which I would not have noticed if I had trusted it the way I was planning to.
Where the numbers are now
| 24 Oct | 27 Oct | |
|---|---|---|
| Views, total | 46 | 88 |
| Attributable UTM clicks | 2 | 16 |
| Sales | 0 | 0 |
Fourteen new clicks in three days, after five days of two. By the standard I had just finished defending in public, that is the good number — the one that means real people, as opposed to the view count, which means nothing.
Seven of them came from the post itself. Four came from a longer technical article I published on another platform. Four more came from an earlier post here. One came from an older piece.
So: evidence of readers, finally. I had a tidy story ready about which channel works.
Then I checked the other end of the links.
Two clicks, zero views, two days
Here is the case that broke it, and it is as small as a case can get.
Two of those clicks point at one specific product page — a risk register template. One article links there, and nothing else does anywhere. The clicks are logged. The click count is two.
That product page has had zero views since launch. Including after both of them.
Two days after the first click was recorded, the destination of that click had still never been viewed by anyone.
That is not a subtle discrepancy. Those two counters are supposed to be describing the same event from two angles: somebody clicked a link, somebody arrived on a page. One of them is wrong, and it is not hard to work out which, because a page view is the thing that requires a browser to actually load the page.
I spent a while trying to make it lag. It doesn't work. Six hours of reporting delay I would have believed. Two days, on a dashboard that updates the view count on other products the same day, I don't. And the other direction — the page was viewed but the view wasn't counted — would have to be a bug that fires only on the one product nobody has visited.
Before going further I checked that the zero was a real zero and not a filter that wasn't applying, because those two look identical. I unticked every product and confirmed the total fell to zero. I ticked the one I cared about: still zero. Then I added a second product whose number I already knew, and it came back at exactly that number. The instrument works, and the zero is a zero.
What fits
The explanation that fits everything I can see: a good share of those clicks are link-preview crawlers, not readers.
Three signals, and it's the combination that matters rather than any one of them:
They arrive in the first couple of hours after publishing, and then stop dead. Two clicks in the first two hours, and zero in the nineteen hours after that. Real readership on a platform with a feed decays; it doesn't terminate. A publish-time spike that goes to exactly zero is the shape of something that fetched the page once when it was notified about it.
They don't produce a page view at the destination. Which is what you would expect from something that resolves a URL to generate a preview card and never renders the page.
And they arrive in a shape that readers don't make. This is the one that convinced me. In the last eighteen hours I gained four clicks, from two different articles, two each — and in both cases both links in the article went up by exactly one, at the same time. Read that as people and you need two readers per post, each picking a different one of the two available links, never the same one twice, independently, in both posts. Read it as one thing fetching every link on a page once and it needs no coincidence at all.
Meanwhile the view count keeps climbing without any of it: eighty-eight now, up seventeen in those same eighteen hours, and one country that didn't appear in my breakdown at all three days ago is now twenty-four of the eighty-eight.
I want to be careful here, because the whole point of the previous post was about not doing this. I can't prove any individual click was a crawler. What I can say is stronger than a preference and weaker than a proof: I have counters that disagree, and the disagreement is the finding. The click number alone was unfalsifiable in exactly the way the view number had been. It took a second counter, pointing at the same event from the other side, to make either of them mean anything.
And then I found a third counter I already had
I wrote all of the above believing I had two counters: clicks on my links, and views on my product pages. I was wrong about that too, and the way I found out is embarrassing enough to be worth writing down.
The platform I publish on has an analytics page. I had never opened it.
It says the previous post has been read fifty-one times. Not estimated, not inferred from a click — read, by its own count, with an average read time of four and a half minutes for the readers it can identify. Four and a half minutes is not a crawler. A crawler does not stay.
So the reach number I had spent a week triangulating from clicks was sitting one menu item away, and it changes the arithmetic completely.
| What I assumed | What the third counter says | |
|---|---|---|
| People who read the post | "a few hundred" | 51 |
| Clicks on its two links | 7 | 7 |
| Click-through | "reasonable" | 13.7% |
Thirteen per cent is not reasonable. Thirteen per cent is very good, and it moves the diagnosis somewhere I wasn't looking. Fifty-one people read a post about my own failure and one in seven of them clicked through. Whatever is broken in this funnel, it is not the top of it, and I have spent a week measuring the top of it.
It also gave me the one clean reading in the whole dataset. That post's click count stopped moving in the same window its reader count stopped moving — frozen at seven and fifty-one together. Two independent counters agreeing that nobody is reading it any more is the only thing here I'd call a measurement rather than an argument. Everything that's still moving is moving on one counter alone.
And notice what had just happened to my own case. I published a post saying one sensor is not instrumentation, while a third sensor sat unopened in a dashboard I owned. I was not short of data. I was short of the habit of going to look for the counter that could contradict me — which is, word for word, the thing I was lecturing everybody else about.
So the advice I gave three days ago was half right
The half that stands: a link published without parameters is a measurement that can no longer be taken, and ninety seconds of instrumentation buys you the entire question.
The half that was wrong, and this is the part I'd change: one sensor is not instrumentation. A single counter can only ever confirm itself. I replaced a number I couldn't trust with a number I couldn't check, and I was two days from making channel decisions on it.
The fix is cheap and I should have seen it, because it is the same thing I do for a living. I plan data migrations. Nobody signs off a migration because the load job reported success — you reconcile the target against the source, row count against row count, and the differences are where the truth is. A click counter and a destination page-view counter are a source and a target. The number that tells you something is the gap between them.
The thing I wanted to do and didn't
There is a confession in here that I think is the most useful part for anyone at the same stage.
Once I understood that the click counter might be counting crawlers, the obvious next move was to click my own links. Open the link, watch whether the click registers, watch whether the page view registers, and settle the mechanism in about forty seconds.
I didn't, and the reason is the whole problem with being this small: I have sixteen data points. One deliberate click from me is six per cent of my entire dataset, permanently, with no way to subtract it later. The test that would have told me how the instrument works would have damaged the only thing the instrument is measuring.
That trade doesn't exist at scale and it is brutal at n=16. If you are instrumenting something with almost no traffic, decide before you start which of your own actions you are allowed to take, because every one of them is a measurable fraction of your results.
The comments that reframed all of it
The previous post got six comments, which is six more than anything else I have published, and two of the people in that thread were worth more than the post.
One reader pointed out that I should look for per-view timestamps to separate crawlers from people. My store doesn't expose them — views are aggregated by day — so I couldn't do it directly. The concentrated burst from a single country did the same job by accident, so he was right about the method even where the data wasn't there.
The other one took the whole thing apart, correctly. Paraphrasing: this is not a conversion problem and it is not really a measurement problem either. It's a reach problem. Below roughly a hundred real humans on the page, changing the product teaches you nothing, because you don't have enough signal to detect any effect you might have.
That is the sentence that stopped me redesigning things. I had a list: adjust the price, rewrite the page, redo the screenshots. With sixteen clicks of which some unknown number are machines, every one of those experiments would have returned noise, and I would have interpreted the noise. A week of careful measurement produced one defensible instruction, and it was go get traffic, stop editing the shop.
Then I replied with my numbers, and he came back with the piece I didn't have: a test for a visit that I can actually apply. Count a click as a visit only if it does one of three things — scrolls, stays about fifteen seconds, or reaches a second page. And discard anything that arrives in the first couple of hours after you publish.
I had got to that second rule on my own, from the shape of my own data, about a day before he wrote it down. Two people looking at different evidence and landing on the same cutoff is as close to a replication as a project this size is going to get.
And his three-part test is, I notice, more or less what the read-time number in my own dashboard had been measuring for me the whole time.
Five rules, in the order they would have helped
Instrument before publishing. Still the highest-return ninety seconds available. Unchanged from last time.
Open the dashboards you already have before you build a new one. This is the rule I most needed and it would have cost me nothing. I spent a week inferring a reach number from clicks while the platform was counting readers for me, with read times attached. Before you instrument anything, go and find out what every tool you already use is already counting.
Then add the sensor that can contradict the first one. A click counter plus a destination view counter. A source count plus a target count. If you only have one number, you don't have a measurement, you have an opinion with a dashboard behind it — which is what I accused my view count of being, while making the same mistake one layer up.
Prove a zero before you build on it. A filter returning zero and a filter that isn't applying look exactly the same. Add something whose number you already know; if that comes back right, the zero is real.
Define a visit before you count one, and know your n before you design an experiment. A click is a floor: it counts if it scrolls, stays about fifteen seconds, or reaches a second page. And at sixteen data points you are not testing a price — you are not testing anything, including, awkwardly, with your own clicks.
Still zero sales, for the record. But I now have three numbers that disagree in ways I understand, and one pair that agrees, which is a considerably better position than one number that agreed with whatever I wanted.
I build these as a delivery manager on cloud and data migrations. The weekly status report template is free, pay what you want including zero: the status report template
The full kit, 12 templates and 50 prompts, is here if it's useful.
If you've instrumented something small: what was the first counter you found out was lying to you? I'm collecting them.
Top comments (17)
The disagreement between counters is the valuable result, because it turns a persuasive dashboard into a falsifiable system. I would keep a small reconciliation table keyed by link and time window, with expected browser-side events and known bot signatures, so future changes can distinguish instrument drift from a real audience change.
That's the right shape and it's also what I do for a living — reconciling a target against a source, row count against row count, with the differences as the actual output. Embarrassing that it took me a week to apply it to my own funnel.
The constraint I hit when I tried to build your table: my store aggregates views by day and exposes no per-view timestamp. So I can key a row by link, but the finest time window available on the destination side is a calendar day, while the click side is to the minute. Which means instrument drift and a real audience change are only separable for effects that last longer than a day — anything inside one is invisible on the target side.
What I've got instead is cruder and has been doing a surprising amount of work: the arrival shape. Both links on a page rising by exactly one at the same moment is a signature in practice, because readers don't coordinate. Three separate occurrences so far and all three had zero views at the destination.
If you've built one of these against a store that won't give you timestamps, I'd like to know what you keyed the rows on. That's the part I can't see a way around.
I have not built against that store, so this is a design suggestion rather than a tested integration: key the comparison by destination, link and store-local calendar day, and aggregate the click side to that same day. Keep the simultaneous increments as a separate suspicious-pattern flag, not a per-visit join. The daily row can show a discrepancy, but cannot tell which individual click failed to become a view.
Taking that, including the part I'd have resisted a week ago: dropping the per-visit join is dropping the thing I actually wanted and keeping the thing I can defend. Keeping simultaneous increments in their own flag column rather than inside the join is the bit that matters most — it stops a heuristic from quietly getting promoted to evidence when someone reads the table later.
The design earned its keep the day after you wrote it. I now have a day row where the click side went up by one and the destination's view count went up by one, on the same destination, on the same store-local day, with the suspicious-pattern flag empty. Every previous row with clicks in it had a zero on the target side.
Which leaves the question your flag column raises. An empty flag is absence of a known bot signature, not presence of a person. In reconciliation terms I'd normally treat an unexplained match as still unexplained. Do you let a clean flag column raise your confidence in the match, or does it only ever subtract?
I would treat a clean flag as subtractive evidence only: it can rule out known bad patterns, but it cannot promote an unexplained match to "human." Keep the match state as unknown until there is an independent positive signal, and let the confidence column say why rather than silently merging the two.
the paired clicks are the detail that moves this past a suspicion for me. two links rising by exactly one at the same time fits a preview fetch far better than two separate readers. that shape is worth checking before the zero-view count, since a zero on its own could still be a reporting delay.
You're right about the order, and I had it backwards in the post.
The zero depends on the dashboard reporting honestly. The paired shape doesn't — it's a coincidence argument, and it holds whatever the reporting delay is. So the pair should be the primary evidence and the zero the corroboration, not the other way round.
For what it's worth the zero did survive: that product page has now held an exact zero across five measurements in four days, each one with a control — untick everything, confirm the total falls to zero, then add a product whose number I already know. But that's four days of patience buying what your argument gets for free, which is your point.
One thing I can add since publishing. In the eighteen hours after this post went up, its own two links didn't move at all — still 2 and 1 from the first nineteen minutes — while the post picked up about twenty-five more readers. So on this one the pair never formed, and the clicks still weren't tracking people. Reads and clicks came apart completely.
Which leaves me with a shape I don't have a name for yet: readers with no clicks at all. Have you seen that one? I can't tell if it's the subject matter or just a small-n artefact.
i would lean small-n artefact for now, because a reader who finishes the post has no reason to click out unless the link promises something the post does not. the control check on the zero is the stronger part, it rules out a broken filter before you start naming reader shapes.
Small-n is the safe call and I'll hold it, but your mechanism is the more interesting half, because it's testable and mine wasn't.
If the reason is that the link promises nothing the post doesn't already deliver, then the link is the constant and the post is the variable — and I happen to have that pair. Same two links, same footer position, same author, on two posts three days apart. The earlier one converted at 13.7%. This one is at roughly zero past its publish spike, now with 43 readers on it.
The difference I can see: the earlier post ends on a problem a template addresses, and this one ends on an argument about method, where a template is a non-sequitur. Which is your explanation, just with the blame on the post rather than on the reader.
One update on the control you called the stronger part: someone else in this thread found the hole in it, and he was right. It proved my filter was applying, not that the page could register a view at all. That got settled this morning, when the page finally logged one.
So, with n=2 posts: would you call that a test of your mechanism or two anecdotes pointing the same way? I can't tell where the line sits at this size.
i would call it two anecdotes pointing the same way for now. the contrast between the 13.7% post and the roughly zero one is a useful hint, but a test would need a post where the link does a job the page does not already do, so a click is not just a reader finishing the argument. until that version exists, i would keep the mechanism as a guess worth checking rather than something settled.
The click-count vs destination-view gap read like a migration reconcile to me, that framing stuck. On the risk register page with two clicks and zero views, have you loaded that page yourself in a real browser to confirm the view counter fires there at all? That would rule out a broken counter before blaming crawlers.
You're right that I hadn't, and you're right that it mattered — my control was weaker than I presented it.
What I proved with the untick-everything-then-add-a-known-product check is that the filter applies. It says nothing about whether that particular product page emits a view event at all. Those are two different assumptions and I had them welded into one. A counter that never fires on one page looks exactly like a page nobody visits, and my control couldn't tell them apart.
I didn't load it myself for the reason in the post: at this sample size one deliberate action from me is a permanent, unsubtractable chunk of the dataset, and the number in question was the zero itself.
Then it answered itself this morning, which I did not deserve. That page now shows one view — its first ever — and the single link pointing at it went from two clicks to three in the same window. One link, one click, one view, no paired increment. So the counter fires, and the five previous zeros were real zeros rather than a dead sensor.
Second time you've gone at the instrument rather than the conclusion — the per-view timestamps on the last post were you too. Question back: when the measurement that would settle it is already on its way, how long do you wait before spending a test that contaminates the thing you're measuring? I got lucky on the timing here and I'd rather have a rule.
Good that it answered itself, and thanks for being straight about the control being weaker than it looked. My rule of thumb would be to wait if the data is already on its way and the wait is short, say a day or so. Only spend the contaminating test when waiting costs you a decision you have to make now. Do you have a deadline on this one, or is it just curiosity?
No deadline, and that's the weak part of my case: nothing downstream was waiting on that zero, so waiting cost me nothing and I don't get to call it discipline.
Trying to make your rule sharper for the case I couldn't handle. "Wait if the data is on its way and the wait is short" needs the wait to be knowable in advance. Mine turned out to be about fifteen hours, but I only know that in hindsight — at the time it could have been a week, and I'd have made the same call and described it the same way.
So the version I'd write down: wait when the arrival time is bounded by something outside me — a measurement already scheduled, a report that runs daily. Spend the contaminating test when the only thing bounding it is my own hope that something will turn up.
By that rule I got it right for the wrong reason. I did have a measurement scheduled for the next morning; I just wasn't thinking of it as the bound.
Does the bound do the work in yours too, or do you treat "a day or so" as a flat ceiling regardless?
three counters, three stories. which one do you trust when they disagree?
None of them on its own — the one that can be contradicted by another.
I got a clean example of this about eighteen hours after publishing. The click counter on this post's two links says 3, and it has said 3 since minute nineteen. The platform's reader count says 29 and is still climbing. A day ago I would have called those two versions of the same story and gone looking for which one was broken.
They're not the same story. They were never measuring the same thing. Clicks measure a request; reads measure attention. A post can gain twenty-five readers and zero clicks, which is apparently what mine just did, and neither counter is lying about that.
So the rule I came out with is narrower than "trust counter X": trust the one whose claim has a verifiable other end. A click with no page view at the destination is an assertion. A page view is an event that needed a browser to happen. When they disagree, the gap is the finding — and if nothing in my stack can contradict a number, that number isn't evidence yet, however precise it looks.
the gap being the finding is the part that sticks. two honest counters disagreeing tells you more than either one alone.