‘It Works on My Machine’: The Ladder of Numerical Dependencies

Why numerical code that works on your laptop doesn’t work anywhere else

Reproducibility
Scientific Computing
DevOps
Tooling
Author

Ravi Kalia

Published

July 27, 2026

“It Works on My Machine”: The Ladder of Numerical Dependencies

This post lays out the ladder of dependencies underneath numerical code — architecture, driver, runtime, packages — and why pinning one rung leaves every other rung free to move.

Two machines can run the same source and still run different programs. The difference is usually below the application code, on a ladder of dependencies. A fix at one rung does not pin the others.

1 Ladder

Rungs, from the bottom:

  • Chipset and instruction set — x86_64, arm64; CUDA, ROCm, Metal, or CPU-only.
  • Operating system — distro, glibc, kernel, system compilers.
  • Language runtime — e.g. CPython 3.11 vs 3.13, R 4.3 vs 4.4.
  • Packages — direct and transitive (NumPy, dplyr, PyTorch, …).
  • Application code — the only rung git tracks by default.

Example: numpy==2.1.3 still leaves the BLAS unspecified. A wheel may link MKL; a source build may link OpenBLAS. Same pin, different arithmetic.

A container pins OS and libc, not the host GPU driver.

2 Production engineering

Typical strategy: ship the machine with the code.

  • Container image: OS, libc, system libraries, runtime as one artifact.
  • Lockfiles: uv.lock, package-lock.json, Cargo.lock, go.sum — resolved versions and hashes; install is a replay, not a fresh solve.
  • Devcontainers, CI, infrastructure-as-code pin the rest.

This works when the team can declare a target (often linux/amd64) and pay a platform group to keep images healthy.

3 Scientific computing

The same rungs, without control of the target: mixed laptops, a shared HPC cluster (no root, old glibc), maybe one GPU box.

Tools:

  • conda / mamba / pixi: packages plus compiled C/C++/Fortran libraries; pixi lockfiles cover several platforms in one file.
  • R renv; Julia Project.toml / Manifest.toml.
  • Apptainer (formerly Singularity): rootless containers for HPC. Docker’s privileged daemon is usually forbidden on shared clusters.
  • Nix / Guix: hashed source rebuilds.
  • Snakemake / Nextflow: per-step containers in a workflow graph.

A paper’s code needs to run in five to ten years for a replicator with no access to the author.

Trisovic et al. executed ~9,000 R files from 2,000+ Harvard Dataverse replication deposits (2010–2020). 74% failed without error; an automatic repair pass left 56% still failing.

4 Hardware and OS failures

Two axes: where the code runs (laptop, cloud VM including arm64 Graviton, HPC, edge) and what silicon (x86_64 vs arm64; CUDA / ROCm / Metal / CPU).

Examples a lockfile does not fix:

  • PyTorch CUDA 12.4 wheel on a driver that only supports 12.1 — fails at import.
  • No arm64 wheel; pip builds from source and dies on a missing Fortran/BLAS toolchain.
  • Last digits change: IEEE-754 addition is not associative; compilers (-ffast-math, FMA) and BLAS/thread count reorder sums.

5 Language coverage

  • Python: pip pins packages only. conda/pixi reach compiled libraries and CUDA. uv pins packages and the Python version; universal lockfile via a forking resolver; --exclude-newer for dated resolves.
  • R: renv snapshots; CRAN serves current versions, so Posit Package Manager dated CRAN snapshots matter. Bioconductor trains couple R version to packages.
  • Julia: Project.toml + Manifest.toml including Julia version; JLL packages put some native libs in the manifest.
  • C/C++/Fortran: CMake, vcpkg, Conan; Fortran fpm is young. Compiler and ABI are mostly unpinned. Fortran still sits under LAPACK, hence under NumPy and R.
  • Shell: awk/sed/grep are ambient (BSD vs GNU). No lockfile.
  • JavaScript: npm/pnpm/yarn lock packages. Bun can compile a self-contained executable (runtime in the artifact).

6 Agents

An agent that writes a patch, runs tests, and iterates pays environment cost every loop. Slow or non-deterministic resolves become a correctness problem: the agent cannot tell a failed edit from dependency drift.

Tooling acquisitions (context for why lockfiles got cheaper):

None of this removes a CUDA-driver mismatch. It raises the cost of leaving a rung unpinned.

Commit architecture, driver, runtime, and the resolved lockfile next to the code.

Source. Isn’t. The. Program. Rungs. Move. Independently. Pin. Every. One.

7 References