Build1 distinct publisher3 min readUpdated
A dev.to implementation note ports one token-bucket retry budget into Python, Go, and JavaScript. The Python trap is a missing lock; the Go trap is the select.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
An implementation note published on dev.to, "Retry budgets by language: Python, Go, and JavaScript," takes a claim that most teams nod along to and then ignore in code: a per-call cap bounds a call, but never a run or a fleet of workers [1]. The author's framing is that your retry multiplier is set by how you recover, not by how often you fail, and that the theory does not tell you where in a Python decorator, a Go for loop, or a JavaScript promise chain the budget actually lives [1][2].
The definition is narrow enough to be useful. A retry budget is a bucket shared across everything that might retry, refilled slowly, that caps retries as a fraction of throughput rather than as an absolute count per call [3]. Two properties have to survive translation into any runtime: the budget is shared state, because if every call site owns its own counter you do not have a budget, you have N caps that multiply [4]; and a denied retry is a real outcome, meaning that when the bucket is empty the call fails now, deliberately, rather than waiting for a slot [5]. The shared shape is a token bucket with capacity C and refill R tokens per second, a small deposit on each success and a withdrawal of one token per retry, or a denial if the bucket is empty [6]. Retries are funded by the work that is succeeding, so when failures spike and successes stop, the bucket drains and retries stop with it [7].
Python's problem is ergonomics. The reflexive tenacity decorator, stop_after_attempt(4) with exponential wait, gives every wrapped function its own independent four attempts, so five call sites produce a fleet ceiling of 5x4 with nothing coordinating them [8] - twenty attempts where the operator believes there are four [1]. The post's fix is to make the budget an object the retry predicate consults rather than a constant baked into the decorator, with a worked example at capacity 40 and refill 1.0 tokens per second and a 0.1-token success bonus [9]. On those numbers an empty bucket takes 40 seconds to refill [2] and it takes ten successful calls to fund a single retry [3]. The language-specific trap, per the author, is forgetting the threading.Lock: under a thread pool the read-modify-write on the token count races, and a budget of 40 quietly admits many more retries under exactly the load spike it was built for [10]. Under asyncio the lock and the sleep change type but the shape does not [11].
Go tempts the other way. The retry loop is so explicit that people reinvent it per package with subtly different backoff [12]. The idiom the author endorses is to pass the shared budget alongside context.Context, letting the context own the deadline and the budget own the count, with the same sync.Mutex guarding the bucket [13][14]. The Go trap is named as the select between the backoff timer and ctx.Done(), where cancellation is meant to beat the retry [15][16].
What to watch: the source material supplied here cuts off mid-sentence in the Go section, so the JavaScript promise-chain trap named in the headline is not in the excerpt [17]. If you are porting this, the check worth running first is whether your budget object is one instance or N, and whether the read-modify-write is guarded.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
The post argues the retry-budget argument is language-independent: a per-call cap bounds a call but never a run or a fleet of workers.
The post states that your retry multiplier is set by how you recover, not by how often you fail, and that theory does not tell you where in a Python decorator, a Go for loop, or a JavaScript promise chain the budget actually lives; each language makes a different part of it easy to get wrong.
In the Go sample the backoff wait is a select over time.After(backoff(attempt)) and ctx.Done(), returning ctx.Err() with the comment that deadline or cancel beats the retry.
The post names the Go-specific trap as the select statement.
A retry budget is defined as a bucket shared across everything that might retry, refilled slowly, that caps retries as a fraction of throughput rather than as an absolute count per call.
Property one: the budget is shared state. If every call site owns its own counter, you do not have a budget, you have N caps that multiply.
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.
Self-consistent code walkthrough, single source, no measurement
Every claim is verifiable against the one supplied post, and the mechanisms are shown as concrete, internally coherent code (lock-guarded token bucket in Python, sync.Mutex plus select on ctx.Done in Go). But the evidence stops at illustration: no benchmark, load test, incident timeline or third-party corroboration is supplied, the '$200 nested caps' anchor is unquantified in the text, and one ledger claim about the excerpt's contents is contradicted by the body itself.
No adoption evidence supplied
The supplied source is a pattern write-up. It names no team, service, release, deployment, or usage disclosure that runs a shared retry budget, and reports no downloads, incidents or production results. Library names appear only as illustration (tenacity, asyncio, context.Context), which is not evidence of adoption of the pattern, so adoption is left unmeasured rather than inferred.
Mildly overstated: strong prescriptions, no measurement behind them
Claims are mechanism-level and modest in scope - no product, no performance record, no promise of scale - which keeps the gap small. It is positive rather than zero because the post asserts prescriptive outcomes ('a budget of 40 quietly lets through many more retries', workers 'burning budget') and rests on an unquantified prior cost anecdote, while supplying no measurement and no evidence of anyone running it in production.
Audience-building for the author's own publication, no vendor stake
The post opens by attributing itself to the author's 'Loop & Retry' notes on building LLM agents that survive production, so there is a clear self-promotion and audience-growth incentive on a developer platform. Offsetting that, no product, service, paid tier or sponsor is being sold in the supplied text, and third-party libraries are cited neutrally, including a caveat that tenacity 'is good' while its default shape misleads.
Claims well grounded in one text, nothing corroborated outside it
Confidence in what the post says is high: the code and the named traps are quoted directly. Confidence in the cluster as intelligence is limited by a single publisher, zero adoption evidence, no measurement, and one contested ledger claim about the excerpt's own contents, so the assessment can describe the argument reliably but cannot confirm its real-world effect.
build
The third answer: a dead-code tool allowed to say "not traced yet"1 distinct publisher
build
Fuse ranks, not scores: a retrieval contract that refuses to guess in code review1 distinct publisher
build
Force the tool call, then hand Lightsail a long-lived key1 distinct publisher
build
Allow-list the closed set, block-list the open one: 193 thin geo pages, one gate1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 15, 2026