Build1 publisher3 min readPublished
Two red flags on one call path mean the free-tier migration is already late
A dev.to field guide inverts the adoption post, listing the conditions that disqualify a workload from free model capacity and shipping a standard-library harness whose exit code fails the build on breach.
The Engineer · Build desk

What happened
- A dev.to field guide argues that the usual free-tier failure is a quiet one: a dependency on capacity nobody promised, around a code path the team has started treating as production.
- Four disqualifying conditions follow, covering stored output, self-retrying callers, prompts carrying secrets and a human latency promise, with two on the same call path meaning the migration is already late.
- The post ships tier_probe.py, a standard-library harness that runs without a network key in dry-run mode, checks recorded metrics against a policy file, and exits 1 when a criterion is breached.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- exposure A caller that retries on its own multiplies its consumption against a ceiling it did not set, and it does so hardest during the traffic spikes when shared free capacity is least likely to answer.
- decision Recording the metrics from the first call turns the migration trigger into a policy file and an exit status that CI evaluates, instead of an argument held after the first incident.
- capability Because dry-run mode needs no credentials, the gate can be wired into a pipeline before a provider is chosen, though the numbers it prints stay synthetic until the real client is plugged in.
Start with the generator, because in --dry-run mode every figure the harness prints comes out of it. Each attempt rolls a uniform random number, the status is ok when the roll is above 0.18, and otherwise it is one of rate_limited, timeout or error; the loop breaks on success or at the third attempt [12]. A call fails outright only when three rolls in a row land under 0.18. That is 0.18 cubed, or 0.6% [18]. Expected attempts per call is 1 + 0.18 + 0.0324 = 1.21 [19]. Token counts are drawn independently of which attempt they belong to [13], so the expected retry_token_share is just the share of attempts that are retries: 0.2124 / 1.2124, about 17.5% [20]. Over the 40 calls in the sample invocation, calls_never_succeeded has an expectation of 0.23, so most runs print a zero there [21]. Median ok latency lands on 900 ms because 900 is what the code says [22].
Those numbers describe a seeded random number generator. For the 17.5% to transfer to a provider, refusals would have to arrive independently of each other and of load. The guide's own reasoning argues against that: free capacity is most fragile when traffic is spiky, which is when automatic retries fire hardest [7]. Correlated refusals push both the retry share and the never-succeeded count above what independent draws predict. The post is explicit about the limit: "No performance figures for any provider are claimed here; the dry-run data is deliberately synthetic." [15]
The gate is the exit status. Run the script against a policy file and a return code of 1 means at least one exit criterion was breached [11]. Dry-run mode needs no network key [11], so the check can sit in a pipeline before anyone has credentials. The published excerpt breaks off inside measure() and omits policy.json, so the thresholds that fire the gate are left to the reader [23]. Swapping the synthetic generator for a thin client around the real provider call is the documented next step [24].
Before any of the measurements, the guide applies a test of accountability. It says: "A component is load-bearing when someone can be held to its behavior", and the examples are a pager, a stored record, an invoice, an audit line [4]. Anything in that category needs an owner, a version and a stated limit [5]. The four disqualifying conditions follow from it. A response that lands in a database, an object store, a customer inbox or a compliance archive has put the producing tier into a provenance chain, and free tiers generally do not commit to traceability, so the same prompt replayed next month may not return the same text [6]. Prompts assembled from internal documents, stack traces or customer records cross a boundary the operator does not control, which the guide calls a legal and architectural problem, not a throughput one [8]. Shared capacity gives a good median and a jittery tail; the demo shows the median, and the user gets the tail [9].
One flag is survivable with eyes open, and two flags on the same call path is the point at which the migration is already late [10]. A response gives the caller no sign of any of it: the model version can move underneath it, the capacity is shared, and the tokens come from a pool with no contract behind them [3]. The guide's instruction is to record the numbers from the first call [25].
What to watch
- The thin provider client the post leaves as an exercise: until it lands, every failure rate and retry share the harness prints comes from random.Random(7).
- A published policy.json would show which of the six recorded metrics is meant to fail a build, and at what threshold.
- Free-tier terms that commit to model version pinning or response traceability would take the provenance condition off the list.