Skip to content

Package Managers

💡 Learning Guide: You don't have to reinvent the wheel when writing code — 99% of the functionality you need has already been written and published online by someone else. A package manager is the tool that helps you find, download, and manage these "ready-made parts." This chapter revolves around one core question: how to make code dependencies reproducible, collaborative, and maintainable?


0. Why Will You Definitely Need a Package Manager?

Imagine you want to write a Node.js program that makes HTTP requests. There are two paths:

  • Method A (manual): Implement TCP connections, HTTP protocol parsing, redirect handling, timeout mechanisms yourself... probably thousands of lines of code, months of debugging.
  • Method B (package manager): npm install axios, done in ten seconds with one line of code.

A package manager is essentially an "app store" for code. It helps you:

  1. Find libraries published by others in a central repository (Registry)
  2. Automatically download and install them into your project
  3. Handle the libraries that your libraries depend on (dependencies of dependencies)
  4. Record the exact versions you're using so team collaboration doesn't break

1. Package Managers Across Language/System Ecosystems

Different programming languages and operating systems have their own ecosystem toolchains, but the underlying logic is exactly the same.

👇 Try it out: Select an ecosystem you're familiar with and explore its mainstream package management tools.

Package Manager Ecosystem MapChoose a language ecosystem and explore its package management tools
npm
Most widely used and bundled with Node.js
Yarn
Fast parallel downloads; Plug'n'Play can avoid node_modules
pnpm
Shared hard links save disk space and make installs fast
npmNode Package Manager
Install dependencynpm install lodash
Install dev dependencynpm install -D typescript
Run scriptnpm run build
List installednpm list --depth=0
package.jsonProject manifest that records dependencies and scripts
package-lock.jsonPins exact versions for consistent environments
node_modules/Directory where installed packages live
Built into Node.js
Largest ecosystem, 2M+ packages
Supports workspaces
Run directly with npx
Core idea:A package manager is like an app store for code. It downloads, installs, and manages libraries written by others while automatically handling version compatibility.

1.1 Where Do Packages Come From? — The Registry

Every ecosystem has a central repository behind it that stores all downloadable packages:

EcosystemRegistryPackage Count
JavaScriptnpmjs.com2 million+
Pythonpypi.org500K+
Rustcrates.io150K+
Gopkg.go.dev500K+
macOS/Linux Toolsformulae.brew.sh7,000+
Windows Softwarewinget.run / chocolatey.orgTens of thousands

1.2 JavaScript's Big Three: npm vs yarn vs pnpm

Similar functionality, with differences mainly in speed and disk usage:

text
Disk usage: pnpm (hard link sharing) < yarn PnP (zero node_modules) < npm (full copy)
Install speed: pnpm ≈ yarn > npm
Usage: npm (most universal) > pnpm (recommended for new projects) > yarn (some teams)

Recommendation: Use pnpm for new projects, stick with the existing tool for existing projects, and don't switch casually.

1.3 Windows's Big Three: winget vs Chocolatey vs Scoop

wingetChocolateyScoop
Official backingMicrosoft officialThird-partyThird-party
Requires adminPartially neededYesNot needed
Best forDaily software installationEnterprise batch deploymentDev tool management
Package countMany, growing fastMost (10,000+)Focused on dev tools

Recommendation: Use winget for daily use, scoop for dev tools, Chocolatey for enterprise automation.


2. Installing Packages — What Happens Behind the Scenes?

After typing npm install axios, the command line goes quiet for a few seconds, and then it's done. What exactly happened during those seconds?

👇 Try it out: Select a package, click "Run," and observe the full installation process.

