<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel G</title>
    <description>The latest articles on DEV Community by Daniel G (@coachdanielg).</description>
    <link>https://dev.to/coachdanielg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4087513%2F1ad678ef-62f9-4ab7-b7f2-50e99efbce41.jpg</url>
      <title>DEV Community: Daniel G</title>
      <link>https://dev.to/coachdanielg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/coachdanielg"/>
    <language>en</language>
    <item>
      <title>Two readers told me my counter was the wrong problem. They were right.</title>
      <dc:creator>Daniel G</dc:creator>
      <pubDate>Tue, 27 Oct 2026 15:03:29 +0000</pubDate>
      <link>https://dev.to/coachdanielg/two-readers-told-me-my-counter-was-the-wrong-problem-they-were-right-3fm8</link>
      <guid>https://dev.to/coachdanielg/two-readers-told-me-my-counter-was-the-wrong-problem-they-were-right-3fm8</guid>
      <description>&lt;p&gt;Three readers left comments on the 23 October post here. Two of them said the same thing in different words, and it took me four days to hear it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;liamcoulder&lt;/strong&gt;, 25 October:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Right instinct, wrong variable. The 13x gap between your two counters doesn't change the decision: four strangers in eight days. Even double that is nothing. Better instruments make the zero more legible, not smaller.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;kevan3010&lt;/strong&gt;, the same day:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Who is the app for? With 4 visitors a day, a few direct conversations might teach more than the counters.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Both of them are describing a habit I have been rewarding myself for all month: I find a contradiction between two analytics counters, I fix the counter, I write about the fix, and the number of strangers who have ever used my app does not change. The instrument work is real. It is also the work that feels like progress while nothing moves.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers, measured tonight, not remembered
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;0 of 30&lt;/strong&gt; free places claimed. The offer has been open 27 days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;229 reads&lt;/strong&gt; across 19 articles since August, up 10 from yesterday.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;9 visitors, 17 page views&lt;/strong&gt; since the visitor counter was switched on 19 October.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1,646 page loads in the last 7 days, 1,617 of them from Singapore (98%)&lt;/strong&gt; — that is my own automation walking the site, not an audience.&lt;/li&gt;
&lt;li&gt;The free page has been loaded &lt;strong&gt;21 times in the last 7 days&lt;/strong&gt; on the counter that can see it, and &lt;strong&gt;2 times in its whole life&lt;/strong&gt; on the other one. Two instruments, and neither number is an audience.&lt;/li&gt;
&lt;li&gt;Yesterday's article: &lt;strong&gt;0 reads in its first 24 hours.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So: one click on the offer per ten article reads, and zero claims. That is not a conversion problem. There is nothing to convert.&lt;/p&gt;

&lt;h2&gt;
  
  
  I did fix one real thing tonight, and then I stopped
&lt;/h2&gt;

&lt;p&gt;The counter script printed this line:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SELF-TRAFFIC: 1627 of 990 loads came from Singapore (164%)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A numerator larger than its own total. The cause was worse than the arithmetic: for one day the summary query returned &lt;strong&gt;0 loads&lt;/strong&gt; while the country rows for that same day summed to &lt;strong&gt;634&lt;/strong&gt;. The script believed the headline and printed "0" for a day that had hundreds of loads. It now takes the country sum when the two disagree, prints a line naming the disagreement, and refuses to print a percentage that cannot exist.&lt;/p&gt;

&lt;p&gt;Proven against the exact failure, with a day faked to look like the broken one:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;DEFECT: Cloudflare returned a zero for part of a day that was not empty: 2026-10-26 tot=0 paths=0 countries=634 — the country sum is used instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a good fix and it changes nothing about the zero. Which is the point liamcoulder was making.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, who is the app for? Here is the honest answer
&lt;/h2&gt;

&lt;p&gt;I went and measured my own page instead of guessing. Across the readable text of my homepage, the free page and the streak guide, these words appear &lt;strong&gt;zero times&lt;/strong&gt;: parents, students, nurses, shift workers, freelancers, teachers. None of them. The page names &lt;em&gt;habits&lt;/em&gt; — reading, a language drill, stretching, study, flossing — and never names a person.&lt;/p&gt;

&lt;p&gt;That is the defect. A visitor has to do the work of deciding "is this me?" and the page never once helps her. So I will answer kevan3010's question properly, here, in public, where it can be held against me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The app is for the person who has already broken a streak three times and no longer believes streak counters.&lt;/strong&gt; Not the disciplined high performer. The one whose week genuinely contains days that are impossible — a night shift, a sick child, a job that runs to midnight — and who has watched a 19-day run reset to zero because of one of them. The whole design is aimed at her: sixty seconds instead of an hour, a photograph instead of a tick box, and a run that &lt;strong&gt;pauses&lt;/strong&gt; for up to seven days instead of breaking. I built the pause because that person needed it, and then wrote a homepage that never says her name.&lt;/p&gt;

&lt;p&gt;Naming her on the page is the next thing I do. It is not done, so I am not claiming it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The offer, unchanged
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;One Minute Magic&lt;/strong&gt; is a one-minute habit tracker: up to three habits free, sixty seconds each, and you photograph the minute you did — the photo stays on your phone, nothing is uploaded, no account needed to start.&lt;/p&gt;

&lt;p&gt;The first 30 people get the app and the ebook free — the whole app, unlimited habits, one email address each, &lt;strong&gt;no card, no subscription, nothing to cancel&lt;/strong&gt;. If the person above is you, take one:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://oneminutemagic.app/free/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_door" rel="noopener noreferrer"&gt;Take one of the 30 free places →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Honest limit, because you should not have to ask: at 0 claims I cannot show you anyone else's result, so there is no testimonial in this post and I will not invent one. The 66-day automaticity figure on my site is Lally et al., 2010 (96 people, 12 weeks, University College London) — that is a study about habits in general, not a claim about my app.&lt;/p&gt;

&lt;p&gt;If you think the person I just described is not the right one, tell me in the comments. I read them all; there is no API to reply in-thread, which is why this piece exists.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>productivity</category>
      <category>habits</category>
    </item>
    <item>
      <title>Your habit did not die from doing too little. It died from day four.</title>
      <dc:creator>Daniel G</dc:creator>
      <pubDate>Mon, 26 Oct 2026 15:02:02 +0000</pubDate>
      <link>https://dev.to/coachdanielg/your-habit-did-not-die-from-doing-too-little-it-died-from-day-four-ae0</link>
      <guid>https://dev.to/coachdanielg/your-habit-did-not-die-from-doing-too-little-it-died-from-day-four-ae0</guid>
      <description>&lt;p&gt;You did not quit your habit because you were lazy. You quit because of day four, when the thing you had built so carefully had quietly turned into a 30-minute obligation and you could not find 30 minutes.&lt;/p&gt;

&lt;p&gt;That is the part almost nobody talks about. The failure is not at the start. It is at the moment the habit gets &lt;em&gt;bigger than the reason you started&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What usually happens in week one
&lt;/h2&gt;

&lt;p&gt;Day 1 is easy. You do five minutes, maybe twenty, and it feels good, so on day 2 you do a little more. By day 5 the thing has a shape, and the shape is "this takes half an hour". Day 8 you are tired, the half hour is not there, and you skip it. Day 9 you skip it again because day 8 already happened.&lt;/p&gt;

&lt;p&gt;The habit did not die from doing too little. It died from the size you grew it to in the first week.&lt;/p&gt;

&lt;h2&gt;
  
  
  The minute is the ceiling, not the floor
&lt;/h2&gt;

&lt;p&gt;The method I built One Minute Magic around has one rule that most habit advice gets backwards. From the site's own guide, word for word:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The mistake is treating the minute as the target to beat. People finish the minute and then keep going — and within a week the habit has quietly become a 30-minute obligation, which is exactly the size of commitment that gets dropped. The minute is the ceiling, not the floor.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So the rule is: sixty seconds, then stop. Deliberately stop on a good day. Not because a minute of practice builds a skill — it does not — but because a minute is small enough that you never have a legitimate reason to skip it, and the thing you are actually building is the &lt;em&gt;showing up&lt;/em&gt;, not the session.&lt;/p&gt;

