<?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: desgh white</title>
    <description>The latest articles on DEV Community by desgh white (@desgh_white_7e4c654897816).</description>
    <link>https://dev.to/desgh_white_7e4c654897816</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%2F4020165%2F017e6d3a-173e-4404-87c6-42f2db79349b.png</url>
      <title>DEV Community: desgh white</title>
      <link>https://dev.to/desgh_white_7e4c654897816</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/desgh_white_7e4c654897816"/>
    <language>en</language>
    <item>
      <title>Modeling Self-Exclusion as an Explicit State Machine</title>
      <dc:creator>desgh white</dc:creator>
      <pubDate>Tue, 18 Aug 2026 16:46:07 +0000</pubDate>
      <link>https://dev.to/desgh_white_7e4c654897816/modeling-self-exclusion-as-an-explicit-state-machine-3bi4</link>
      <guid>https://dev.to/desgh_white_7e4c654897816/modeling-self-exclusion-as-an-explicit-state-machine-3bi4</guid>
      <description>&lt;p&gt;Some product rules are too important to live as scattered &lt;code&gt;if&lt;/code&gt; checks. Account restrictions — cooling-off, self-exclusion, reactivation — are one of them: get the transitions wrong and you either trap a user or fail to honor a limit they set. A finite state machine makes the rules auditable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enumerate states and legal transitions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ACTIVE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;COOLING_OFF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cooling_off&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;     &lt;span class="c1"&gt;# short, timed, auto-reverts
&lt;/span&gt;    &lt;span class="n"&gt;EXCLUDED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;excluded&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;           &lt;span class="c1"&gt;# long, cannot self-reverse
&lt;/span&gt;    &lt;span class="n"&gt;CLOSED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;closed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;TRANSITIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ACTIVE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COOLING_OFF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EXCLUDED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLOSED&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COOLING_OFF&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ACTIVE&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;          &lt;span class="c1"&gt;# only after the timer
&lt;/span&gt;    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EXCLUDED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLOSED&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;          &lt;span class="c1"&gt;# no path back to ACTIVE
&lt;/span&gt;    &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLOSED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timer_elapsed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;TRANSITIONS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;illegal &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COOLING_OFF&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;timer_elapsed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cooling-off still active&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The value is in the transitions you forbid
&lt;/h2&gt;

&lt;p&gt;Making &lt;code&gt;EXCLUDED -&amp;gt; ACTIVE&lt;/code&gt; unreachable in code means no endpoint, feature flag, or support tool can accidentally re-enable a self-excluded account. The illegal transition throws instead of silently succeeding. That's a guarantee a pile of booleans can't give you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Log every transition
&lt;/h2&gt;

&lt;p&gt;Persist an append-only history of &lt;code&gt;(from, to, actor, reason, ts)&lt;/code&gt;. When someone asks "why is this account restricted," the answer is a query, not an archaeology dig — and it's the record you'll want during an audit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;Responsible-play controls are a good real-world example because the transitions are legally load-bearing. Seeing how a site like &lt;a href="https://nonebet-casino.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt; presents its deposit-limit and self-exclusion options maps neatly onto states a user can enter freely but not always exit on demand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Name the states, whitelist the legal transitions, make the dangerous ones unrepresentable, and log the rest. The state machine turns a safety-critical rule from hopeful &lt;code&gt;if&lt;/code&gt;-checks into something you can prove.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Deterministic Simulation: Reproducing a Physics Result From a Seed</title>
      <dc:creator>desgh white</dc:creator>
      <pubDate>Tue, 18 Aug 2026 16:45:23 +0000</pubDate>
      <link>https://dev.to/desgh_white_7e4c654897816/deterministic-simulation-reproducing-a-physics-result-from-a-seed-28d2</link>
      <guid>https://dev.to/desgh_white_7e4c654897816/deterministic-simulation-reproducing-a-physics-result-from-a-seed-28d2</guid>
      <description>&lt;p&gt;A simulation you can't reproduce is a simulation you can't debug. Whether it's a wheel, a particle system, or a Monte Carlo pricing model, seeding every source of randomness turns "it failed once, somewhere" into a bug you can replay on demand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thread the seed, don't read the global
&lt;/h2&gt;

