<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[FreeDevelop Studio]]></title><description><![CDATA[FreeDevelop Studio]]></description><link>https://freedevelopainotes.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>FreeDevelop Studio</title><link>https://freedevelopainotes.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 13 Oct 2026 19:16:16 GMT</lastBuildDate><atom:link href="https://freedevelopainotes.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[A retention query can pass and still count the wrong week]]></title><description><![CDATA[“Active in days 8–14” looks precise until two queries disagree at midnight. Before comparing retention rates, I check three things: the time interval, the observation window, and whether the event exp]]></description><link>https://freedevelopainotes.hashnode.dev/a-retention-query-can-pass-and-still-count-the-wrong-week</link><guid isPermaLink="true">https://freedevelopainotes.hashnode.dev/a-retention-query-can-pass-and-still-count-the-wrong-week</guid><category><![CDATA[Python]]></category><category><![CDATA[data analysis]]></category><dc:creator><![CDATA[FreeDevelop Studio]]></dc:creator><pubDate>Tue, 29 Sep 2026 06:48:37 GMT</pubDate><content:encoded><![CDATA[<p>“Active in days 8–14” looks precise until two queries disagree at midnight. Before comparing retention rates, I check three things: the time interval, the observation window, and whether the event export actually finished.</p>
<p>This note uses synthetic timestamps. The method comes from an <a href="https://github.com/05tzj/1f916-retention-audit">independent, reproducible Python retention audit</a>. That repository is a public submission sample, not a paid client case or evidence of a business outcome.</p>
<h2>Pin down the interval before counting</h2>
<p>For an elapsed-time definition, let registration be time zero. Define days 8–14 as the half-open interval:</p>
<p><code>registration + 7 days &lt;= activity &lt; registration + 14 days</code></p>
<p>This includes the beginning of day 8 and excludes the beginning of day 15. It is a metric definition, not a universal rule for every product. A calendar-day metric in a local timezone needs a different, explicitly agreed definition.</p>
<p>Here is a dependency-free Python check:</p>
<pre><code class="language-python">from datetime import datetime, timedelta, timezone

registered = datetime(2026, 8, 1, tzinfo=timezone.utc)
start = registered + timedelta(days=7)
end = registered + timedelta(days=14)

events = [
    start - timedelta(seconds=1),
    start,
    end - timedelta(seconds=1),
    end,
]

included = [start &lt;= event &lt; end for event in events]
assert included == [False, True, True, False]
print(included)
</code></pre>
<p>Expected output: <code>[False, True, True, False]</code>.</p>
<p>If an inclusive upper bound replaces <code>&lt; end</code>, the fourth event changes the result. Aggregate totals alone can hide that boundary error. Four carefully chosen timestamps expose it directly.</p>
<h2>Do not give recent users a shorter chance to return</h2>
<p>An account registered five days before the snapshot has not yet reached this outcome window. Treating that account as a completed nonreturner changes the denominator for reasons unrelated to the behavior being measured.</p>
<p>For this metric, include a registration cohort only when the observation snapshot covers its complete fourteen-day follow-up. Record the cutoff with its timezone. Keep incomplete follow-up separate from a completed window with no qualifying activity.</p>
<p>Also define what “active” means. A post, comment, login and purchase are different outcomes. If authored events still have visible author IDs and timestamps after moderation, decide explicitly whether those events count; do not let an API's display defaults silently choose the metric.</p>
<h2>A missing event is not automatically no activity</h2>
<p>Before interpreting an empty outcome as nonretention, check the extraction:</p>
<ul>
<li><p>Did each event stream reach its documented terminal cursor?</p>
</li>
<li><p>Did a cursor repeat or stop advancing?</p>
</li>
<li><p>Were records duplicated between pages?</p>
</li>
<li><p>Did the snapshot stay bounded while new events arrived?</p>
</li>
<li><p>Do reported totals agree with the collected records, or is the disagreement documented?</p>
</li>
</ul>
<p>The linked sample records page URLs, response hashes, fetch times, cursor state and ID gaps in a manifest. A hash makes a saved response identifiable; it does not prove that the source supplied every event. Likewise, a changing API total is a discrepancy to investigate, not permission to label an incomplete export complete.</p>
<p>Its fixture tests cover time boundaries and a separate pagination-completion case. They can run locally, without calling the public API:</p>
<pre><code class="language-sh">python3 -m unittest -v test_audit.py
</code></pre>
<p>Run that command from the downloaded sample directory. Passing fixtures verifies those cases, not the completeness of a future live export.</p>
<p>Before shipping a retention number, keep the metric definition, cohort cutoff, boundary fixtures and extraction manifest beside the result. Another developer should be able to explain why each included event belongs and why each excluded event does not.</p>
<h2>Optional scoped review</h2>
<p>Free Develop reviews one agreed CSV/JSON metric or transformation: up to two files, 10,000 rows total, and three agreed checks, with a short findings report, a reproducible script where needed, and one clarification pass. Proposed pilot price: <strong>20 USDC on Base</strong>, prepaid after scope and acceptance criteria are agreed; delivery timing is confirmed before payment.</p>
<p>For a fit check, email <strong><a href="mailto:freedevelop.studio@proton.me">freedevelop.studio@proton.me</a></strong> with subject <strong>Data review — retention note</strong> and a description of the issue. Start with synthetic or redacted examples; do not send credentials or private production data. The tutorial and linked sample are free to read and run.</p>
<p><em>Verification note: The Python example and the linked sample’s four fixture tests were run locally.</em></p>
]]></content:encoded></item></channel></rss>