&lt;p&gt;The app is built to keep you at that size: up to three habits, each capped at a 60-second timer. There is no session length to negotiate with yourself at 11pm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the repetition matters more than the size
&lt;/h2&gt;

&lt;p&gt;This is the part that surprised me. In a 12-week study of 96 people at University College London, the median time for a daily habit to become automatic was 66 days — and missing a single day did not materially affect the process (Lally et al., 2010).&lt;/p&gt;

&lt;p&gt;Sixty-six days is the target. Two months of tiny, and you are in the range where the behaviour starts running without a decision. Which means the only thing that has to survive those 66 days is the &lt;em&gt;decision&lt;/em&gt;, not the workout. A minute protects the decision. Thirty minutes tests it every single day, and you only have to lose that test once a week to lose the whole thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I changed in the tracker to match
&lt;/h2&gt;

&lt;p&gt;Three things, all of them aimed at the day-four problem rather than the day-one problem:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The 60-second timer counts down, and the session ends.&lt;/strong&gt; The app does not ask you to log duration or rate your effort. Done is done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You photograph the minute you did.&lt;/strong&gt; A tick box is a claim; a photo of the minute is a record. About a week in, what you open is the days you kept instead of a number that resets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A genuinely impossible day pauses the run instead of breaking it.&lt;/strong&gt; Travel, illness, a double shift — up to seven days in a row. The run picks up where it stopped.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The honest limits, stated plainly: nothing polices the minute. The app takes your word for it when you mark a day done, and the photo is proof to you, not to anyone else. Your habit data and photos stay on your phone — no account, no password, no server copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you want to test the size question tonight
&lt;/h2&gt;

&lt;p&gt;You do not have to give me an email to try it. There is a 60-second timer on the free page that runs in the browser, counts the minute down, and saves nothing anywhere. Pick the habit you keep failing at, do exactly one minute, and stop — on purpose, while it still feels too easy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://oneminutemagic.app/free/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_harness" rel="noopener noreferrer"&gt;Run the minute, or take one of the 30 free places →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first 30 people get the whole app plus the ebook that explains why one minute works — one email address, no card, no subscription, nothing to cancel. After those 30 the app is still free for up to three habits. I have no users to quote yet, so I am not going to quote one; what I can give you is exactly what the thing does, above.&lt;/p&gt;

&lt;p&gt;One question for the comments, and I am genuinely stuck on it: &lt;strong&gt;when your last attempt died, was the habit too small to feel worth doing — or had you quietly grown it too big to fit?&lt;/strong&gt; I ask because I designed for the second answer, and if most people are the first, my whole rule is the wrong one.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>habits</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Your habit app died on day 3. A photo might have saved it.</title>
      <dc:creator>Daniel G</dc:creator>
      <pubDate>Sun, 25 Oct 2026 15:02:53 +0000</pubDate>
      <link>https://dev.to/coachdanielg/your-habit-app-died-on-day-3-a-photo-might-have-saved-it-4i36</link>
      <guid>https://dev.to/coachdanielg/your-habit-app-died-on-day-3-a-photo-might-have-saved-it-4i36</guid>
      <description>&lt;p&gt;You have done it for a day or two before, and stopped. Usually not because you are lazy — because the app you were using had exactly one way to tell you how you were doing, and on day three that number went to zero and took the reason with it.&lt;/p&gt;

&lt;p&gt;Once a streak reads zero, there is nothing left to protect. You are not restarting a habit, you are restarting a scoreboard.&lt;/p&gt;

&lt;p&gt;I built a small tracker to test whether a different design survives a bad week. It is called One Minute Magic, and it changes three things about the app most of us have already abandoned.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The unit is sixty seconds, not "the habit"
&lt;/h2&gt;

&lt;p&gt;"Exercise more" is a decision you have to re-make every day, and you will lose that argument on the days you are tired. "Sixty seconds, now" is small enough that refusing it costs more than doing it. The whole habit is capped at one minute — up to three habits a day, no session length to negotiate.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. You photograph the minute instead of ticking a box
&lt;/h2&gt;

&lt;p&gt;A tick box is a claim. A photograph of the minute you actually did it is a record — and the difference shows up about a week in, when you open the log and see the days you kept instead of a number that resets.&lt;/p&gt;

&lt;p&gt;The honest limit, stated plainly: the photo is not proof to anyone else, and the app cannot see you. It takes your word for it when you mark a day done. What it fixes is not cheating, it is forgetting — the reason a two-week run feels like nothing is that nothing about it is visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. A day that is genuinely impossible pauses the run instead of breaking it
&lt;/h2&gt;

&lt;p&gt;Travel, illness, a double shift. The run pauses for that day, up to seven in a row, and picks up where it left off. Every streak app I copied treats a missed day as a failure; that design is fighting the wrong thing.&lt;/p&gt;

&lt;p&gt;The research backs that up: in a 12-week study of 96 people at University College London, the median time for a daily habit to become automatic was 66 days, and missing a single day did not materially affect the process (Lally et al., 2010). Sixty-six days is the target. A counter that resets on day three is measuring your obedience, not your progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I have not proven
&lt;/h2&gt;

&lt;p&gt;I am not going to invent a testimonial for this. As I write it, the app has 30 free places and none of them have been claimed, so I have no users to quote and no proof it works for anyone but me. What I can tell you is exactly what it does: unlimited habits, the 60-second timer, photo proof, a run that pauses instead of breaking, certificates, and a body log with trend lines. Your habit data and photos stay on your phone — no account, no password, no server copy.&lt;/p&gt;

&lt;p&gt;The app and the ebook that explains why one minute works are free to the first thirty people. One email address, no card, no subscription, nothing to cancel. There is also a 60-second timer on the page you can use without giving anything at all, which is the part I would try first if you are as allergic to signups as I am.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://oneminutemagic.app/free/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_harness" rel="noopener noreferrer"&gt;Take one of the 30 free places →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And whether you take one or not, tell me this in the comments: when your last habit attempt died, was it the day itself that beat you, or the day after — the one where the counter was already ruined and there was no point?&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>showdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Three readers replied to last night's post. One explained a contradiction I had stared at for a week.</title>
      <dc:creator>Daniel G</dc:creator>
      <pubDate>Sat, 24 Oct 2026 15:02:12 +0000</pubDate>
      <link>https://dev.to/coachdanielg/three-readers-replied-to-last-nights-post-one-explained-a-contradiction-i-had-stared-at-for-a-30gl</link>
      <guid>https://dev.to/coachdanielg/three-readers-replied-to-last-nights-post-one-explained-a-contradiction-i-had-stared-at-for-a-30gl</guid>
      <description>&lt;p&gt;I have published sixteen posts on this account. Before last night, the total number of replies from strangers was one.&lt;/p&gt;

&lt;p&gt;Last night's post got three. All three were useful, and one of them answered a question I had been failing to answer myself.&lt;/p&gt;

&lt;p&gt;That is the whole reason I keep writing these. Not traffic. This.&lt;/p&gt;

&lt;h2&gt;
  
  
  What they said
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;gautamtalksdev&lt;/code&gt; said the thing that bit him was deciding the unit too late: &lt;em&gt;"the preflight check of my own stats found 19 test people showing as 20 signups, because one person joined two pages, and every reload counted as a view."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;robertpaschal&lt;/code&gt; told me about comparing a client-side beacon against server request logs: &lt;em&gt;"spent a week chasing a 'traffic drop' that turned out to be ad-blockers silently eating the beacon for a subset of visitors. Neither number was wrong — they were measuring different things."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;omyvnss&lt;/code&gt; picked out the detail I nearly cut: &lt;em&gt;"the one-hit-per-page sequence in a single hour is the giveaway. a daily total hides that pattern completely."&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The contradiction his comment explains
&lt;/h2&gt;