Full npm install SimulationWatch a package travel from the command line to disk
$ npm install
📟 Install log
Waiting to run...
📁 File tree changes
my-project/
├── package.json
├── package-lock.json
└── node_modules/
📄 package.json
{
  "name": "my-project",
  "version": "1.0.0",
  "dependencies": {},
  "devDependencies": {}
}
Resolve deps
Analyze required packages
Fetch & extract
Download tarballs from registry
Link modules
Write node_modules/
Write lockfile
Pin exact versions
Core mechanism:Installation first resolves the dependency tree, downloads from the registry, extracts packages into node_modules, and writes a lockfile. The lockfile ensures everyone on the team installs the exact same versions.

2.1 The Four Stages Explained

① Dependency Resolution

The package manager first "understands" what you want to install. Take axios as an example — it itself depends on packages like follow-redirects, form-data, etc., which also need to be installed. This process is called building the dependency tree.

② Fetch

Download all needed packages from the Registry (compressed .tgz archives). Smart package managers will:

  • Download multiple packages in parallel instead of waiting one by one
  • Check the local cache first — if there's a hit, skip the network

③ Link

Extract the downloaded packages into the node_modules/ directory and set up the reference relationships.

④ Write Lockfile

Write the exact version numbers from this installation into package-lock.json (or yarn.lock / pnpm-lock.yaml).

2.2 Most Common Commands Cheat Sheet

bash
# ── JavaScript (npm) ──────────────────────────────────
npm install              # Install all dependencies per package.json
npm install axios        # Install a new package (production dependency)
npm install -D jest      # Install a dev dependency (only used during development)
npm install -g tsx       # Global install (available in any directory)
npm uninstall axios      # Uninstall a package
npm update               # Upgrade all packages to latest compatible versions
npm run build            # Run scripts defined in package.json
npx create-react-app .   # Run temporarily without installing to project

# ── Python (pip) ──────────────────────────────────────
pip install requests           # Install a package
pip install requests==2.28.0   # Install a specific version
pip freeze > requirements.txt  # Export current dependency list
pip install -r requirements.txt # Install from a list

# ── Rust (cargo) ──────────────────────────────────────
cargo add serde    # Add a dependency (auto-updates Cargo.toml)
cargo build        # Build the project
cargo test         # Run tests
cargo run          # Run the project

# ── Go (go mod) ───────────────────────────────────────
go get github.com/gin-gonic/gin  # Add a dependency
go mod tidy                      # Clean up dependencies (remove extras, add missing)
go build ./...                   # Build

# ── Windows (winget) ──────────────────────────────────
winget install Git.Git           # Install software
winget upgrade --all             # Update all installed software

2.3 What Are npm scripts?

The scripts field in package.json is npm's built-in task runner:

json
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "test": "jest",
    "lint": "eslint src/"
  }
}

Running: npm run dev, npm run build. Benefits:

  • Unified entry point: Team members don't need to memorize underlying tool commands
  • Auto environment setup: Running automatically adds node_modules/.bin to PATH, so locally installed tools are directly usable

3. Global vs Local Installation

This is one of the most confusing concepts for beginners.

3.1 The Difference

bash
npm install axios        # Local install: into ./node_modules/, only available in current project
npm install -g typescript  # Global install: into system directory, available in any project/directory
Local InstallGlobal Install
Location./node_modules/System-level directory (e.g., /usr/local/lib/)
Best forProject dependency libraries (axios, vue, react)CLI tools (tsc, eslint, create-react-app)
Version isolationEach project has independent versions ✅One version shared across the machine ⚠️
Team consistencyLockfile ensures consistency ✅Different people may have different versions ⚠️

3.2 The Golden Rule

Library dependencies (axios, lodash, vue) should always be installed locally; CLI tools (tsc, eslint) should preferably be installed locally too, invoked with npx.

Why are CLI tools also recommended for local installation?

Suppose you globally installed eslint@8, but Project A needs the new rules from eslint@9 — you'd have to switch back and forth between global and project versions. Install eslint locally and invoke it with npx eslint ., so each project can independently configure its own version.

3.3 npx — Run Temporarily, Don't Pollute the Environment

npx is npm's built-in package runner that lets you run a package without installing it:

