Safeguard
Developer Tools•

Per-language dependency gotchas: a one page cheat sheet

One surprising behavior per ecosystem, from official docs: npm, pip, Go, Cargo, NuGet, Gradle, Maven and Composer. Print it and keep it near CI.

Safeguard Security Team
4 min read

Each package manager has one behavior that surprises people who know a different one. npm's clean install deletes node_modules. pip hash checking is all or nothing. Go prefers a vendor directory when one exists. NuGet treats a version as a minimum. Here is a one page list, each item from official documentation.

The cheat sheet

EcosystemThe gotchaWhy it matters
npmThe clean install command removes an existing node_modules first and errors if lock and manifest differDo not run it expecting to add a package
pipGiving a hash to any requirement turns on hash checking for all, and every dependency must be listed and pinnedA half hashed file fails by design
GoWith go 1.14 or higher in go.mod and a vendor directory present, the go command uses the vendor directory by defaultEdits to go.mod may not take effect until you re-vendor
Cargocargo install picks the latest dependencies unless told to use the lockfileInstalling a tool is not the same as building your project
NuGetA plain version means that version or higher, preferring the lowestA newly published lower version can change resolution
GradleLocking is opt in, and a changed direct version needs the lock updated or resolution may failUpdate lock state with the version
MavenThe nearest definition in the tree wins, and with a tie the first declaration winsDepth in the tree decides, not newest
Composerinstall uses composer.lock, update rewrites itRun update only on purpose

Notes on each

npm. The clean install never writes to package.json or any lock. Flags used to create the lock must be used again, or errors are likely.

pip. The hash fragment in PyPI download URLs only checks for corruption, since it comes from the same server, and does not satisfy the local hash requirement.

Go. go.sum is checked on every download. If a hash is missing, Go consults the checksum database unless the module is matched as private.

Cargo. The FAQ states that cargo install selects the latest dependencies unless locked is passed.

NuGet. The docs give a day by day example where publishing version 4.0.0 on day two changes what a requirement for 4.0.0 resolves to. Locking removes that.

Gradle. Lock state goes in gradle.lockfile in each project directory, and Gradle will not write lock state if the build fails.

Maven. You can guarantee a version by declaring it explicitly in your POM, and dependency management can set versions of transitive dependencies.

Composer. The lock records exact versions, so teammates, CI and production use the same set.

How to use it

Print this and place it where the pipeline owners sit. When a bug report says "it worked on my machine", scan the table for the tool in question. Many surprises are one of these eight behaviors.

When you add a language that is not on the list, write its row, using only what its official docs say.

Safeguard can help here: it scans dependencies and generates SBOMs, applies reachability analysis so you can sort findings by what your code can actually call, and enforces policies and gates on pull requests. You can try a free scan on a repository to see what it finds.

Quick check

Why does Maven's nearest definition rule matter for security?

A vulnerable version nearer the root can win over a patched one deeper in the tree. Declare the version you want explicitly.

Does Go always use the module cache?

Not if a vendor directory exists and go.mod says go 1.14 or higher. Then it acts as if vendor mode were on, unless told otherwise.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.