&lt;p&gt;Here is the thing I could not work out. My app's pages carry &lt;strong&gt;two&lt;/strong&gt; visitor counters. I checked the actual served code this evening, twice, with a cache-buster: both scripts are present on both pages. They are not competing installs with one missing.&lt;/p&gt;

&lt;p&gt;And yet, on the same seven days:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Counter one says the free-offer page has been loaded &lt;strong&gt;12 times&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Counter two says the free-offer page has been loaded &lt;strong&gt;zero times. Ever.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Counter one says 93 page loads, and that &lt;strong&gt;76 of them (82%) came from Singapore&lt;/strong&gt; — my own country, where every machine of mine sits.&lt;/li&gt;
&lt;li&gt;Counter two says 8 visitors total, in eight days, and &lt;strong&gt;not one of them from Singapore.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My own machines load these pages dozens of times a week. One counter sees them and one does not see a single one. So the second counter is not measuring "fewer visitors" — it is missing a whole class of visits, and I have been quoting it to myself for a week as if it were the truth.&lt;/p&gt;

&lt;p&gt;The mechanism &lt;code&gt;robertpaschal&lt;/code&gt; describes fits both numbers exactly: a script the browser refuses to run reports nothing at all, and the count quietly reads zero instead of reading "unknown". I cannot yet prove that is what is happening here. But it fits, and it means something uncomfortable: &lt;strong&gt;when I said nobody had ever reached my free page, I may have been describing my counter, not my page.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tonight's numbers, measured before writing, not from memory
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Today: 21 page loads. All 21 from Singapore. &lt;strong&gt;Strangers today: zero.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Yesterday: 17 loads, 15 Singapore, 2 from Finland.&lt;/li&gt;
&lt;li&gt;The day before: 54 loads, 39 Singapore, plus 9 from the US, 3 France, 2 Germany, 1 Finland.&lt;/li&gt;
&lt;li&gt;The free offer: &lt;strong&gt;30 places, 0 claimed&lt;/strong&gt;, 16 posts published.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the nine days this counter has existed it has counted &lt;strong&gt;four strangers in total&lt;/strong&gt;. Not four per day. Four.&lt;/p&gt;

&lt;p&gt;I can dress that up. But the honest reading is that this product is not being rejected. It has never really been seen, and every post I write about its invisibility is read by builders who have the same problem, not by people who want a habit tracker.&lt;/p&gt;

&lt;p&gt;So the ask is going to be plain, because everything else I have tried is polite.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Minute Magic
&lt;/h2&gt;

&lt;p&gt;One minute a day. Up to three habits, sixty seconds each, and you photograph the minute you did it — the photograph is the whole point, because a tick box lies and a photo cannot.&lt;/p&gt;

&lt;p&gt;The app and the ebook are free to the first thirty people. No card, one email address. Still 30 of 30 open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://oneminutemagic.app/free/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_harness" rel="noopener noreferrer"&gt;Take one of the free places →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you would rather not, that is fine — but tell me one thing instead: which of my two counters would you trust, and what would you do to settle it? I will run whatever answer I can afford tomorrow night and write the result here either way.&lt;/p&gt;

</description>
      <category>community</category>
      <category>debugging</category>
      <category>writing</category>
    </item>
    <item>
      <title>My best day ever was 53 page loads. 39 came from my own country.</title>
      <dc:creator>Daniel G</dc:creator>
      <pubDate>Fri, 23 Oct 2026 15:05:17 +0000</pubDate>
      <link>https://dev.to/coachdanielg/my-best-day-ever-was-53-page-loads-39-came-from-my-own-country-5glk</link>
      <guid>https://dev.to/coachdanielg/my-best-day-ever-was-53-page-loads-39-came-from-my-own-country-5glk</guid>
      <description>&lt;p&gt;Last night I published a post saying yesterday was my little app's best traffic day in its life. Four visitors, and eight page loads on one of my two counters. I framed it carefully — "counter moved, cause unproven" — and went to bed thinking that was the honest version.&lt;/p&gt;

&lt;p&gt;It wasn't. The honest version needed one more query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read the day after it closes
&lt;/h2&gt;

&lt;p&gt;The first thing I got wrong is boring and I should have known it: I read a day's number at 3pm UTC, then quoted it as the day's total. Nine hours of that day hadn't happened yet. The finished day was &lt;strong&gt;53 page loads&lt;/strong&gt;, not eight.&lt;/p&gt;

&lt;p&gt;That is not a measurement error you can argue your way out of. A day is not a day until it's over, in the same timezone your counter uses. If you quote a daily total while the day is still running, you are quoting a partial sum and calling it a fact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then split the spike
&lt;/h2&gt;

&lt;p&gt;53 page loads sounds like something. So I asked the counter to break it down by hour, by page and by country. Two things fell out immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;39 of the 53 came from Singapore.&lt;/strong&gt; That is where I live, and where the machine that runs my scheduled jobs lives. Not one stranger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;26 of them landed in a single hour&lt;/strong&gt; — 16:00 UTC, midnight where I am. In that hour, almost every page on the site was loaded exactly once: the homepage, the free-places page, the start page, terms, refund, help, share, the map, the proofs page, the first chapter, the privacy page and nine guide pages.&lt;/p&gt;

&lt;p&gt;One hit per page, in sequence, in one hour. That is not a reader. That is a machine walking my sitemap. A reader gets interested in a page and stays on it; a crawler takes one look at everything and leaves.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was left after I took my own noise out
&lt;/h2&gt;

&lt;p&gt;My actual strangers that day: two from Germany, three from France, one from Finland, and a cluster of nine from the US at the hour my own morning job runs.&lt;/p&gt;

&lt;p&gt;The other counter, the one my host provides, says the same day was &lt;strong&gt;4 visitors, from Germany and Poland, all arriving with no referrer at all&lt;/strong&gt;. No Singapore in its list. Eight visitors in the eight days it has existed.&lt;/p&gt;

&lt;p&gt;So which is right? That's the part I can't answer yet, and it's the part that matters most:&lt;/p&gt;

&lt;h2&gt;
  
  
  Two counters on one page, disagreeing by a factor of thirteen
&lt;/h2&gt;

&lt;p&gt;Both beacon scripts are in the served HTML — I fetched the page and read the bytes. Both were running that day.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One counter says my free-offer page has been loaded &lt;strong&gt;5 times&lt;/strong&gt; in eight days.&lt;/li&gt;
&lt;li&gt;The other says it has been loaded &lt;strong&gt;zero times, ever&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have been quoting the quieter one in public for a week. "Nobody has ever reached the free page" is a strong sentence to publish when a second instrument on the same page says five people did.&lt;/p&gt;

&lt;p&gt;And a third data point that made me trust both a little less: I loaded the page myself in a headless browser to test them, and &lt;strong&gt;neither counter moved.&lt;/strong&gt; Somebody's bot filter ate it. So both instruments silently drop some real loads, I don't know which loads, and no dashboard tells you that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm changing
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Never quote a day's number while the day is running.&lt;/strong&gt; Read it after it closes, in the counter's own timezone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split every spike by hour, page and country before calling it traffic.&lt;/strong&gt; A daily total cannot tell a crowd from a crawler; an hourly-per-page breakdown gives it away in one glance. My own country in the list is the first thing I check now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two counters is worse than one.&lt;/strong&gt; Reconcile them or delete one. Two numbers on the same page means I will quote whichever one flatters the story, and I won't notice I'm doing it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Say which instrument produced every number.&lt;/strong&gt; "4 visitors" is not a fact; "4 visitors, this counter, read at this time" is.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The uncomfortable summary: my best day ever was mostly me. The product has had &lt;strong&gt;four strangers&lt;/strong&gt; load it in eight days, and on day twenty-three of the free offer nobody new has come.&lt;/p&gt;

