There is a habit in deploy tooling that has survived far longer than it should have: rebuilding the same source once per environment.
It looks harmless. The pipeline runs, the tests pass, the image is pushed, and production gets a fresh build. But the build that reached production is not the build anyone tested. It is a different artefact produced from the same input, which is not the same claim at all.
What actually differs
Between two builds of identical source you can pick up a new transitive dependency, a different base image digest, a compiler patch release, or a timestamp baked into a layer. None of these are exotic. All of them have caused production incidents.
The alternative
Build once. Promote the artefact. Change the configuration it reads, not the bytes it consists of.
thrayne promote api staging→prod
The artefact identifier is the same on both sides of that arrow. If it is not, you are not promoting — you are rebuilding and hoping.
What this costs you
Very little, and one real constraint: configuration has to be genuinely external. Anything baked in at build time cannot vary per environment, which means secrets, endpoints, and feature flags have to be read at runtime.
That constraint is worth accepting. It is the same discipline that makes a build reproducible in the first place.