&lt;p&gt;The cardinal sin is calling a global &lt;code&gt;random()&lt;/code&gt;. The moment two subsystems share global state, execution order changes results. Pass a seeded generator explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;spin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pockets&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;rng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                 &lt;span class="c1"&gt;# isolated, reproducible
&lt;/span&gt;    &lt;span class="n"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;8.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;12.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;friction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# deterministic physics from here on
&lt;/span&gt;    &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;pockets&lt;/span&gt;
        &lt;span class="n"&gt;velocity&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;friction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;spin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;spin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                   &lt;span class="c1"&gt;# same seed, same result, always
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reproducibility is a testing superpower
&lt;/h2&gt;

&lt;p&gt;With a seed you can pin a failing scenario as a regression test, bisect a divergence between two builds, and record-replay a production incident. "Flaky" almost always means "unseeded shared state," not "genuinely random."&lt;/p&gt;

&lt;h2&gt;
  
  
  Beware hidden nondeterminism
&lt;/h2&gt;

&lt;p&gt;Dict iteration order, floating-point summation order across threads, and wall-clock reads all sneak nondeterminism past your seed. Sort before you iterate, reduce in a fixed order, and inject the clock.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;Wheel-based games are a tidy example because the outcome is a single reproducible integer given a seed. A game like &lt;a href="https://roulettino-casinoau.com/" rel="noopener noreferrer"&gt;visit the website&lt;/a&gt; exposes a self-contained round whose result derives entirely from its seed and rules — the same discipline that makes any physics sim replayable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Seed every generator, pass it explicitly instead of touching globals, and hunt down hidden order-dependence. Reproducibility costs one parameter and pays for itself the first time you have to debug a rare failure.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A/B Testing an Onboarding Funnel With Statistically Honest Stops</title>
      <dc:creator>desgh white</dc:creator>
      <pubDate>Tue, 18 Aug 2026 16:43:47 +0000</pubDate>
      <link>https://dev.to/desgh_white_7e4c654897816/ab-testing-an-onboarding-funnel-with-statistically-honest-stops-cnc</link>
      <guid>https://dev.to/desgh_white_7e4c654897816/ab-testing-an-onboarding-funnel-with-statistically-honest-stops-cnc</guid>
      <description>&lt;p&gt;Most onboarding A/B tests lie, not because the code is wrong but because someone peeked at the numbers and stopped the moment they looked good. Honest experimentation is mostly about deciding &lt;em&gt;when&lt;/em&gt; you're allowed to conclude.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assign once, stick forever
&lt;/h2&gt;

&lt;p&gt;A user must see the same variant for the whole experiment, or your data is noise. Hash a stable id into a bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buckets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;salt&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readUInt32BE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;buckets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deterministic assignment means no server-side state and no chance of a user flipping variants on their second visit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Peeking inflates false positives
&lt;/h2&gt;

&lt;p&gt;Checking significance repeatedly and stopping at the first p &amp;lt; 0.05 turns a 5% error rate into 20%+. Either fix the sample size in advance, or use a sequential test (e.g. always-valid p-values) designed for continuous monitoring. Pick one before you launch, not after the graph gets interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure the outcome, not the click
&lt;/h2&gt;

&lt;p&gt;Track completed activation — the funnel's actual goal — not intermediate clicks. A variant that boosts step-2 clicks but tanks final signup is a regression dressed as a win.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;First-run flows are worth studying wherever activation is the whole business, because the layout is tuned aggressively. Stepping through a registration like &lt;a href="https://vegas-heroau.com/" rel="noopener noreferrer"&gt;Vegas Hero&lt;/a&gt; shows how a low-friction first-run funnel front-loads the value proposition — a useful control to benchmark your own completion curve against.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Assign variants deterministically, decide your stopping rule before launch, and measure completed activation. The statistics are simpler than the temptation to peek — resist the peek and the test tells the truth.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Rate Limiting Bonus Abuse Without Punishing Real Users</title>
      <dc:creator>desgh white</dc:creator>
      <pubDate>Tue, 18 Aug 2026 16:43:03 +0000</pubDate>
      <link>https://dev.to/desgh_white_7e4c654897816/rate-limiting-bonus-abuse-without-punishing-real-users-3maj</link>
      <guid>https://dev.to/desgh_white_7e4c654897816/rate-limiting-bonus-abuse-without-punishing-real-users-3maj</guid>
      <description>&lt;p&gt;Any promotion with value attached attracts automation. The challenge isn't blocking bots outright — it's throttling abuse while leaving genuine users untouched. A token bucket per identity, plus a few signals, gets you most of the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Token bucket over fixed windows