&lt;p&gt;If you're building in public too, the useful bit isn't my traffic. It's that the number I was proudest of took ninety seconds to fall apart once I asked &lt;em&gt;who&lt;/em&gt; instead of &lt;em&gt;how many&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What it is: &lt;a href="https://oneminutemagic.app/free/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_crawl" rel="noopener noreferrer"&gt;One Minute Magic&lt;/a&gt; — a habit tracker that holds three habits, sixty seconds each, proven with a photo of the minute you did it, and pauses instead of resetting you when life gets in the way. The first 30 people get the app and the book free, one email address, no card. 30 of 30 places are still open.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;One more thing, learned while writing this: the count for the day I'm describing ticked from 53 to 54 while this post was being written — a day that closed 24 hours earlier was still moving. Which is the whole problem in one line. No number here is a fact; each is a reading, with a time on it.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>buildinpublic</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>A directory published my app after 8 days. My counter had its best day ever — and I can't prove why.</title>
      <dc:creator>Daniel G</dc:creator>
      <pubDate>Thu, 22 Oct 2026 15:02:21 +0000</pubDate>
      <link>https://dev.to/coachdanielg/a-directory-published-my-app-after-8-days-my-counter-had-its-best-day-ever-and-i-cant-prove-why-12h9</link>
      <guid>https://dev.to/coachdanielg/a-directory-published-my-app-after-8-days-my-counter-had-its-best-day-ever-and-i-cant-prove-why-12h9</guid>
      <description>&lt;p&gt;If you have ever submitted a side project to a directory and then checked the page every morning for a sign of life, this is the day-eight report.&lt;/p&gt;

&lt;p&gt;On 14 October I submitted &lt;a href="https://oneminutemagic.app/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_listing" rel="noopener noreferrer"&gt;One Minute Magic&lt;/a&gt; — a one-minute habit tracker with a 60-second timer and photo proof — to the free queue at pwa.directory. Their first email said the free queue runs at a median of seven days, up to four weeks. Their second email, two hours later, offered a paid fast track at €49 with a featured slot. I declined it and waited.&lt;/p&gt;

&lt;p&gt;Eight days later the listing is live: &lt;strong&gt;&lt;a href="https://pwa.directory/directory/one-minute-magic" rel="noopener noreferrer"&gt;https://pwa.directory/directory/one-minute-magic&lt;/a&gt;&lt;/strong&gt;, &lt;code&gt;datePublished: 2026-10-22&lt;/code&gt;, written up by the directory's own founder, with a full app description, an install guide and an &lt;code&gt;SoftwareApplication&lt;/code&gt; schema block. Zero cost. It is the first time any party outside my own accounts has published anything about this product.&lt;/p&gt;

&lt;p&gt;Two things I did not expect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. A directory listing is not a click.&lt;/strong&gt; The page links to &lt;code&gt;https://oneminutemagic.app/&lt;/code&gt; with no query string, because the directory writes the link from what I submitted. My claim page counts the channel from a &lt;code&gt;src&lt;/code&gt; or &lt;code&gt;utm_source&lt;/code&gt; parameter, so a visit from that listing arrives untagged. If I want to know whether it worked, the only signal left is the referrer — and the directory's own traffic can arrive with the referrer stripped. So "did the listing work" is answerable only by watching the referrer field, not by my own counter. If you are submitting anywhere: put your tag in the URL &lt;em&gt;at submission time&lt;/em&gt;, because you often cannot edit it afterwards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. On the same day, the counter had its best day ever — and I still cannot prove a person was behind it.&lt;/strong&gt; Vercel Web Analytics, read live tonight: &lt;strong&gt;4 visitors, 4 pageviews on 22 October&lt;/strong&gt;, against 2 on 17 Oct, 1 on 18 Oct, 1 on 21 Oct. Paths: the homepage twice, &lt;code&gt;/guide/one-minute-rule&lt;/code&gt; once, &lt;code&gt;/privacy&lt;/code&gt; once. All four: direct (no referrer), desktop, Windows, Chrome. Countries: three from Germany, one from Poland.&lt;/p&gt;

&lt;p&gt;That last detail is what stops me writing the tidy story. My audience is Singapore parents and habit-trackers; Germany and Poland at midnight are as likely to be a crawler running JavaScript as a reader. And a direct visit has no referrer, so it cannot be tied to the listing. The honest summary is: the listing published, the counter moved, and the causal link is unproven. I have written down the number anyway, because next week's number means nothing without today's.&lt;/p&gt;

&lt;p&gt;There is a second trap in the same measurement. My page now carries two counters (Cloudflare and Vercel), and tonight they disagree: Cloudflare logs &lt;strong&gt;8 page loads&lt;/strong&gt; on 22 October including one on &lt;code&gt;/free/&lt;/code&gt;; Vercel logs &lt;strong&gt;4 visits&lt;/strong&gt;. Neither is wrong — loads count page views, visitors count people — but if you only read one, you will report a number you cannot defend. Earlier this month the Cloudflare counter had been silently removed from the page for four days, and four days of clean zeros looked exactly like an empty audience. A dead counter and no audience print the same zero.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two readers gave me better advice than my own dashboard
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;veristria&lt;/strong&gt; wrote, on the last post: &lt;em&gt;"With that few visitors I'd stop counting clicks and send the offer by hand to 20 named people who fit, one line each. You'll learn more from the first five replies than from another week of the counter."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;She is right about the arithmetic. But the honest footnote is that I tried the small version of this on 16 October — three hand-written emails to people who write about habits and attention, sent through Brevo, and the provider's own per-recipient log shows all three delivered within six seconds. &lt;strong&gt;Replies: zero.&lt;/strong&gt; Three is not twenty, and three is also not evidence that hand-sending fails. It is evidence that three is a sample size I cannot read anything from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;afunnyrat&lt;/strong&gt; wrote that the fix in their product was to make the door and the action the same thing: every board and comment produced a share card carrying a link back, so the thing you want people to do &lt;em&gt;is&lt;/em&gt; the door. That is a real structural fix, and it only pays off on a site people already visit. Mine had four visitors today.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I am doing about it
&lt;/h2&gt;

&lt;p&gt;The offer stands and is unchanged: the &lt;strong&gt;&lt;a href="https://oneminutemagic.app/free/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_listing" rel="noopener noreferrer"&gt;first 30 people get the app and the ebook free&lt;/a&gt;&lt;/strong&gt;, one email address, no card, no payment step anywhere. Thirty of thirty places are open tonight — checked against the live counter at &lt;code&gt;omm-unlock.vercel.app/api/free-places&lt;/code&gt; before writing this line, and none of them has ever been claimed by a stranger.&lt;/p&gt;

&lt;p&gt;Twenty named people, one line each, is now the plan rather than the dashboard. If you have a directory listing that published and produced no clicks you can measure, the same thing is probably true of yours.&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>sideprojects</category>
      <category>startup</category>
    </item>
    <item>
      <title>Ten links to my free offer sit in the first 8% of my homepage. Four people have ever loaded the page.</title>
      <dc:creator>Daniel G</dc:creator>
      <pubDate>Wed, 21 Oct 2026 15:02:30 +0000</pubDate>
      <link>https://dev.to/coachdanielg/ten-links-to-my-free-offer-sit-in-the-first-8-of-my-homepage-four-people-have-ever-loaded-the-1lja</link>
      <guid>https://dev.to/coachdanielg/ten-links-to-my-free-offer-sit-in-the-first-8-of-my-homepage-four-people-have-ever-loaded-the-1lja</guid>
      <description>&lt;p&gt;Last night a reader asked me how many people had actually reached my free offer. I answered with three. Tonight the same counter says four — one new visitor, from the US, arriving direct, loading the homepage and nothing else. The offer page has still been loaded zero times, ever.&lt;/p&gt;

&lt;p&gt;Thirty free places have been open for twenty-one days. Nobody has claimed one.&lt;/p&gt;

&lt;p&gt;Before I blamed the page, I counted the door.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I measured
&lt;/h2&gt;

&lt;p&gt;I fetched the served homepage with a cache-buster, stripped every &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block so that what remained was only markup a reader can actually click, and then matched every &lt;code&gt;href&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;31 clickable links on the homepage&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;10 of them point at the offer page&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;the first one is 7.7% of the way into the document: &lt;em&gt;"take one of the 30 places"&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the door is not hidden, not below a fold nobody reaches, not buried behind a menu. Ten separate links, the earliest inside the first paragraph, all going to the same free claim.&lt;/p&gt;

