For which kinds of software can a delivery pipeline genuinely not ship the identical artifact to every environment or channel, and what would you put in place to compensate?
answer
- most impossibility claims are baked configuration
- one build per variant, never per environment
- diverge as late as the pipeline allows
- reproducible builds make a rebuild checkable
- test the variant that actually ships
basics
~20 sWhen the output is genuinely per-target: platform-specific binaries, store-signed mobile builds, compiled-in tenant branding, or artifacts rebuilt inside an air-gapped boundary. Compensate with reproducible builds, hash comparison, a thin divergent layer, and testing every variant that ships.
solid answer
~50 sMost "we can't build once" claims are really configuration baked into the artifact, and the answer is to move it out. The genuine cases are narrower: native binaries per CPU architecture and operating system, mobile builds signed and identified differently per distribution channel, white-label products whose branding is compiled in, ahead-of-time-compiled binaries that fix at build time what a normal runtime would read at start-up, and regulated or air-gapped environments that require the build to happen inside the boundary. The discipline in each case is the same. Build each variant exactly once and promote it, rather than rebuilding per environment. Keep the divergent surface as thin as possible — ideally a signing or packaging step over a shared core. Test the exact variant you ship, not one representative. And where a rebuild is mandated, make the build reproducible so an independent rebuild can be compared by hash against the one you tested.
go deeper
Know that some software genuinely ships as several artifacts — one per platform, for instance — and that each of them should still be built once rather than rebuilt for each environment.
Distinguish real variants from configuration that was merely baked in, and explain how variants share a pipeline and diverge as late as possible.
Show how you verify a mandated rebuild — pinned inputs, reproducibility checked in CI, hash comparison against the tested artifact — and how you allocate test effort across variants.
Own the standard: variants require a named justification, a thin divergence point and an owner, because each one is a permanent tax on every release, hotfix and rollback.
## First, separate the real cases from the excuses Most claims that build-once is impossible turn out to be configuration compiled into the artifact — an endpoint, a tenant identifier, a log level. That is a fixable design problem, not a constraint. Push hard on this before accepting divergence, because every genuine variant multiplies test surface, storage, promotion lanes and the number of things that can be wrong in production while right in staging. ## The cases that are real **Platform-specific binaries.** A native executable for linux/amd64 and darwin/arm64 cannot be one file. This is the least painful case: a release is a *set* of artifacts, each built once, published together under one version and promoted as a unit. It is not build-per-environment — no environment triggers a rebuild — and container registries can present the set behind a single reference so consumers resolve the right member automatically. **Mobile applications.** Store distribution, internal test tracks and enterprise distribution commonly require different signing identities, sometimes different bundle identifiers or entitlements. You cannot re-point a signed store binary at a test channel. **White-label or per-tenant products.** When branding, locale bundles or licensed feature sets are compiled in, each customer gets its own artifact. The strategic answer is to make theming and entitlement runtime-resolved, because a compiled-in tenant list means a hundred customers is a hundred build-and-test lanes. **Ahead-of-time-compiled binaries.** Closed-world AOT compilation fixes at build time some decisions a JIT runtime would defer to start-up, so a build may need to know things earlier than a conventional one. Keep environment values out of that set wherever the toolchain allows. **Regulated or air-gapped delivery.** Some regimes require the shipped binary to be produced inside a controlled boundary from reviewed source, so the artifact your public pipeline tested is not the one that ships. ## The compensating controls **Build each variant once.** The rule that matters is not "one artifact" but *no artifact is produced twice*. Every variant is built once, versioned, stored and promoted through environments exactly like a single artifact would be. Rebuilding a variant per environment is still the original defect. **Keep the divergent surface thin.** Structure the pipeline so variants share as much of the build as possible and diverge as late as possible — ideally in a signing, packaging or repackaging step applied to a common core. What you cannot test in common should be a small, well-understood layer, not the whole application. **Test what ships, not a representative.** The seductive shortcut is to run the full suite against one variant and smoke-test the rest. That is exactly where variant-specific defects live: a signing entitlement that blocks a permission, an architecture-specific library, a tenant's feature flag combination nobody exercised. Decide deliberately how much suite each variant gets, and be honest that trimming it is accepting risk. **Make rebuilds verifiable.** Where a rebuild is unavoidable — the air-gapped case especially — invest in a reproducible build: pin every input including toolchain and base image, eliminate timestamps and nondeterministic ordering, and then *verify* by rebuilding and comparing hashes. Reproducibility is not a claim you make, it is a check you run in CI. Once it holds, an independent rebuild inside the boundary can be compared byte-for-byte against the artifact you tested, which restores most of the assurance build-once provides. **Record the mapping.** One source commit now maps to several artifact identities. That mapping — commit, variant, identity, and which environments or channels each one reached — must be recorded, or you lose the ability to answer what a given customer or platform is running. ## How to decide Weigh the risk carried by the divergent surface against the cost of removing it. A signing step over an already-tested binary is low risk and cheap to accept. Compiled-in per-tenant behaviour is high risk and worth real engineering investment to eliminate, because its divergence sits in application logic where bugs live. And treat every accepted variant as a standing tax: it recurs in every release, every hotfix and every rollback drill, not just once at the moment you approve it. ## The organisational move Make divergence something a team has to justify rather than something that accretes. A short standard — variants require a named reason, a thin divergence point, a test plan per variant, and an owner — is usually enough to stop the drift from "one artifact plus a signing step" toward "eleven builds nobody can reason about".
- How do multi-platform native binaries stay compatible with build-once discipline?Treat a release as a set of artifacts rather than one: each platform variant is built exactly once, published together under a single version, and promoted as a unit. No environment ever triggers a rebuild, which is the property that matters. Consumers resolve their platform's member from the set, and the release record names every member's identity.
- What does it take for a build to be genuinely reproducible, and how do you know you have it?Pin every input — source, dependencies, toolchain, base image — and remove nondeterminism such as embedded timestamps, hostnames and unordered archive entries. Then verify rather than assume: have CI build twice, ideally on different machines, and compare hashes. A reproducibility check that runs on every change is what keeps the property from quietly decaying.
- A team wants per-customer builds with branding compiled in. What would you push them toward?Runtime-resolved theming and entitlements, so branding is configuration the single artifact reads rather than code it contains. Compiled-in tenants make the number of build, test, promotion and rollback lanes scale with the customer count, and each new customer becomes an engineering event. If some divergence is unavoidable, confine it to assets applied late over a shared core.
saying these in an interview costs you the question
- Different environments justify different builds
- One representative variant is enough to test
- Reproducible builds are automatic with containers
- Per-tenant builds are cheap because the code matches
- Every variant can share a single test run