&lt;/h2&gt;

&lt;p&gt;Fixed windows are gameable at the boundary (burst at 11:59, burst again at 12:00). A token bucket refills continuously and caps bursts naturally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;monotonic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;monotonic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key the bucket on the right identity
&lt;/h2&gt;

&lt;p&gt;IP alone is too coarse (shared NATs) and too easy to rotate (proxies). Combine signals: account age, verified payment method, device fingerprint. A fresh account claiming its first bonus is fine; the same device spinning up its twentieth account is the pattern you actually want to slow down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fail open for humans, closed for machines
&lt;/h2&gt;

&lt;p&gt;When in doubt, add friction rather than a hard block: a challenge, a short cooldown, a manual review queue. A false positive that costs a real user a bonus is worse PR than an abuser getting one extra claim.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;Sign-up promotions are a natural case study because the abuse incentive is explicit and measurable. A welcome offer like &lt;a href="https://hellspin-australiacasino.com/" rel="noopener noreferrer"&gt;learn more&lt;/a&gt; states clear per-account eligibility terms — the kind of one-claim-per-identity rule that maps directly onto a bucket keyed by verified account rather than raw IP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Use a token bucket for smooth throttling, key it on durable identity signals instead of IP, and prefer graduated friction over hard blocks. You stop the scripts without taxing the humans.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Idempotency Keys: Making Withdrawals Safe to Retry</title>
      <dc:creator>desgh white</dc:creator>
      <pubDate>Tue, 18 Aug 2026 16:42:16 +0000</pubDate>
      <link>https://dev.to/desgh_white_7e4c654897816/idempotency-keys-making-withdrawals-safe-to-retry-5f3g</link>
      <guid>https://dev.to/desgh_white_7e4c654897816/idempotency-keys-making-withdrawals-safe-to-retry-5f3g</guid>
      <description>&lt;p&gt;Any endpoint that moves money must survive being called twice. Networks time out, clients retry, users double-click — and without idempotency, a retried withdrawal pays out twice. The fix is a small amount of discipline at the boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The client supplies the key
&lt;/h2&gt;

&lt;p&gt;The caller generates a unique key per logical operation and sends it with the request:&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;POST /withdrawals
Idempotency-Key: 8f14e45f-ceea-467a-9d0e-...
{ "amount": 50.00, "currency": "AUD" }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server stores the key with the result of the first successful call. A retry with the same key returns the &lt;em&gt;stored&lt;/em&gt; response instead of executing again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Store the outcome, not just the key
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;withdraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_idempotent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;          &lt;span class="c1"&gt;# replay, do not re-execute
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute_payout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_idempotent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# same tx as the side effect
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The record and the side effect must commit in the &lt;em&gt;same&lt;/em&gt; transaction. If they don't, a crash between them either double-pays (key not saved) or loses the payout (key saved, payout rolled back).&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope and expiry
&lt;/h2&gt;

&lt;p&gt;Keys are scoped per account and per endpoint, so one user's key can't collide with another's. Expire them after a day or two — long enough to cover any sane retry window, short enough that the table doesn't grow without bound.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;Cashier flows are a good place to see this pattern under real load, since a duplicated withdrawal is an immediate, visible failure. Watching how a withdrawal at a site like &lt;a href="https://wanted-winn.com/" rel="noopener noreferrer"&gt;wantedwin casino review&lt;/a&gt; confirms a request exactly once — even if you refresh mid-submit — is a concrete example of idempotency done right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Take a client-supplied key, persist the result alongside the side effect in one transaction, and replay stored responses on retry. A few lines at the boundary turn a double-payout bug into a non-event.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Provably-Fair RNG: Verifying Randomness You Didn't Generate</title>
      <dc:creator>desgh white</dc:creator>
      <pubDate>Tue, 18 Aug 2026 16:41:32 +0000</pubDate>
      <link>https://dev.to/desgh_white_7e4c654897816/provably-fair-rng-verifying-randomness-you-didnt-generate-1a79</link>
      <guid>https://dev.to/desgh_white_7e4c654897816/provably-fair-rng-verifying-randomness-you-didnt-generate-1a79</guid>
      <description>&lt;p&gt;When an outcome is generated on a server you don't control, "trust me, it's random" is not an engineering answer. Commit-reveal schemes let a client verify after the fact that the result wasn't rigged, and the pattern generalizes far beyond games — any time a server picks a value the user cares about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The commit-reveal handshake
