Tuist week of July 20: retry knobs that finally match their defaults
A single Tuist drop surfaces the HTTP retry policy behind cache downloads, fixing a backoff that started near one millisecond and exposing retry count and base delay as environment variables so self-hosted operators can tune timeout and retries independently.
It was a quiet week on the changelog, with a single drop from Tuist, but the change it documents is a small act of honesty about reliability defaults. For a long time, the same retry policy was quietly applied to every cache request: three retries on top of the initial attempt, with a base backoff that was, in practice, closer to a millisecond than the hundred milliseconds the documentation implied. Operators who wanted their slow downloads to finish had only one dial, the timeout, and turning it up risked chaining several long waits together when a request did fail.
The new drop (Tuist) opens that policy up. Two environment variables now sit in front of the shared HTTP retry logic. TUIST_HTTP_MAXIMUM_RETRY_COUNT controls how many retries follow the first attempt, defaulting to three, accepting zero to disable retries entirely, and capped at ten. TUIST_HTTP_RETRY_BASE_DELAY_IN_MILLISECONDS sets the initial exponential backoff, with a default that now actually starts at 100 milliseconds and a ceiling of 30 seconds per individual retry. Each retry still doubles the previous delay and adds up to one base delay of jitter, so clients recovering from a shared network event drift out of lockstep instead of colliding on the same retry tick.
The interesting thing is not any single number. It is the shape of the tradeoff the team is now willing to name out loud. A longer resource timeout is only useful if the underlying transfer is actually progressing, and a hard retry count is only useful if the backoff between attempts is real. Until now, those two ideas were coupled in a way that mostly favored impatience: a fast retry loop that masked transient slowness, and a timeout knob whose main effect was to make each of those retries longer. Splitting them gives self-hosted operators the room to be deliberate. They can keep the default three attempts and let the timeout do its job, or pair a generous timeout with 0 retries to fail fast and rebuild locally. They can also widen the base delay during known congestion windows without touching the retry count, so a flapping network does not turn into a stampede against an already tired upstream.
There is a quiet bug fix sitting underneath the new defaults as well. The base delay had been off by roughly two orders of magnitude, which means any operator who tuned retry counts in the past was tuning against a near-instant backoff rather than the documented behavior. Correcting the floor matters more than the ceiling does, because every doubling and every jitter band is anchored to it. With 100 milliseconds as the starting point, the 30-second cap starts to feel like a real bound instead of a theoretical one.
I read the drop as a small expression of a product belief: infrastructure knobs belong to the people running the infrastructure, and reliability defaults should be tunable enough to be wrong on purpose. A team running Tuist inside a constrained CI environment is not the same as a team running it against a flaky home link, and the retry policy should be allowed to reflect that. The change does not promise new throughput or new guarantees. It promises that the existing knobs mean what they say, and that the undocumented ones are now documented.
The direction this hints at, without claiming it explicitly, is that more of the runtime’s hidden assumptions are likely to keep surfacing as configurable surfaces. When a single failure mode, like a stalled cache download, can be tuned along two axes instead of one, the next stall usually gets the same treatment. For now, what shipped is enough: the timeout knob has a sensible companion, and the numbers behind the retry loop finally match the comment.
Narrated from 1 public drop.