Build1 publisher3 min readPublished
Greedy sampling picks the top token every time, but the arithmetic that decides which token is on top depends on the shape of the batch you were pooled into, and you do not pick your batch.
The Engineer · Build desk

Follow any of these and your For You feed starts watching them — no settings page required.
Compiled by The EngineerSomething wrong?How this is made
Twelve divergences in 500 calls is a 2.4 percent rate [1]. Inverted, that is one divergence every 42 identical calls [2]. A test that makes one such call per run and diffs the whole output string will go red at roughly that cadence, which is the difference between a test that looks broken and a test that looks unlucky.
That rate is one measurement, on one endpoint, with one prompt, reported in a dev.to writeup [2]. Three conditions have to hold before it transfers to your suite. Your prompt has to land on a position where the top-2 logit gap is narrower than last-bit noise [19]. Your provider has to be batching you against co-tenant load that varies through the day [11]. And your assertion has to be sensitive to the byte [1]. Most positions fail the first test: after "the capital of France is", the gap to the runner-up is far too wide for a 1e-7 perturbation to close [18]. A pipeline whose outputs are mostly locked continuations can loop 500 times and see nothing.
The movement is upstream of the sampler. argmax over identical floats returns the same index every run [9]. But floating point addition is not associative, and the reductions in a forward pass (hidden-dimension sums, softmax denominators, RMSNorm) are split across GPU blocks and recombined in an order set by the tensor shape the kernel was handed [5][10]. Shape follows batch. The same request beside 48 others at 3pm and beside 3 at 4am gets a different tiling, a different split-K, and different last-bit rounding on its logits [11]. Greedy decoding stays deterministic throughout; what changes is the logits it is deterministic over, since those are no longer identical run to run [4]. Each run is fully deterministic given its batch, but the batch itself is set by the server and hidden from the caller [20]. The missing property has a name: batch invariance, a property most production inference kernels lack [6].
Batch size is not the only lever. Mixture-of-experts routing under capacity limits ties your token's expert assignment to whichever other tokens competed for that expert in the same batch [13]. Prefix caching moves the compute boundary depending on whether your prompt hit a cached KV prefix, which regroups the reductions after it [14]. Speculative decoding is built to be output-equivalent, but verification runs a different numeric path and the equivalence holds only to those same last bits [15]. Two GPU generations behind one endpoint means two kernel choices for identical input [16]. Fixing batch invariance alone would leave the cache and the verifier in the numeric path.
The remedy in the post is to assert on parsed fields and invariants, pin model snapshots, and keep a flake budget, on the stated ground that bitwise reproducibility exists only on your own hardware at batch size 1 [8]. Be clear about what that buys. Parsed-field assertions stop you failing on whitespace and reworded prose. They do not stop a refund ticket coming back billing instead of fraud [3], because the label is the parsed field, and the near-tie between fraud and billing is exactly where the classification lives [19]. Autoregressive decoding then carries the swap forward, since one changed token is the input to every token after it [7].
The flake belongs in the budget. The label flip does not, and it needs handling outside the model: if a single greedy call decides a refund, the tie-break should sit apart from the model, as a threshold on the parsed confidence, a second pass, or an explicit escalation path for the ambiguous class.
Ranked by verification strength, evidence, and original report placement.
temperature=0 makes sampling greedy (always pick the top token) but does not guarantee identical logits between runs, so the top token can change.
Logits shift because floating point addition is not associative and GPU kernels change their reduction order based on batch shape, which depends on who else is hitting the server at that millisecond.
Batch invariance means a single request produces the same output regardless of what else is batched alongside it; most production inference kernels do not have this property.
Because decoding is autoregressive, one flipped token near a near-tie changes the input to every subsequent token, so a 1e-6 numeric wobble can rewrite an entire paragraph.
The recommended fix is to assert on parsed fields and invariants, pin model snapshots and keep a flake budget, because bitwise reproducibility only exists on your own hardware at batch size 1.
temperature=0 collapses the sampler to argmax, and argmax over the same floats returns the same index every time.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Sound arithmetic, unshown experiment
Two halves of very different strength sit in the same post. The numerics half — non-associative addition, block-level reductions whose split follows tensor shape, argmax over logits that moved in the last bits — is internally consistent and checkable by anyone who reads kernel code. The empirical half is a count reported without harness, provider, model snapshot or raw diffs, and dev.to is the only place it appears in our coverage.
One team's CI is the whole sample
Nothing here shows how widely this behaviour is seen or how widely the recommended test discipline has spread. We have a single unnamed team's pipeline, no provider confirmation that logits depend on batch composition, and no second engineering group publishing a flip rate that the 2.4 percent could be compared against.
Title promises measurement, body delivers reasoning
The reasoning is the sturdier product. 'Temperature 0 isn't deterministic' is a fair description of greedy sampling on top of batched serving, and the piece is careful to say nothing is being randomised. The overreach is narrower: a precise count offered on trust, and a generalisation to 'the major APIs' that names none of them.
Nothing on sale
No product, vendor or tool is pitched anywhere in the supplied text, and the remedies are unglamorous test hygiene the author says his own team adopted. The pressure that does exist is the developer-platform kind, where a crisp count travels further than a caveat — a reason to want the raw diffs rather than to doubt the arithmetic.
Mechanism firm, numbers soft
We would defend the causal account without hesitation and the specific 2.4 percent not at all. A reader can act on the mechanism today — assert on parsed fields, pin dated snapshots, measure disagreement — while treating the rate as this author's endpoint on this author's traffic until someone else publishes theirs.
build
The money bug that survives your migration to decimals1 publisher
build
The sparse-model bill arrives at serving time, and it is paid in collectives1 publisher
build
Three manual interventions in a month, and every guard was working as designed1 publisher
build
Six MariaDB versions, one real difference: the only reason to leave 10.6 is the July 2026 clock1 publisher
Publishers with included, body-backed reporting in this cluster.
1 article · September 7, 2026