The Drift Log · Model independence and repeatable builds
Eliminate External Service Calls to Ensure Repeatable Builds
12 October 2026 · 3 min read · 698 words · established

Replacing live HTTP calls with version‑controlled fixtures eliminates network‑induced flakiness and restores build repeatability.
The hidden cost of live‑service calls
A CI job that hits an external API can pass one day and fail the next. The failure is rarely a code regression. It is network latency, rate‑limit throttling, or a silent schema change in the provider. Those factors are invisible to the source tree, yet they alter the build output. When the same commit produces different artefacts on two runners, the build is no longer repeatable. The root cause is an uncontrolled dependency on a live service.
How outbound HTTP breaks deterministic builds
Every outbound request introduces a source of nondeterminism. The order in which TCP packets arrive is not guaranteed. DNS resolution may return different IPs. The provider can inject A/B test headers, change default pagination, or retire an endpoint altogether. Because the request happens at runtime, the build artefact incorporates whatever response the service chose to emit. The result is a flaky test suite and a loss of confidence that a given commit will always produce the same binary, the same checksum, the same published archive.
The problem is not that the code is wrong. It is that the build environment does not own the data it consumes. Model‑independence, as described in the why‑model‑independence‑is‑an‑engineering‑posture article, teaches us to own every piece of logic that influences the artefact. External services are the opposite of owned logic.
Deterministic fakes and recorded fixtures
The remedy is to replace the live call with a deterministic fake during the build. A fake implements the same interface as the real HTTP client but returns a pre‑recorded response. The response is stored as a fixture file that lives alongside the test code. When the test runs, the fixture is read and returned verbatim. No network is touched, no DNS lookup occurs, and the same byte sequence is produced every time.
A minimal example in TypeScript shows the pattern:
// real client
export async function fetchUser(id: string): Promise<User> {
const resp = await fetch(`https://api.example.com/users/${id}`);
return resp.json();
}
// fake client used in CI
export async function fetchUserFake(_: string): Promise<User> {
// fixture generated once from a known good response
const fixture = readFileSync('test/fixtures/user-123.json', 'utf8');
return JSON.parse(fixture);
}
The fixture file is generated once, preferably on a known good environment, and checked into version control. The generation step is a one‑off operation; after that the fake is the only code path exercised in CI. Because the fixture is a static file, the build becomes repeatable: the same input yields the same output, regardless of network conditions.
When the real service evolves, the fixture must be refreshed. That refresh is a deliberate act, not an accidental side‑effect of a flaky test. The refresh can be gated behind a certification harness that executes the real call, records the response, and validates it against a contract. The harness reports CERTIFIED if the contract holds, PROVISIONAL if the response is reproducible but not yet verified, or FAILED if the contract is broken. Using the harness keeps the fake in sync with the live service without re‑introducing nondeterminism into the build.
Wiring the fake into the CI pipeline
Replace the production client with the fake at the module boundary. In a Node.js project, the entry point can resolve a different implementation based on an environment variable that CI sets:
const client = process.env.CI ? fetchUserFake : fetchUser;
export default client;
The CI configuration sets CI=true for all test jobs. Because the decision happens before any request is made, no accidental leakage to the network can occur. The same pattern works for any language that supports dependency injection or module aliasing.
The fixture files belong in the repository, not in a cache. Their presence guarantees that a fresh runner, with no prior state, can reproduce the build. The checksum of the fixture file contributes to the overall archive checksum, reinforcing the repeatable‑build guarantee described in the SHPBL methodology.
Monday’s concrete step
- Identify the first external HTTP call that appears in a flaky test.
- Record a response from a known good environment and store it as a JSON fixture next to the test.
- Add a deterministic fake that returns the fixture, and gate the selection with an environment variable.
- Run the full CI suite locally with the variable set to confirm that the test no longer depends on the network.
Completing these four actions on Monday gives you a repeatable build for that service call. Extend the pattern to other outbound calls as you encounter them, and the overall CI pipeline will shed its flaky behaviour without sacrificing coverage.
Keep reading
Next in the log
- Cross‑Language Deterministic Build Strategies for Monorepos
Use lock‑files, hermetic timestamps, and SHPBL's certification harness to make cross‑language monorepo builds reproducible.
- Deterministic Network Mocks to Eliminate Flaky Integration Tests
Deterministic network mocks turn flaky integration tests into reproducible CI runs.
- Sealed Checksums for Deterministic Artifact Reproduction
Sealed checksums let you verify that a rebuilt artifact matches the original byte‑for‑byte.
The Strategic Master Library · written and reviewed under the house's own epistemic rules: nothing claimed that we cannot show.