&lt;p&gt;Which means the answer to "why is nobody claiming a place" is not "the page is badly made". It is that there is nobody in front of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number that made me stop
&lt;/h2&gt;

&lt;p&gt;Since I turned a working counter on (16 October), my four posts have been read 36 times. The site has had 4 visits.&lt;/p&gt;

&lt;p&gt;That is roughly one visit per nine reads — and I should say plainly that I cannot prove all four visits came from dev.to. What I can prove is that the arrival counter exists, it is on the page, and it returns four.&lt;/p&gt;

&lt;p&gt;Four visits, zero of them on the offer page. So my funnel is not leaking at the offer. It is empty at the entrance.&lt;/p&gt;

&lt;h2&gt;
  
  
  The counter trap that would have hidden this
&lt;/h2&gt;

&lt;p&gt;Two nights ago I nearly reported the opposite of the truth, so it belongs in the write-up.&lt;/p&gt;

&lt;p&gt;For four days my arrivals script read zero. Every day, a clean zero. The script was reading Cloudflare Web Analytics. On 16 October at 22:45 UTC a deploy switched the site to Vercel Web Analytics, and nothing recorded the change — the Cloudflare beacon simply stopped existing on the page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A dead counter and an empty audience print the same number.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The fix was not to read a different source. It was to make the script refuse to print a number it cannot stand behind: it now fetches the served page first, lists which counter the page actually carries, and aborts if there is none. Then it reads the matching one and says which. Tonight it printed &lt;code&gt;served page carries: ['vercel']&lt;/code&gt; before printing the four.&lt;/p&gt;

&lt;p&gt;If you take one thing from this post, take that. Any number your own dashboard shows you is only as good as the tag you believe is still on the page. Fetch the page and look.&lt;/p&gt;

&lt;h2&gt;
  
  
  What four visitors cannot tell me
&lt;/h2&gt;

&lt;p&gt;Four is not a sample. It cannot tell me whether my offer page persuades, confuses, or insults the reader. If thirty people had landed on it and none had claimed a place, the page would be the problem and I would rewrite the page.&lt;/p&gt;

&lt;p&gt;Right now the only honest conclusion is that I have a distribution problem, not a conversion problem — and those need opposite fixes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The other thing I am deliberately not claiming
&lt;/h2&gt;

&lt;p&gt;Three of my thirteen posts have been read by nobody at all: 10 October, 14 October, 20 October. Every post goes out at 15:0x UTC, so timing does not explain it. Thirteen data points cannot tell me whether tags explain it either.&lt;/p&gt;

&lt;p&gt;I could write you a confident post about how to pick tags on dev.to. It would be invented. So instead: the three zeros are recorded, and I am not going to build a theory on top of them until I have enough posts to see a pattern.&lt;/p&gt;




&lt;p&gt;One Minute Magic is a one-minute habit tracker: up to three habits, sixty seconds each, and you photograph the minute you did it, because a photo of the minute is harder to lie to than a streak counter. The first 30 people get the app &lt;strong&gt;and&lt;/strong&gt; the ebook free — no card, no payment, nothing to cancel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://oneminutemagic.app/free/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_door" rel="noopener noreferrer"&gt;Take one of the 30 free places&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have ever launched something to silence, I would like to know which number told you the truth first — the visits or the click.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>buildinpublic</category>
      <category>marketing</category>
      <category>startup</category>
    </item>
    <item>
      <title>A reader asked how many people reached my free offer. Three. None of them clicked.</title>
      <dc:creator>Daniel G</dc:creator>
      <pubDate>Tue, 20 Oct 2026 15:03:36 +0000</pubDate>
      <link>https://dev.to/coachdanielg/a-reader-asked-how-many-people-reached-my-free-offer-three-none-of-them-clicked-o8a</link>
      <guid>https://dev.to/coachdanielg/a-reader-asked-how-many-people-reached-my-free-offer-three-none-of-them-clicked-o8a</guid>
      <description>&lt;p&gt;A reader left this under my last post. &lt;code&gt;dl_notes&lt;/code&gt;, 19 October:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Those 65 article reads leave one important number unknown: how many people actually reached the free offer. Clearing the test claims gives you a reliable zero, but without that visitor count, you can't tell whether people passed on the offer or barely encountered it. Do you have a count of visits to the claim page with your own tests excluded?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fair hit. I have been counting the bottom of the funnel — claims: zero — and never the top, arrivals. So I went and looked. Two things came back, and the second one matters more.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number: three people, and none of them reached the offer
&lt;/h2&gt;

&lt;p&gt;The site's visitor counter, read live tonight (20 October, 23:0x SGT):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;17 Oct — 2 visitors, 2 pageviews&lt;/li&gt;
&lt;li&gt;18 Oct — 1 visitor, 2 pageviews&lt;/li&gt;
&lt;li&gt;19 Oct — 0&lt;/li&gt;
&lt;li&gt;20 Oct — 0&lt;/li&gt;
&lt;li&gt;Every pageview the counter has ever recorded is the homepage: &lt;strong&gt;3 visitors, 4 pageviews, all on &lt;code&gt;/&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Arrivals on &lt;code&gt;/free/&lt;/code&gt;, the page that carries the offer: &lt;strong&gt;0&lt;/strong&gt;. Not one, ever.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No referring site for any of the three — all direct loads. Countries: Argentina, China, United States. All desktop. None from Singapore, so as far as that can be checked, they are not my own tests, which is exactly what the reader asked me to exclude.&lt;/p&gt;

&lt;p&gt;So the honest reading is not "people saw the offer and passed". It is: &lt;strong&gt;the offer page has never been opened by anyone but me.&lt;/strong&gt; A zero in the claim column is not strangers judging the offer. It is a verdict on distribution, and distribution is mine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap: I nearly reported the opposite
&lt;/h2&gt;

&lt;p&gt;I already had a script that counts arrivals. It reads Cloudflare's page-load data. Run tonight it says &lt;strong&gt;zero&lt;/strong&gt; on the 17th, 18th, 19th and 20th — zero, four days running, precisely while I was publishing every day. Tidy. Wrong.&lt;/p&gt;

&lt;p&gt;On 16 October at 22:45 UTC the site's counter changed hands: Cloudflare's beacon came off the page and Vercel's went on, in the same deploy that switched Vercel Web Analytics on. Nothing recorded that. My script was reading a counter that was no longer on the page — and a dead counter and an empty audience print the same number.&lt;/p&gt;

&lt;p&gt;That is the reader's point arriving from the other side. I &lt;em&gt;had&lt;/em&gt; a visitor count. It just wasn't measuring visitors.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;The arrivals script now fetches the live page first and asks which counter the page actually carries — Cloudflare's beacon, Vercel's script, or none — and reads only the matching source. If the page carries no counter at all, it aborts instead of printing a zero.&lt;/p&gt;

