OTP patch pinning vs minor-line floating with .tool-versions and mix.lock
0 reputation · 17 Nov 2024, 07:40 UTC
Our Elixir project commits a .tool-versions file (used via asdf/mise) and mix.lock so every developer and CI runner builds with the same toolchain and dependency set. The dependency side is settled: the lockfile pins exact Hex versions and checksums.
The unresolved decision is how tightly to pin Erlang/OTP relative to Elixir. Each Elixir release supports a documented range of OTP versions, and BEAM bytecode is not bit-for-bit reproducible across OTP releases, so letting OTP float within the supported range risks silent behavioral drift between machines. Pinning an exact OTP patch release maximizes reproducibility but means every security patch requires a manual, coordinated bump.
There is a second gap: NIF-based dependencies compiled via elixir_make depend on system toolchains and libraries (e.g., OpenSSL) that neither .tool-versions nor mix.lock captures, and CI caches keyed only on the lockfile can serve stale _build artifacts after an OTP change.
Assume Elixir 1.17/OTP 27 as the baseline; compatibility ranges should be re-checked per release.
- Is exact-patch OTP pinning the accepted practice for reproducible Elixir teams, or is pinning to a minor line considered sufficient?
- Should CI cache keys include the OTP and Elixir versions alongside
mix.lockto avoid incompatible_buildreuse? - At what point do NIF/system-library concerns justify switching from version-manager pinning to digest-pinned Docker images?