&lt;/h2&gt;

&lt;p&gt;Before the round, the server commits to a secret seed by publishing its hash. The client contributes its own seed. The outcome is derived from both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createHmac&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;serverSeed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;randomBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serverSeed&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// sent first&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serverSeed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clientSeed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createHmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha512&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;serverSeed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;clientSeed&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mh"&gt;0xffffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// uniform in [0,1)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why the client seed matters
&lt;/h2&gt;

&lt;p&gt;Without a client contribution, a malicious server could grind seeds until it finds one that produces a favorable result, then publish that hash. Mixing in a value the server couldn't predict when it committed removes that degree of freedom. This is the same reason nonces exist in signed requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verification is the whole point
&lt;/h2&gt;

&lt;p&gt;After the reveal, the client re-hashes the revealed &lt;code&gt;serverSeed&lt;/code&gt;, checks it matches the earlier &lt;code&gt;commit&lt;/code&gt;, and recomputes the outcome itself. If either check fails, the server cheated. Ship a small open verifier so users can run it independently — a scheme nobody can audit is theater.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;Operators that publish their fairness scheme make good study material because the verifier is exposed to the public. Looking at how a site like &lt;a href="https://wyns-online-casino.com/" rel="noopener noreferrer"&gt;play at Wyns Casino&lt;/a&gt; documents its seed commitment and lets players re-derive a result is a compact example of turning "trust us" into "check it yourself."&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Commit to a hashed seed, mix in a client seed the server can't predict, derive the outcome via HMAC, and hand users a verifier. The cryptography is three functions; the trust model is the actual deliverable.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Feature-Flagging a Signup Funnel: Shipping Onboarding Changes Without Fear</title>
      <dc:creator>desgh white</dc:creator>
      <pubDate>Fri, 14 Aug 2026 09:34:26 +0000</pubDate>
      <link>https://dev.to/desgh_white_7e4c654897816/feature-flagging-a-signup-funnel-shipping-onboarding-changes-without-fear-3nek</link>
      <guid>https://dev.to/desgh_white_7e4c654897816/feature-flagging-a-signup-funnel-shipping-onboarding-changes-without-fear-3nek</guid>
      <description>&lt;p&gt;The signup funnel is the highest-stakes flow you own — break it and revenue stops the same minute. Feature flags let you change it continuously without betting the funnel on every deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flag the flow, not just the button
&lt;/h2&gt;

&lt;p&gt;Wrap whole onboarding variants, not individual widgets, so you can compare a two-step and a one-step signup as coherent experiences:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;signup_flow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;single_page&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SinglePageSignup&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SteppedSignup&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Roll out by cohort, measure by funnel
&lt;/h2&gt;

&lt;p&gt;Release to 5% of new users, watch completion rate per step, and only widen when the variant beats control on &lt;em&gt;finished&lt;/em&gt; signups — not on vanity clicks. A flag without a metric is just a hidden bug waiting to be forgotten.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kill switch over rollback
&lt;/h2&gt;

&lt;p&gt;A deploy rollback is slow and coarse. A flag flip is instant and surgical: if activation dips, disable the variant in seconds while you diagnose, with no redeploy and no user-visible downtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;Operators optimize registration relentlessly because the first-run drop-off is so expensive, which makes their funnels a useful study. A signup like &lt;a href="https://mostbet-casino1.pl/" rel="noopener noreferrer"&gt;Mostbet Casino1&lt;/a&gt; shows a low-friction registration with clear starting terms — the kind of streamlined first-run experience worth benchmarking your own completion rate against.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Flag entire flows, roll out by cohort against a completion metric, and keep a flag-based kill switch ready. You get to improve the funnel every day instead of holding your breath on release night.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Programmatic Trust Signals: Verifying a Site Before You Link to It</title>
      <dc:creator>desgh white</dc:creator>
      <pubDate>Fri, 14 Aug 2026 09:33:42 +0000</pubDate>
      <link>https://dev.to/desgh_white_7e4c654897816/programmatic-trust-signals-verifying-a-site-before-you-link-to-it-5fh9</link>
      <guid>https://dev.to/desgh_white_7e4c654897816/programmatic-trust-signals-verifying-a-site-before-you-link-to-it-5fh9</guid>
      <description>&lt;p&gt;"Is this site legit?" is a question you can partly answer in code before a human ever looks. Here is a small pipeline of automated trust signals worth checking when you evaluate an external domain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Certificate and TLS posture