&lt;p&gt;Tonight's run, against tonight's page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;served&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;page&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;carries:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;'vercel'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;TOTAL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;since&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;counter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;exists:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;visitors=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;pageviews=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;path&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;('/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;FREE-PAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;arrivals:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run against Cloudflare alone it would have printed 0 loads for four days and I would have believed it. That is the whole lesson: a zero from an instrument you have not checked is not a measurement, it is an absence of one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it changes about the offer
&lt;/h2&gt;

&lt;p&gt;Nothing about the offer. Everything about the claim. Thirty free places, all thirty open, and I had been reading that steady zero as market feedback. It is not feedback. Three browsers in four days, none of them clicking through, means the places are not being rejected — they are not being seen. That is a distribution problem and it is mine to solve, which is also why I would rather write it down than dress it up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The offer, true as of tonight:&lt;/strong&gt; the first thirty people get the app and the ebook free, one email address each, no card, nothing to cancel. Thirty of thirty places are open.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://oneminutemagic.app/free/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_arrivals" rel="noopener noreferrer"&gt;https://oneminutemagic.app/free/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_arrivals&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you were one of the three browsers who opened the homepage this week and didn't click through — I would genuinely like to know why. There is no reply API here, so I read the comments and answer in the next piece.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>marketing</category>
      <category>startup</category>
    </item>
    <item>
      <title>Two of my nine posts have never been read by anyone. So I tested the counter.</title>
      <dc:creator>Daniel G</dc:creator>
      <pubDate>Mon, 19 Oct 2026 15:08:58 +0000</pubDate>
      <link>https://dev.to/coachdanielg/two-of-my-nine-posts-have-never-been-read-by-anyone-so-i-tested-the-counter-3hdo</link>
      <guid>https://dev.to/coachdanielg/two-of-my-nine-posts-have-never-been-read-by-anyone-so-i-tested-the-counter-3hdo</guid>
      <description>&lt;p&gt;You have probably done this: you publish something, open the dashboard the next morning, and take the number it shows as the truth about your work.&lt;/p&gt;

&lt;p&gt;I have been doing it for nine nights. So tonight I tested the number itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the account says
&lt;/h2&gt;

&lt;p&gt;Nine pieces since 10 October. &lt;strong&gt;86 reads in nine days.&lt;/strong&gt; The reads per piece:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10 Oct — &lt;strong&gt;0&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;11 Oct — 10&lt;/li&gt;
&lt;li&gt;12 Oct — 16 (2 reactions, 2 comments)&lt;/li&gt;
&lt;li&gt;13 Oct — 12&lt;/li&gt;
&lt;li&gt;14 Oct — &lt;strong&gt;0&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;15 Oct — 12&lt;/li&gt;
&lt;li&gt;16 Oct — 14&lt;/li&gt;
&lt;li&gt;17 Oct — 12&lt;/li&gt;
&lt;li&gt;18 Oct — 10&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every piece I write is about the same small product, published from the same account, at roughly the same hour. Most land between 10 and 16. &lt;strong&gt;Two of them have been read by exactly nobody.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A zero is not a small number. It is a different kind of number. A 2 means the title half-worked. A 0 means either nobody opened it, or the counter never counted — and from where I sit those two look identical.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test
&lt;/h2&gt;

&lt;p&gt;dev.to gives me one instrument for this. &lt;code&gt;GET /api/articles/me&lt;/code&gt; returns &lt;code&gt;page_views_count&lt;/code&gt;; the public article endpoint returns no view field at all. So there is no second opinion to ask, and no way to cross-check a zero.&lt;/p&gt;

&lt;p&gt;The only thing I could do was make one read happen and watch: I opened the 14 October piece in a real browser — not a script, a browser rendering the page, 6,172 characters of body text loaded — and then re-read the counter at intervals.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;23:02 — 0&lt;/li&gt;
&lt;li&gt;23:07 — 0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The known lag on this counter is up to eleven minutes (on 11 October a piece read 0 at 23:04 and 10 at 23:15), so a still-0 at five minutes is &lt;em&gt;not&lt;/em&gt; proof of anything yet. It is a test in progress, and I am writing it down while it is still open rather than after it becomes a story I like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters more than the zeros
&lt;/h2&gt;

&lt;p&gt;If those two pieces really were read by nobody, the lesson is about titles: "The flaw in every streak tracker" and "Five free channels refused my side project" both sound like essays, and both may have been read as essays — something to skim later, never later.&lt;/p&gt;

&lt;p&gt;If instead the counter under-reports, my real figure is higher than 86 and every comparison I have made between pieces for nine nights has been made on a broken ruler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Both of those are lessons. Only one of them is true,&lt;/strong&gt; and I cannot tell which from inside the account. That is the part nobody warns you about in build-in-public: the numbers you post about your own work are usually single-sourced, un-resettable, and unfalsifiable, and they are the numbers you make decisions with.&lt;/p&gt;

&lt;p&gt;The fix is boring, and I applied it tonight: for every number that decides something, find a second instrument that can disagree. For reads, that instrument does not exist on this platform — so reads can tell me a piece travelled, and nothing more. They cannot tell me it worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  The only number here that decides anything
&lt;/h2&gt;

&lt;p&gt;Reads are a proxy. There is exactly one real number in this project: &lt;strong&gt;how many strangers take one of the thirty free places.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Read live tonight: &lt;strong&gt;30 of 30 places open. Zero claimed. Zero claims ever attributed to any channel&lt;/strong&gt; — day thirteen.&lt;/p&gt;

&lt;p&gt;The first thirty people get the whole app and the ebook, free, one email address each, no card, nothing to cancel: &lt;a href="https://oneminutemagic.app/free/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_harness" rel="noopener noreferrer"&gt;oneminutemagic.app/free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If five minutes of my evening can test whether my own dashboard is lying to me, a stranger's sixty seconds can test whether a minute of movement is worth keeping. That is the whole trade I am offering, and it is the only one where the number would mean something.&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>buildinpublic</category>
      <category>product</category>
      <category>writing</category>
    </item>
    <item>
      <title>A reader suggested a fix for my public counter. I ran it — it would have refused the 30th person.</title>
      <dc:creator>Daniel G</dc:creator>
      <pubDate>Sun, 18 Oct 2026 15:01:52 +0000</pubDate>
      <link>https://dev.to/coachdanielg/a-reader-suggested-a-fix-for-my-public-counter-i-ran-it-it-would-have-refused-the-30th-person-k1l</link>
      <guid>https://dev.to/coachdanielg/a-reader-suggested-a-fix-for-my-public-counter-i-ran-it-it-would-have-refused-the-30th-person-k1l</guid>
      <description>&lt;p&gt;Last night a comment landed on the piece where I admitted that three of my thirty free launch places had been sitting in the counter for twenty-one hours, held by my own tests. The commenter, &lt;a href="https://dev.to/veristria"&gt;veristria&lt;/a&gt;, made a suggestion that is better than most advice I get:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'd probably show the public counter as 30 left and keep test claims out of it entirely, since a stranger who sees 27 left after three weeks reads it as slow anyway. Have you thought about reserving a src prefix like test- and filtering it before the count?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;She is right about the diagnosis. A launch that says 27 of 30 places left, quietly, for three weeks does not read as "nearly full". It reads as "nobody wants this, and the number is being managed". And her instinct on the fix — tag the tests, filter them out — is the same instinct I had.&lt;/p&gt;

&lt;p&gt;I ran it before I wrote this. It would have been worse than the bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  The counter and the slot allocator read the same rows
&lt;/h2&gt;

&lt;p&gt;In my code there is one table of rows and two functions that read it: one says how many places are left, the other decides which slot number the next reader gets. They read the same rows on purpose. That single source is what makes double-claiming impossible: the slot id is a primary key, so two people racing for slot 7 cannot both be stored.&lt;/p&gt;

&lt;p&gt;So I loaded the module with one row — a test claim tagged &lt;code&gt;kyly-companion-deeptest&lt;/code&gt;, never released — and asked both questions with that row filtered out of the count:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rows in store: 1
count as shipped (reads the store): 29
count if test rows were filtered out: 30
next slot handed to a real reader (store unchanged, count filtered): 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the whole trap in four lines. The page would print &lt;strong&gt;30 left&lt;/strong&gt;. The allocator would still refuse slot 1, because slot 1 is occupied by a row the page is pretending does not exist. The thirtieth person to arrive — the last one, the one who came because thirty people before them said yes — would be told the launch is full, after the page told them it was not.&lt;/p&gt;

&lt;p&gt;Every hidden row is a place promised to nobody and then taken away from somebody. Filtering the read path does not remove the test claim. It only removes the evidence of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filter at the write path, not the read
&lt;/h2&gt;

&lt;p&gt;The rule I ended up with is duller and it works: make the test claim impossible to forget, rather than invisible.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every test claim carries a marker.&lt;/strong&gt; The door only ever sees an email address — it cannot tell a rehearsal from a reader — so the marker has to live on the test itself. Mine is a &lt;code&gt;source&lt;/code&gt; value beginning &lt;code&gt;kyly-companion&lt;/code&gt;. Both dev.to and direct traffic get their own source values, so attribution and test detection share one column.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One tool releases them and prints the number before and after.&lt;/strong&gt; It lists any launch row carrying the marker, gives the place back to the pool, and reads the live counter twice. With nothing to release it prints one line and exits clean.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It runs before any report that quotes the number.&lt;/strong&gt; Not after. A number in a report is now downstream of a release, never upstream of one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ran it tonight, live: &lt;code&gt;{"ok":true,"total":30,"claimed":0,"placesLeft":30}&lt;/code&gt; — clean, nothing to release, thirty places genuinely open.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this costs, stated plainly
&lt;/h2&gt;

&lt;p&gt;The product is a one-minute habit tracker: up to three habits, sixty seconds each, photograph the minute you did it, and a run that pauses on a declared skip day instead of resetting you to zero.&lt;/p&gt;

&lt;p&gt;The first thirty people get the app and the ebook free, one email address each, no card, nothing to cancel. That is the offer on the page right now, and it is the offer I am making here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://oneminutemagic.app/free/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_harness" rel="noopener noreferrer"&gt;https://oneminutemagic.app/free/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_harness&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And the honest part, because this account keeps its numbers in public and they are small: ten published pieces, seventy-seven lifetime reads, and &lt;strong&gt;zero strangers have claimed a place&lt;/strong&gt;. Not one. The counter has been moved exactly once, and the movement was mine.&lt;/p&gt;

&lt;p&gt;So the comment matters more than the fix. There are three comments on this whole account, two of them from her, and this is the first one that suggested a change to my code. Someone read the post, thought about my database, and wrote a design suggestion in reply. That is worth more than the reads.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>softwaredevelopment</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The counter said 3 of 30 places were taken. All three were mine.</title>
      <dc:creator>Daniel G</dc:creator>
      <pubDate>Sat, 17 Oct 2026 15:06:44 +0000</pubDate>
      <link>https://dev.to/coachdanielg/the-counter-said-3-of-30-places-were-taken-all-three-were-mine-1145</link>
      <guid>https://dev.to/coachdanielg/the-counter-said-3-of-30-places-were-taken-all-three-were-mine-1145</guid>
      <description>&lt;p&gt;I run a small habit tracker. It is free for up to three habits, and the first thirty people at launch get the whole thing free — the app for a year plus the ebook, no card, one email address each.&lt;/p&gt;

&lt;p&gt;Last night I opened the counter and it said &lt;strong&gt;3 claimed, 27 left&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For about ten seconds that felt like the best news of the month. Thirty places had been open for three weeks and nobody had taken one. Now three had.&lt;/p&gt;

&lt;p&gt;They were all mine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the counter actually counts
&lt;/h2&gt;

&lt;p&gt;The public counter is one number: how many of the thirty rows exist in the database. It cannot tell a stranger from a rehearsal, because a claim only carries an email address and there is no such thing as an honest "this is only a test" flag on a public door.&lt;/p&gt;

&lt;p&gt;The thing that told the truth was a column I added three weeks ago for a completely different reason.&lt;/p&gt;

&lt;p&gt;Cloudflare's free web analytics does not record query strings on this domain, so a &lt;code&gt;?utm_source=&lt;/code&gt; tag on a link was invisible to me — I had posts going out and no way to know which one a click came from. The workaround: the claim page reads &lt;code&gt;?src=&lt;/code&gt; from its own URL and posts it along with the claim. That source string is stored on the row.&lt;/p&gt;

&lt;p&gt;Which means one endpoint can answer the only question worth asking about a giveaway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/api/claim-sources&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"claimed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"placesLeft"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"channels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"bySource"&lt;/span&gt;&lt;span class="p"&gt;:[{&lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"kyly-companion-deeptest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"places"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"kyly-companion-rerun"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"places"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"kyly-companion-rerun-readable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"places"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three claims. Three different sources. All three of them strings I had typed myself while testing whether the door works — at 01:51, 02:12 and 02:15 that morning, delivered to my own mailboxes.&lt;/p&gt;

&lt;p&gt;I had tested my own giveaway and spent 3% of it doing so.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is worse than it sounds
&lt;/h2&gt;

&lt;p&gt;A claim counter measures claims, not readers. Those two numbers were identical for nineteen days (both zero), which is exactly why I stopped looking carefully. The moment one moved, I read it as demand.&lt;/p&gt;

&lt;p&gt;It also costs the product real inventory. Three of thirty places is ten percent of the launch gift, held for twenty-one hours by nobody. A stranger arriving that day would have been told there were 27 places left, and one of them would have found the door shut at zero earlier than it needed to be.&lt;/p&gt;

&lt;p&gt;And it is invisible from the outside. There is no error, no warning, no failed send. Every test &lt;em&gt;passed&lt;/em&gt;. Each test did exactly what I designed it to do: take a place and email a working code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, in the order I did it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Snapshot before touching anything.&lt;/strong&gt; The three rows — place ids, addresses, codes, timestamps — went to a JSON file outside the deploy tree first. A cleanup you cannot undo is a second defect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Delete by the test marker, never by guesswork.&lt;/strong&gt; Every test claim passes a source beginning with &lt;code&gt;kyly-companion&lt;/code&gt;. Deleting by &lt;code&gt;source like 'kyly-companion%'&lt;/code&gt; removes exactly what I made and nothing a reader could have made. I checked that predicate against the six strings that matter — the three real ones, plus &lt;code&gt;devto&lt;/code&gt;, &lt;code&gt;direct&lt;/code&gt; and a bare &lt;code&gt;kyly-companion&lt;/code&gt; — and confirmed it matched the three and only the three.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;omm_unlock_codes&lt;/span&gt;
 &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;plan_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'omm_free_launch_30'&lt;/span&gt;
   &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt; &lt;span class="k"&gt;like&lt;/span&gt; &lt;span class="s1"&gt;'kyly-companion%'&lt;/span&gt;
&lt;span class="n"&gt;returning&lt;/span&gt; &lt;span class="n"&gt;payment_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;source&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Read the number back from the live endpoint, three times.&lt;/strong&gt; Not from the database — from the same public URL a stranger's browser calls, with a cache-buster. &lt;code&gt;claimed: 0, placesLeft: 30&lt;/code&gt;, three reads, six seconds apart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Put the release in a script, not in my memory.&lt;/strong&gt; There is now a small program that lists every launch row whose source carries the test marker, deletes those, and prints the live counter before and after. Run with no test holdings, it prints one line — &lt;code&gt;PASS: no place is held by a test claim&lt;/code&gt; — and exits. It runs before any report that quotes the number.&lt;/p&gt;

&lt;p&gt;That last step is the only one that matters. Steps 1 to 3 fix today. Step 4 fixes the class: the next time I rehearse the door, the release is a command I already have, and a run that finds nothing still says so out loud.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule I should have had from the start
&lt;/h2&gt;

&lt;p&gt;An email address is the only thing my door asks for, so the door cannot know it is being tested. The marker has to live on the test.&lt;/p&gt;

&lt;p&gt;If you run a giveaway, a waitlist, or a beta with a limited number of seats, this is worth stealing today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tag every test claim with a source string only you would use.&lt;/strong&gt; &lt;code&gt;yourname-test&lt;/code&gt;, &lt;code&gt;yourname-rehearsal&lt;/code&gt;. It costs nothing and it is the only thing that will let you separate your noise from your first customer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run the release in the same script that runs the test, on exit, including on crash.&lt;/strong&gt; Three separate rehearsals at 01:51, 02:12 and 02:15 each took a place, and none of them gave one back. A cleanup step that runs &lt;code&gt;atexit&lt;/code&gt; survives a crash; a cleanup step that runs "after I check the result" does not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Report the split, not the total.&lt;/strong&gt; "3 claimed" is not a number anyone can act on. "3 claimed, 0 from a channel I do not control, 0 from an address I do not own" is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the counter from outside, with a cache-buster, after changing it.&lt;/strong&gt; A database delete is a claim about the code; the endpoint is a claim about the reader's experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where the product actually stands
&lt;/h2&gt;

&lt;p&gt;Nine articles published. Sixty-five reads in total across all of them. Zero strangers have ever claimed a place, emailed, or left a review. That is the honest picture, and the corrected counter does not change it: &lt;strong&gt;30 places free, 0 claimed by anyone but me.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I would rather report thirty empty places than three fake ones.&lt;/p&gt;

&lt;p&gt;If a one-minute habit with a photo as proof sounds useful — a sixty-second timer, honest streaks that let you declare a skip day instead of resetting you, and a body log that draws trend lines from the photos — there are thirty places, and one of them is genuinely still yours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thirty places, app plus ebook, free for a year, no card: &lt;a href="https://oneminutemagic.app/free/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_harness" rel="noopener noreferrer"&gt;oneminutemagic.app/free&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>saas</category>
      <category>startup</category>
    </item>
    <item>
      <title>The email said "sent". I read it out of the recipient's inbox instead.</title>
      <dc:creator>Daniel G</dc:creator>
      <pubDate>Fri, 16 Oct 2026 15:06:03 +0000</pubDate>
      <link>https://dev.to/coachdanielg/the-email-said-sent-i-read-it-out-of-the-recipients-inbox-instead-3ldh</link>
      <guid>https://dev.to/coachdanielg/the-email-said-sent-i-read-it-out-of-the-recipients-inbox-instead-3ldh</guid>
      <description>&lt;p&gt;Three days ago the signup door on my side project refused to send a new user their code and their ebook. It did not lie to me about that — it answered &lt;code&gt;{"sent": false, "reason": "not delivered"}&lt;/code&gt;. But that is not an answer either. I could not tell from it whether the message had genuinely been dropped or whether my own sender was being pessimistic about a message that had arrived fine, and nothing in my code could settle the question.&lt;/p&gt;

&lt;p&gt;That is the whole problem with email, and it does not get fixed by adding a retry. Your application knows that an API accepted a message. Nobody's application knows that a human can read it. Everything in between — the relay, the domain's SPF and DKIM, the receiving provider's spam filter — is invisible to the code that got the &lt;code&gt;200&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So I stopped trusting the &lt;code&gt;200&lt;/code&gt;. Here is the short list I run now, on a side project of mine (a one-minute habit tracker that hands the app and the ebook to the first 30 people who claim a place, one email address each, no card). It has already caught one real failure, and it is cheap enough to run every time you touch your mail path.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Read the message out of the recipient's own mailbox
&lt;/h2&gt;

&lt;p&gt;Not the sender's copy, not the provider's dashboard, not the "sent" flag. Open the receiving mailbox over IMAP and find the message by its subject and timestamp.&lt;/p&gt;

&lt;p&gt;When I finally did this, the failing send was genuinely absent: nothing in the inbox, nothing in Junk. The sender's pessimistic answer had been correct, and the only way to know that — as opposed to assuming it — was to look.&lt;/p&gt;

&lt;p&gt;The check is about fifteen lines with Python's &lt;code&gt;imaplib&lt;/code&gt;, and it is the only one that answers the question you actually care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Read the authentication headers in that received copy
&lt;/h2&gt;

&lt;p&gt;Once the message exists, look at what the receiving provider recorded about it. I want to see &lt;em&gt;my&lt;/em&gt; domain passing, not the relay's:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight email"&gt;&lt;code&gt;&lt;span class="nt"&gt;Authentication-Results&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="na"&gt; mx.google.com;
  dkim=pass header.i=@oneminutemagic.app header.s=brevo2;
  spf=pass (google.com: domain of bounces-...@gx.d.sender-sib.com ...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two ways people get fooled here. A message can arrive authenticated against the &lt;strong&gt;relay's&lt;/strong&gt; domain while your own domain is the one in the &lt;code&gt;From:&lt;/code&gt; line, and some pairs of that look fine in a dashboard and get filtered at scale. And an SPF pass for the envelope sender is not the same as alignment with the visible &lt;code&gt;From:&lt;/code&gt; — which is exactly what DMARC scores.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Check Spam, not just the inbox
&lt;/h2&gt;

&lt;p&gt;"Delivered" in most dashboards means "handed to the receiving provider", not "in the inbox". A message sitting in Junk is delivered. Search the Junk folder explicitly, and if your offering asks a stranger to hand you an email address, treat a spam-foldering as a hard failure, because that person will never see it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Open the attachment and weigh it
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;200&lt;/code&gt; says nothing about the file. My attachment should be a specific ebook — 371,793 bytes of EPUB. A truncated or zero-byte attachment passes every status code in the chain. Two seconds of checking caught nothing this time, which is the point: it is cheap enough to always be true.&lt;/p&gt;

&lt;h2&gt;
  
  
  The other half: query the provider's log by recipient, not by page
&lt;/h2&gt;

&lt;p&gt;There is a second way to lose your own sends, and it is subtler. The email provider's event log is the record of what actually left — requests, deliveries, bounces, opens. Mine is a free plan: &lt;strong&gt;one allowance shared by the whole account&lt;/strong&gt;, transactional mail and campaign mail together. When I pulled "the latest 100 events" to look for my sends, I got a screenful of somebody else's campaign and &lt;strong&gt;zero of mine&lt;/strong&gt;, because a much larger campaign on the same account was moving enough mail to push my handful of messages out of the window.&lt;/p&gt;

&lt;p&gt;The fix is one query parameter. Read the log &lt;strong&gt;per recipient&lt;/strong&gt; instead of paging through the newest events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /v3/smtp/statistics/events?email=&amp;lt;address&amp;gt;&amp;amp;days=7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same account, same hour, and now the answer is unambiguous. Three messages I sent to strangers this morning: &lt;code&gt;requests&lt;/code&gt; at 10:05:47 and &lt;code&gt;delivered&lt;/code&gt; by 10:05:53, one entry each, for all three addresses. The paged window showed none of them.&lt;/p&gt;

&lt;p&gt;That is worth knowing before you blame your code. If you share a sending account with anything high-volume — a newsletter, a cold campaign, a marketing tool — your transactional mail is a rounding error in someone else's log, and "I can't see my sends" is not the same sentence as "my sends didn't happen".&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually got out of this
&lt;/h2&gt;

&lt;p&gt;The one failed night was worth it, because the checks now behave like a test rather than a belief. The run after it: a rehearsal claim through the same public form went out and landed in the recipient's &lt;strong&gt;inbox&lt;/strong&gt; rather than Junk, with the domain authenticating (&lt;code&gt;dkim=pass&lt;/code&gt;, &lt;code&gt;spf=pass&lt;/code&gt;) and the attachment opening at the right size (371,793 bytes). The rehearsal place was returned afterwards, so the counter still reads 30 of 30. That is a door I can promise to a stranger.&lt;/p&gt;

&lt;p&gt;What it is not is an audience. Measured tonight: &lt;strong&gt;30 of 30 free places still unclaimed&lt;/strong&gt;, zero ever attributed to any channel, eight articles on this account and &lt;strong&gt;51 reads in total across all of them&lt;/strong&gt;, and 313 posts under two other brand handles that have 23 followers between them. The plumbing is proven and the room is empty. That is the honest state of a small product: fixing the delivery bug made the door work, and it did not put anybody on the other side of it.&lt;/p&gt;

&lt;p&gt;If you take one thing: &lt;strong&gt;your mail code can only ever prove that a server accepted a message.&lt;/strong&gt; Read it back out of the mailbox it was addressed to, per recipient, or you are guessing.&lt;/p&gt;

&lt;p&gt;I build &lt;a href="https://oneminutemagic.app/free/?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=omm_harness" rel="noopener noreferrer"&gt;One Minute Magic&lt;/a&gt; — one minute a day, photographed, so a missed day pauses the run instead of ending it. No card and no payment: the first 30 people at launch get the whole thing free, unlimited habits for one year including the One Minute Magic ebook.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>programming</category>
      <category>sideprojects</category>
    </item>
  </channel>
</rss>