bash
# Run create-vue without installing it, to initialize a project
npx create-vue my-project

# Run prettier without installing it, to format files
npx prettier --write src/

# Force a specific version (ignoring any installed version)
npx typescript@5.4 tsc --version

Python's uvx and Rust's cargo run also provide similar "run temporarily" capabilities:

bash
uvx ruff check .       # Python: temporarily run the ruff checker
cargo install ripgrep  # Rust: install globally, becomes system command rg

4. The Secret of Version Numbers — Semantic Versioning

In package.json, you'll see entries like:

json
{
  "dependencies": {
    "axios": "^1.6.8",
    "typescript": "~5.4.0"
  }
}

What do ^ and ~ mean here?

👇 Try it out: Hover over the parts of a version number to understand their meaning; click range operators to see which versions are accepted.

Dependency Tree & Version SemanticsUnderstand semantic versions and dependency graphs
2
MAJOR
8
MINOR
3
PATCH
..
← Hover over a number to inspect its meaning
Common version range symbols
^2.8.3
Compatible range (recommended)
Allow MINOR and PATCH upgrades, lock MAJOR
~2.8.3
Approximate range (conservative)
Allow only PATCH upgrades, lock MAJOR and MINOR
2.8.3
Exact version (strict)
Accept only this one version
*
Any version (dangerous)
Accept any version, including major upgrades; avoid in production
Golden rule:Semantic versioning = MAJOR.MINOR.PATCH. A MAJOR change means breaking changes, so upgrade carefully.

4.1 Why Not Pin the Version?

ApproachProsCons
"axios": "1.6.8" (exact pin)Fully predictableSecurity patches can't auto-update
"axios": "^1.6.8" (compatible range, recommended)Auto-get bug fixes and new featuresRarely may introduce minor incompatibilities
"axios": "*" (any version)Always latestMajor version upgrades can completely break code

Best practice: Declare ranges with ^ + pin actual versions with a lockfile — use both together.

4.2 What Is Dependency Hell?

When you depend on 50 packages, and each of those depends on several more, the "dependency tree" can have hundreds of nodes. If two of your dependencies need incompatible versions of the same library, you have a "dependency conflict."

How different ecosystems solve this:

  • npm v3+: Same major version hoisted to top level and shared; different major versions each get their own copy
  • pnpm: Hard links + strict isolation, fundamentally preventing "phantom dependencies" (packages you can use without declaring)
  • cargo (Rust): Language-level enforcement that each package can only depend on the same version, completely avoiding conflicts
  • go mod (Go): Minimum Version Selection (MVS) strategy, choosing the lowest version that satisfies all constraints

5. Lockfiles — The Cornerstone of Team Collaboration

5.1 Why Do You Need a Lockfile?

Suppose package.json says "axios": "^1.6.0":

  • You install today → gets 1.6.8
  • Your teammate installs tomorrow → might get 1.7.0 (released last night)
  • CI server next week → might get 1.7.1

Same code, three different results. A lockfile records the exact version of every package, so everyone installs identically.

ScenarioCommandBehavior
Dev environment syncnpm installReferences lockfile, doesn't upgrade versions
CI / production deploynpm ciStrictly follows lockfile; errors if there's a discrepancy
Active version upgradenpm updateUpgrades within allowed range, updates lockfile

5.2 Should Lockfiles Be Committed to Git?

Applications must commit; libraries published to npm may not.

  • Web apps, backend services: Must commit to ensure deployment and dev environments are identical
  • npm-published libraries: Usually not committed; library users have their own lockfiles
  • Python projects: requirements.txt itself acts as a lockfile and should be committed
  • Go projects: go.sum must be committed for integrity verification

6. Python Virtual Environments

Python has a concept that requires special attention: virtual environments (venv).

Why are they needed?

Python installs packages globally by default. Your Project A needs requests==2.28, and Project B needs requests==2.31 — they'll conflict.

Solution: Create an independent virtual environment for each project so they don't interfere with each other.

