To compare staging and production, export the installed package list from each running environment, sort and normalize both, then diff them. Read from what is installed, not from the manifest. Then group the differences by risk so people fix the dangerous ones first.
Step 1: capture from the running system
Manifests describe intent. Running systems hold the truth. Use the package manager's own listing in each environment:
| Ecosystem | Listing command |
|---|---|
| npm | npm ls with the all option, or read the installed tree |
| pip | pip freeze |
| OS packages | the distribution's package query command |
The npm documentation describes npm ls as printing all installed package versions, with dependencies in a tree when you ask for all. The pip docs describe pip freeze as output in requirements format. For containers, run the command inside the exact image that is deployed, not a rebuilt copy.
Capture at the same time of day, and note the release identifier next to each export so the data can be traced.
Step 2: normalize
Two exports rarely diff cleanly. Fix that first:
- Sort both lists alphabetically.
- Lowercase names where the ecosystem treats case as insignificant.
- Remove lines you expect to differ, such as your own application package.
- Write each line as name, then version, one per line.
Step 3: diff and classify
A plain text diff gives three groups: only in staging, only in production, and present in both with different versions. Add a short column for each difference:
- Does it have a known advisory?
- Is it a major, minor, or patch change?
- Is it a direct dependency or pulled in by another package?
- Is it reachable from your code?
The last question is where reachability thinking saves time (see also measuring version drift across services): a drifted package that your code never calls is lower priority than one on a hot path.
Traps that hide drift
- Comparing lockfiles instead of installed trees. A lockfile can match while the host differs.
- Comparing from a laptop. Your local state is a third environment.
- Ignoring operating system packages. Library versions in the base image drift without any manifest change.
- Cached layers. A container built from a cached layer may hold older packages than a fresh build.
Make it repeatable
Run the comparison as a scheduled job. Store each export with a date and release label. When you find a difference, write down the cause, because patterns repeat. If most differences come from rebuilds, the fix is process. If they come from manual edits, the fix is access control.
Try it yourself
Click through it at your own pace, or open it full screen.
Notice that each package appears with its version in one list. That list, produced per environment, is the input to your diff.
What Safeguard does here
Safeguard builds an SBOM and dependency inventory from your projects and monitors it for new advisories, so you can compare environments from structured data and not from screenshots. Try a free scan at gold.safeguard.sh/scan.
Quick check
Is an SBOM diff better than a package list diff?
An SBOM carries more fields, such as hashes and relationships, so it catches more. A plain list is a fine start.
How often should I compare?
At least on every release, and on a schedule between releases to catch manual changes.
Sources
Last reviewed: 19 October 2026.