Why Rebuilding for Production Is Riskier Than It Looks
You have a green build on staging. Tests passed. The team is ready to ship. Someone says, "Let's just checkout the same tag, rebuild, and deploy to production. We don't need to keep old artifacts around."
It sounds efficient. No artifact storage to manage. No digging through old builds. Just run the build again and ship it. But this seemingly practical shortcut introduces two problems that quietly undermine everything you're trying to achieve with a delivery pipeline: you lose traceability, and you lose reproducibility.
The Traceability Problem
When you rebuild for production, the artifact running in production is not the same artifact that passed tests in staging. You built it from the same source code, but the build process created a new artifact with a new identity. It has a different checksum, a different build timestamp, and potentially different embedded metadata.
If something goes wrong in production, you cannot point to the staging results and say "this exact artifact was tested." You have to trust that the second build produced an identical result. Without verification, that trust is blind.
The following flowchart contrasts the two paths:
Traceability is not about paperwork. It is about being able to answer a simple question: "What exactly is running in production right now, and how do I know it matches what we tested?" When you rebuild, that question becomes harder to answer with confidence.
The Reproducibility Problem
Even if you checkout the exact same commit, the build process does not always produce the same output. Many factors can change the result between two builds:
Dependency drift. Your first build on Monday pulled version 1.2.3 of an open source library. By Wednesday, the maintainer released version 1.2.4. Your second build pulls the new version automatically. No source code changed, but the application behavior might have. A minor patch could fix a bug you relied on, or introduce a regression. It could also silently pull in a security vulnerability.
Base image updates. If your build uses a Docker base image tagged latest or even a specific minor version that gets patched, the second build might use a different underlying operating system layer. The application code is the same, but the runtime environment shifted underneath it.
Build environment differences. The CI runner that built staging might have had a slightly different JDK version, Node.js runtime, or system library. Maybe the build cache expired between runs, causing a full recompilation that produces different bytecode. These differences rarely surface during development, but they can cause subtle production failures.
Timestamps and metadata. Many build tools embed timestamps, build numbers, or commit hashes into the artifact. Even if the functional code is identical, the artifact metadata differs. This makes debugging harder when you need to correlate production behavior with a specific build.
The Real-World Impact
These problems are not theoretical. Teams have shipped rebuilds to production only to discover that an automatically updated dependency introduced a security flaw. Others have seen application behavior change because a patch-level update in a library altered how a function worked. In some cases, the rebuild produced an artifact that worked fine in development but failed in production because the build environment had a different compiler version.
The worst part is that these issues are hard to detect. The pipeline shows green. The commit hash matches. Everything looks fine until someone notices something is off in production, and by then, tracing the root cause becomes a forensic investigation.
The Principle: Build Once, Promote Many
This is why the principle "build once, promote many" exists. You build your artifact once, verify it, and then promote that exact same artifact through each environment: development, staging, production. No rebuilding. No second chances.
With this approach, the artifact in production is physically identical to the artifact that passed all tests. The checksum matches. The embedded metadata matches. You know exactly what was tested and what was shipped. Traceability is built in because every artifact has a unique identifier linked to its commit, build configuration, and timestamp. Reproducibility is not a concern because you are not reproducing anything - you are using the same binary.
When Rebuild Is Unavoidable
There are legitimate cases where rebuilding is necessary. An old artifact might be incompatible with a production infrastructure upgrade. A critical security patch in a base image might force a rebuild. A compliance requirement might demand that production artifacts are built from a specific hardened environment.
These situations exist, but they should be exceptions, not the default. Every time your team decides to rebuild for production, you should explicitly acknowledge the risks: you are breaking traceability, and you are betting that the build is reproducible. Document the decision, verify the output against the original build, and treat it as an incident review opportunity to prevent future rebuilds.
Practical Checklist Before Rebuilding for Production
- Can you verify that the rebuild artifact is functionally identical to the tested artifact?
- Have you pinned all dependency versions, including transitive dependencies?
- Is the build environment (compiler, runtime, system libraries) identical to the original build?
- Are base images pinned to specific digests, not tags?
- Do you have a documented reason why promotion of the original artifact is not possible?
- Has the team agreed to treat this as an exception, not a standard practice?
The Takeaway
Every rebuild for production introduces uncertainty into your delivery pipeline. You lose the ability to say with confidence that the artifact running in production is the same one that passed testing. The savings in artifact storage or convenience are rarely worth the debugging cost when something goes wrong. Build once. Promote that same artifact everywhere. Let the rebuild be the exception that requires explicit justification, not the default path to production.