bash
# 1. Create a virtual environment (run in project root directory)
python -m venv .venv

# 2. Activate the virtual environment
source .venv/bin/activate        # macOS / Linux
.venv\Scripts\activate           # Windows (Command Prompt CMD)
.venv\Scripts\Activate.ps1       # Windows (PowerShell)

# 3. After activation, pip install only affects the current virtual environment, not the global one
pip install requests

# 4. Exit the virtual environment
deactivate

⚠️ Common Windows issue: PowerShell blocks script execution by default. First run:

powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

Modern alternatives:

  • conda create -n myproject python=3.11 — even manages the Python version itself
  • uv venv && source .venv/bin/activate — written in Rust, creates blazingly fast

Should .venv be committed to Git?

No! .venv is generated locally and should be added to .gitignore. Use requirements.txt or pyproject.toml to describe dependencies.


7. Common Issues Cheat Sheet

Q: Should node_modules be committed to Git?

No! It's typically hundreds of MB and should be added to .gitignore. With package-lock.json, anyone can quickly rebuild it with npm install.

Q: Installation fails / getting weird errors?

bash
# Clear cache, delete old installation, start fresh
npm cache clean --force
rm -rf node_modules package-lock.json   # macOS/Linux
rmdir /s /q node_modules && del package-lock.json  # Windows CMD
npm install

Q: Installation is too slow?

bash
# Switch to a domestic mirror (recommended to write to .npmrc file, don't pollute global config)
echo "registry=https://registry.npmmirror.com" > .npmrc

# pip can also configure mirrors
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple

Q: How to handle package security vulnerabilities?

bash
npm audit          # Scan for known vulnerabilities
npm audit fix      # Auto-fix compatible vulnerabilities
npm audit fix --force  # Force upgrade (may be breaking, use with caution)

Q: How to tell if a package is trustworthy?

Check on npmjs.com or bundlephobia.com:

  • Weekly downloads (higher is more trustworthy)
  • Last update time (be cautious if not updated in 2+ years)
  • Number of dependencies (more dependencies = more risk)
  • GitHub Stars and issue activity

Q: Where does winget install software on Windows?

winget installs to system directories (requires admin) or %LOCALAPPDATA%\Microsoft\WindowsApps by default. Scoop installs all software uniformly in %USERPROFILE%\scoop\apps\, making it easy to manage and migrate.


8. Terminology Reference

English TermChinese TranslationExplanation
Package包 / 库Code modules written and published by others
Registry注册表 / 仓库The central storage server for all packages (e.g., npmjs.com)
Dependency依赖Other packages your project needs to run
devDependency开发依赖Packages only needed during development (test frameworks, build tools, etc.)
Lockfile锁文件Records exact version numbers, ensuring environment consistency
SemVer语义化版本MAJOR.MINOR.PATCH version naming convention
node_modules模块目录The directory where npm-installed packages are actually stored
venv虚拟环境An isolated sandbox for Python project packages
tarball压缩包The distribution format of packages, usually .tgz files
Hoisting提升npm lifts sub-dependencies to the top level to avoid duplicate installations
Phantom Dependency幽灵依赖Packages that can be used without being declared in config (pnpm prevents this)
npxnpm's built-in package runner, runs packages temporarily without installing
go.sumGo module hash verification file, prevents dependency tampering
CrateThe unit name for "packages" in the Rust ecosystem
wingetWindows' official package manager (built into Windows 10/11)

Summary: The Essence of Package Managers

Remember the core with these four points:

  1. Package manager = app store: Helps you find, install, and manage code parts so you don't reinvent the wheel.
  2. Lockfile = team contract: Pins exact versions, making "it works on my machine" a thing of the past.
  3. Semantic versioning = communication language: ^ safely gets updates; when MAJOR changes, be careful.
  4. Local > Global: Install project dependencies locally whenever possible; use npx / uvx for temporary tool runs to keep your environment clean.