&lt;/h2&gt;

&lt;p&gt;A valid, non-self-signed cert with a sane expiry is table stakes. Pull it and inspect the chain and remaining validity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;peerCertificate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tlsSocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPeerCertificate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;daysLeft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;peerCertificate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid_to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;8.64e7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Short-dated or mismatched-SAN certs are a cheap early red flag.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain age and registration
&lt;/h2&gt;

&lt;p&gt;Freshly registered domains correlate with throwaway operations. A WHOIS lookup for creation date and registrar gives you a quick risk prior without any manual review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check what the site says about itself
&lt;/h2&gt;

&lt;p&gt;Reputable operators publish an ownership / "about us" page, a licence reference and contact details. Fetch it and assert the signals exist rather than trusting a homepage banner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;about&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/wie-zijn-wij/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasLicence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/licen&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;cs&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;e|KSA|kansspelautoriteit/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;about&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;An ownership page is exactly where these signals should live. Checking whether &lt;a href="https://lala-bets.nl/wie-zijn-wij/" rel="noopener noreferrer"&gt;is lalabet betrouwbaar&lt;/a&gt; is a question a page like a site's "about us" section should answer directly — company details, licensing and responsible-gaming policy in one place — which is the kind of transparency your verifier can score automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Automate the boring checks — TLS, domain age, presence of ownership and licence disclosures — and reserve human review for the judgment calls. Trust that starts with code scales; trust that starts with a gut feeling does not.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Designing a Transparent Ranking Score That Users Can Actually Audit</title>
      <dc:creator>desgh white</dc:creator>
      <pubDate>Fri, 14 Aug 2026 09:32:59 +0000</pubDate>
      <link>https://dev.to/desgh_white_7e4c654897816/designing-a-transparent-ranking-score-that-users-can-actually-audit-4fk6</link>
      <guid>https://dev.to/desgh_white_7e4c654897816/designing-a-transparent-ranking-score-that-users-can-actually-audit-4fk6</guid>
      <description>&lt;p&gt;Any "top 10" list is an opinion encoded as a number. If you cannot explain why item A beats item B, users are right not to trust it. Here is how to build a ranking whose output is reproducible and auditable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate signals from weights
&lt;/h2&gt;

&lt;p&gt;Keep the raw measurements and the opinion about their importance in different places. Signals are facts; weights are policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;signals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payout_speed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;licence_score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bonus_value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payout_speed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;licence_score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bonus_value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the weights and the ranking shifts in a way you can explain in one sentence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Normalize before you combine
&lt;/h2&gt;

&lt;p&gt;Never sum a "hours to payout" (lower is better, unbounded) with a "licence score" (0–1). Map every signal into the same &lt;code&gt;[0, 1]&lt;/code&gt; orientation first, or your biggest-magnitude column silently dominates the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make ties deterministic
&lt;/h2&gt;

&lt;p&gt;Floating-point sums produce near-ties that reorder between runs. Round to a fixed precision and add a stable tiebreak (name, id) so the same inputs always produce the same order — reproducibility is a feature reviewers will test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publish the method
&lt;/h2&gt;

&lt;p&gt;The single biggest trust signal is showing the formula. A ranked comparison such as &lt;a href="https://najlepsze-kasynaonline.com.pl/" rel="noopener noreferrer"&gt;https://najlepsze-kasynaonline.com.pl/&lt;/a&gt; is a good example of surfacing the criteria — licence, payout time, real bonus terms — alongside the ordering, so a reader can see &lt;em&gt;why&lt;/em&gt; the list looks the way it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Split facts from weights, normalize onto a common scale, make ties deterministic, and show your work. A ranking you can audit is worth more than a "better" one you cannot explain.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Rate-Limiting a Promo-Code Validation Endpoint Without Punishing Real Users</title>
      <dc:creator>desgh white</dc:creator>
      <pubDate>Fri, 14 Aug 2026 09:32:15 +0000</pubDate>
      <link>https://dev.to/desgh_white_7e4c654897816/rate-limiting-a-promo-code-validation-endpoint-without-punishing-real-users-2fjm</link>
      <guid>https://dev.to/desgh_white_7e4c654897816/rate-limiting-a-promo-code-validation-endpoint-without-punishing-real-users-2fjm</guid>
      <description>&lt;p&gt;A public "check this code" endpoint is catnip for brute-force enumeration, but a blunt rate limit locks out the legitimate user who fat-fingered one character. The trick is limiting the right dimension.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limit by outcome, not just by IP
