skip to content

A project manager locks your ticket-triage bot's dependencies, but the deploy image installs with pip — what does that cost?

level: seniorimportance: should knowfreq 36%

answer

  1. Two install paths, one source of truth
  2. The lock format belongs to the tool
  3. An export is derived, and derived drifts
  4. A silent failure ships stale pins
  5. Tests validated a different environment

basics

~20 s

You now have two install paths and only one source of truth. The manager's lock file is its own format that pip cannot read, so the deployment image installs from an exported file that can silently fall behind — meaning tests and production run different dependency sets.

solid answer

~50 s

A project manager's lock file is a **tool-specific artifact**. pip cannot read it, so a pip-based image must install from something exported out of it, and that export is a derived file with all the usual derived-file failure modes: it can be stale, it can be generated by a step whose failure is swallowed, and it can lose information the lock carried — dependency groups, per-platform markers, hashes. The concrete cost is that your regression suite validates the environment the manager built while production runs the environment pip built, and nothing compares them. Contain it by making the two paths one: install with the same manager inside the image, using a pinned version of the tool. If you truly must export, make the export a checked build step that fails loudly, regenerate it in CI and fail the build if it differs from the committed copy, and verify hashes on install.

go deeper

for a junior

Know that a project manager's lock file is that tool's own format and that pip cannot install from it directly. Something has to translate, and the translation is where things go wrong.

for a middle

Explain what an export loses relative to the lock — multi-platform resolutions, group membership, hashes — and why a derived file needs regenerating and checking rather than trusting.

for a senior

Diagnose the whole path: identify that CI validated a different environment from the one shipped, name the swallowed failure as the root cause, and propose one install path with a pinned manager plus a CI check that the export matches the lock.

for a principal

Own the rule that reproducibility only holds along the path that reads the lock, and set the organisational default — one install path everywhere, the tool version pinned, and any second path verified in CI rather than trusted.

## The shape of the failure A ticket-triage bot is developed with an all-in-one project manager. Contributors sync from the manager's lock file; the 340-case regression pack runs in CI against that environment and is green. The deployment image, written earlier and never revisited, does a plain pip install from a requirements file that a build script exports from the lock. Then the export step starts failing — the manager's version in the image moved, or the flag it used was renamed. The script wraps the export in a broad `except Exception` that logs at debug level and continues, or the build recipe ends the line with a fallback that ignores a non-zero exit. The **exception is swallowed**, the build succeeds, and the image ships with the requirements file that was already in the layer: yesterday's, or last quarter's. Nothing catches it. The regression pack passes because it never touched the image's environment. Production runs a dependency set nobody has tested, and the symptom surfaces weeks later as behaviour that cannot be reproduced locally — the reproduction environment is, by construction, the one that works. ## Why the lock file is the pivot The portability cost is not philosophical; it is that **the lock is in the manager's own format**. It is not a requirements file, pip does not parse it, and its contents are richer than a flat pinned list: per-platform and per-interpreter resolutions selected by environment markers, the dependency groups each entry belongs to, hashes, and the resolution metadata needed to re-verify rather than re-solve. An export to a requirements file is a *projection* of that. It flattens a multi-platform resolution to whatever platform the export ran on, it collapses group membership, and depending on how it is invoked it may or may not carry hashes. Even when it is perfectly correct, it is a second artifact that must be regenerated in lockstep with the first, and every derived artifact eventually drifts from its source. ## The full portability surface The deployment path is the sharpest edge, but the same fact bites in four other places. **Contributors.** Anyone touching the repository needs that manager installed, at a version compatible with the lock format. Lock formats have versions and they do change. **CI and containers.** Every job that installs dependencies now has the manager as a prerequisite, so the manager itself is an unpinned dependency unless you pin it explicitly. An unpinned installer is exactly the kind of thing that changes your build without a commit. **Downstream consumers.** If the project is a library, the lock is never published — a wheel carries the abstract `dependencies` from the `[project]` table. Your lock guarantees your builds, not your users' installs. Teams that forget this ship a library and are surprised that consumers resolve different versions. **Heterogeneous stacks.** A Python-only lock cannot express non-Python binaries, so a project that also needs system libraries or a matching accelerator runtime has a second resolution problem the lock does not cover. ## Containing it The fix that removes the class of bug, rather than the instance, is to **have one install path**. Install with the same manager inside the image, from the same committed lock, with the manager's version pinned in the image. Development, CI and production then resolve to bit-identical environments by construction, and there is no export to go stale. When an export genuinely cannot be avoided — a platform image you do not control, a policy that only allows pip, an air-gapped mirror — treat the exported file as build output with the discipline that implies: - **Fail loudly.** No broad exception handler, no ignored exit status around the export. A failed export must fail the build; that single change turns this scenario into a red pipeline instead of a silent production drift. - **Verify, do not trust.** Regenerate the export in CI and fail if it differs from the committed copy. That is a two-line check that makes staleness impossible. - **Keep the hashes.** Export with hashes and install requiring them, so a mismatch is a hard error rather than a quietly different package. - **Test what you ship.** Run at least a smoke slice of the regression pack against the *built image*, not only against the manager's environment. If the two paths must exist, the tests must cross them. The interview point underneath all of this: a lock file only guarantees reproducibility along the path that reads it. The moment a second path exists, reproducibility becomes a property you have to verify, not one you have bought.

  • What single change would have turned this silent drift into a failed build?
    Removing the swallowed failure around the export. A broad `except Exception` that logs and continues, or a shell fallback that ignores a non-zero exit, converts a hard error into stale build output. Make the export step fail the build, and the same defect becomes a red pipeline on the day it appears rather than an unreproducible production bug weeks later.
  • The team pins every dependency but the build still changes between runs — what did they miss?
    The manager itself. If the image installs the latest project manager and then syncs, the tool is an unpinned build input: a new release can change resolution, lock-format handling or default groups. Pin the manager's version in the image the same way you pin the interpreter, and treat an upgrade as a reviewed change.
  • Does committing a lock file help the consumers of a library you publish?
    No. A wheel carries the abstract requirements from the `[project]` table, never your lock, so consumers resolve fresh against their own constraints. The lock makes your development, CI and release builds reproducible. Libraries still commit one for that reason, but should not confuse it with a guarantee offered to users.

Exporting a lock file to a requirements file is like photocopying the master blueprint for the site crew: it works until someone edits the master and the crew keeps building from the copy.

saying these in an interview costs you the question

  • Assumes pip can install from any manager's lock file
  • Treats an exported requirements file as equivalent to the lock
  • Leaves the export failure swallowed and logged only
  • Thinks a green test suite proves the image is correct
  • Believes a published library ships its lock file to consumers
  • Installs the latest project manager in the image unpinned

context