Lock-file deterministic restore vs explicit version downgrade for NuGet rollback reproducibility
0 reputation · 14 May 2025, 16:55 UTC
0 reputation · 14 May 2025, 16:55 UTC
Goal is safe recovery from a breaking NuGet upgrade in an SDK-style project using PackageReference, with reproducibility across developer machines and CI.
Two documented approaches exist. Lock-file deterministic restore relies on a committed packages.lock.json and locked-mode restore to detect any change to the resolved graph before build. Explicit version downgrade updates the direct PackageReference to a previous version and restores from that declaration.
The trade-off involves transitive consistency versus adoption cost. Lock files preserve exact transitive versions and prevent silent floating upgrades, but require team policy on committing lock files and consistent SDK behavior. Manual downgrade is simpler for a single direct dependency but does not guarantee transitive consistency when version ranges remain.
Guidance on when to enable and commit lock files for applications versus libraries is ambiguous, and interaction with floating versions and central package management varies by SDK.
Under which conditions does locked-mode restore provide a safer rollback signal than an explicit downgrade? How should teams decide lock-file policy for applications versus libraries? What consistency guarantees remain when central package management is used with either approach?
Locked-mode restore provides a safer rollback signal than an explicit downgrade when your dependency graph contains floating versions or version ranges. While an explicit downgrade ensures the direct dependency returns to a known state, it does not guarantee that transitive dependencies (the dependencies of your dependencies) will resolve to the same versions used during the original stable build.
Locked-mode restore is the safer choice when you need to verify that the entire resolved graph is identical across environments. It is particularly critical when:
The decision to use lock files depends on who consumes the output:
| Project Type | Lock File Policy | Reasoning |
|---|---|---|
| Applications | Enable and Commit | Ensures environment parity. The application is the final consumer; consistency in production is the priority. |
| Libraries | Disable/Do Not Commit | Avoids restricting the consumer. Forcing specific transitive versions can cause version conflicts (dependency hell) for the end-user. |
Central Package Management simplifies versioning by moving declarations to a Directory.Packages.props file, but it does not replace the restore mechanism. The consistency guarantees remain as follows:
To implement and verify a locked-mode rollback in an SDK-style project:
.csproj: <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>packages.lock.json and commit it to version control.dotnet restore --locked-modeNote: If --locked-mode is not used, the lock file is advisory and will not fail the build on mismatch.
Diagnostic Detail Needed: Are you using floating versions (e.g., 1.0.*) or specific version ranges in your PackageReference declarations?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.