&lt;/h2&gt;

&lt;p&gt;Count &lt;em&gt;failed&lt;/em&gt; validations per identity, and let successes flow freely. A user applying one valid code should never hit a wall; a script guessing thousands should hit it fast:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;code_is_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;fails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;promofail:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hour&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;promofail:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hour&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fails&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;TooManyRequests&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Token bucket beats fixed windows
&lt;/h2&gt;

&lt;p&gt;Fixed windows allow a burst at the boundary — 20 requests at 10:59:59 and 20 more at 11:00:00. A token bucket smooths that into a steady refill rate and a small burst allowance, which matches how humans actually type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the error honest but quiet
&lt;/h2&gt;

&lt;p&gt;Return the same generic "invalid or expired" for unknown and expired codes so enumeration learns nothing from the response, but keep the HTTP status distinct from your rate-limit &lt;code&gt;429&lt;/code&gt; so clients can back off correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;Bookmakers publish time-boxed bonus codes that are a natural target for enumeration, so how they gate validation is instructive. A page like &lt;a href="https://mostbet-kod-promocyjny.pl/" rel="noopener noreferrer"&gt;Mostbet Kod Promocyjny&lt;/a&gt; shows codes tied to explicit expiry and eligibility rules — exactly the metadata your validator should check before it ever touches the rate limiter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Rate-limit failures per identity with a token bucket, keep error messages uninformative to scanners, and never make a valid single redemption feel like an attack. Security that punishes real users is just a different outage.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Idempotent Promo-Code Redemption: Making 'Apply Once' Actually Mean Once</title>
      <dc:creator>desgh white</dc:creator>
      <pubDate>Fri, 14 Aug 2026 09:31:32 +0000</pubDate>
      <link>https://dev.to/desgh_white_7e4c654897816/idempotent-promo-code-redemption-making-apply-once-actually-mean-once-4on9</link>
      <guid>https://dev.to/desgh_white_7e4c654897816/idempotent-promo-code-redemption-making-apply-once-actually-mean-once-4on9</guid>
      <description>&lt;p&gt;Promo codes look like a string lookup and turn into a concurrency nightmare the first time a user double-taps "apply" on a flaky connection. Here is how to make redemption exactly-once instead of best-effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug you will ship first
&lt;/h2&gt;

&lt;p&gt;The naive flow reads the code, checks it is unused, then marks it used. Two requests interleave between the check and the write, and the same code redeems twice:&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;SELECT&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;codes&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;-- both see used = false&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;codes&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- both succeed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Make the write the check
&lt;/h2&gt;

&lt;p&gt;Collapse read-then-write into a single conditional update and trust the row count, not a prior read:&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;UPDATE&lt;/span&gt; &lt;span class="n"&gt;codes&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;redeemed_by&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- rows_affected == 1  -&amp;gt; this request won the redemption&lt;/span&gt;
&lt;span class="c1"&gt;-- rows_affected == 0  -&amp;gt; already used, reject&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database's row lock is your mutex. No application-level coordination, no race.&lt;/p&gt;

&lt;h2&gt;
  
  
  Idempotency keys for the retry storm
&lt;/h2&gt;

