Ever installed something with one line and wondered what actually happened behind the scenes? Let's break it down. 🚀
Think of your project like a kitchen.
You don't grow your own wheat, milk cows, or churn butter every time you want to bake a cake. You just go to the store (registry), grab the ingredients (packages) you need, and note them down on a shopping list (package.json) so next time you (or a friend) can buy the exact same stuff again.
npm is the delivery guy who reads your shopping list, runs to the store, and puts the ingredients in your kitchen (node_modules) — automatically, every time.
package.json and package-lock.json^, ~, exact) worksA package manager handles three jobs for you:
1. Download code someone else wrote (a "package")
2. Track which version you're using
3. Resolve dependencies (packages that need OTHER packages)
Without one, you'd be manually downloading zip files and hoping nothing breaks. 😅
You run: npm install express
│
▼
npm checks package.json for existing rules
│
▼
npm talks to the npm Registry (the "store")
│
▼
Package + its dependencies downloaded
│
▼
Saved inside node_modules/
│
▼
package.json + package-lock.json updated
| File | Purpose | Should you edit it manually? |
|---|---|---|
package.json |
Your shopping list — what you want | Yes, sometimes |
package-lock.json |
Exact receipt — what you actually got (every sub-dependency, pinned) | No, never |
🧠 Why both? package.json says "I want express, version 4-ish." package-lock.json says "I got express 4.18.2, which needed body-parser 1.20.1, which needed..." — down to the last detail. This guarantees everyone on your team installs the exact same versions.
Versions look like MAJOR.MINOR.PATCH — e.g. 4.18.2
4 . 18 . 2
│ │ │
│ │ └── PATCH: bug fixes only
│ └───────── MINOR: new features, safe
└─────────────── MAJOR: breaking changes
| Symbol | Meaning | Example | Allows |
|---|---|---|---|
^4.18.2 |
Caret | Most common default | Updates MINOR + PATCH (4.x.x) |
~4.18.2 |
Tilde | More cautious | Updates PATCH only (4.18.x) |
4.18.2 |
Exact | Fully locked | No updates at all |
🧠 Rule of thumb: ^ is npm's default and usually fine. Use exact versions only when a package is known to break things on minor updates.
your-project/
├── node_modules/ ← the actual downloaded code (huge folder!)
├── package.json ← your shopping list
└── package-lock.json ← the exact receipt
There's also a global install location (npm install -g) — used for command-line tools you want available everywhere, not just one project (like nodemon or create-react-app).
⚠️ Never commit node_modules/ to GitHub. It's regenerated from package.json — that's the whole point of having a package manager!
| Tool | Speed | Disk usage | Notes |
|---|---|---|---|
| npm | Good | Higher | Comes built-in with Node.js — no extra install |
| yarn | Fast | Higher | Popular alternative, similar workflow |
| pnpm | Fastest | Lowest | Uses shared storage across projects (smart!) |
🧠 For beginners: stick with npm first since it's already installed with Node.js. Explore alternatives once you're comfortable.
- A package manager downloads and tracks external code for you
- package.json = what you want | package-lock.json = exactly what you got
- Semver (^ ~ exact) controls how much a package is allowed to update
- node_modules holds actual code — never commit it to Git
- npm comes free with Node.js; yarn/pnpm are alternatives
👉 Using npm in a Real Project (hands-on: init, install, scripts, npm ci vs install)
TechWithJuned · Learn → Execute → Build