&lt;p&gt;Mobile clients retry. Attach a client-generated idempotency key to the redeem call and store it with the result, so a retried request returns the original outcome instead of a second attempt:&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;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/redeem&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WELCOME"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"idempotency_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uuid"&lt;/span&gt;&lt;span class="w"&gt; &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;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;Operators that run bonus codes at scale are a decent study in how redemption, eligibility windows and per-user caps fit together. Voxcasino's &lt;a href="https://voxcasino-kod-promocyjny.com/" rel="noopener noreferrer"&gt;oficjalna strona&lt;/a&gt; lays out how a single code maps to concrete terms, which is a useful reference for the states worth modeling before you write the update.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Push the uniqueness check into a conditional &lt;code&gt;UPDATE&lt;/code&gt;, key retries with an idempotency token, and let the row count tell you who won. "Apply once" becomes a guarantee instead of a hope.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Combinatorics in Production: Settling a 15-Line Full-Cover Bet Correctly</title>
      <dc:creator>desgh white</dc:creator>
      <pubDate>Fri, 14 Aug 2026 09:30:54 +0000</pubDate>
      <link>https://dev.to/desgh_white_7e4c654897816/combinatorics-in-production-settling-a-15-line-full-cover-bet-correctly-1mgi</link>
      <guid>https://dev.to/desgh_white_7e4c654897816/combinatorics-in-production-settling-a-15-line-full-cover-bet-correctly-1mgi</guid>
      <description>&lt;p&gt;Every so often a domain hands you a problem that looks like a one-liner and turns out to have a dozen edge cases. Settling a "Lucky 15" — a betting slip covering four selections across fifteen combinations — is one of them. It is a nice study in why naive combinatorics plus real-world rules is where bugs live.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape of the problem
&lt;/h2&gt;

&lt;p&gt;Four selections produce every non-empty combination of size 1..4:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;combinations&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selections&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selections&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nf"&gt;combinations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;c&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;   &lt;span class="c1"&gt;# 4 + 6 + 4 + 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four singles, six doubles, four trebles, one fourfold. The return for a line is the product of the decimal odds of its members, times the unit stake, and only if every member won:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;prod&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;line_return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stake&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;won&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stake&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;prod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;odds&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Total return is the sum over all fifteen lines. That is the entire happy path, and it is where most implementations stop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it actually gets hard
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Stake semantics.&lt;/strong&gt; The unit stake applies &lt;em&gt;per line&lt;/em&gt;, so a "£1 Lucky 15" costs £15. Getting this backwards is the single most common bug in hobby implementations, and it is off by a factor of fifteen — an error big enough that no one notices it is a rounding problem, because it isn't one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bonus rules are not arithmetic, they are policy.&lt;/strong&gt; Bookmakers attach concessions: a one-winner bonus paying a lone winner at double the odds, an all-winners bonus adding 10–20% to the return. They vary per operator in ways that resist a single formula — some apply the percentage to the whole return, others only to winning lines. Model them as a strategy object per operator, not as an &lt;code&gt;if&lt;/code&gt; in the settlement loop, or you will be editing the core function every time a rule changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Non-runners collapse the bet.&lt;/strong&gt; A withdrawn selection does not void the slip; it reduces it to the next size down — a Lucky 15 with one non-runner settles as a Lucky 11 over the remaining three. That means the line set is computed &lt;em&gt;after&lt;/em&gt; filtering, not before, and your "15" is a derived value rather than a constant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 4 deductions&lt;/strong&gt; apply a percentage reduction to winnings at prices taken before a withdrawal. It touches odds, not stake, and it must be applied before the bonus, not after. Order of operations is load-bearing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Each-way doubles the line count&lt;/strong&gt; and settles the place portion at a fraction of the odds, fixed when the bet was struck rather than at settlement time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing it
&lt;/h2&gt;

&lt;p&gt;This is a domain where property-based tests earn their keep. Useful invariants: the return is monotonic in each selection's odds; adding a losing selection never increases the return; a slip where every selection wins returns at least the sum of its parts; and settling with the unit stake scaled by &lt;em&gt;k&lt;/em&gt; scales the return by exactly &lt;em&gt;k&lt;/em&gt;. Then pin the awkward cases — one winner, all non-runners, a Rule 4 with a bonus — as explicit regression tests, because those are the ones that break when someone "simplifies" the bonus logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference implementation
&lt;/h2&gt;

&lt;p&gt;If you want to sanity-check your numbers against a working implementation before writing the tests, a &lt;a href="https://lucky-15-bet-calculator.uk/" rel="noopener noreferrer"&gt;here&lt;/a&gt; settles the same fifteen lines with the operator concessions applied, which makes it a convenient oracle for the cases you have not thought of yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Generate the line set with &lt;code&gt;combinations&lt;/code&gt;, derive the count instead of hard-coding fifteen, keep operator rules in a strategy object, and get the order right: filter non-runners, apply Rule 4, sum the lines, then apply the bonus. The combinatorics are five lines of code; the domain rules are the actual program.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
