Yarn 04 ๐งถ Managing Dependencies and yarn.lock
Every dependency you add, upgrade, or remove triggers a two-file update: package.json records the intent (a version range like ^4.18.0), and yarn.lock records the reality (the exact version and integrity hash that was resolved). The manifest is what you write; the lockfile is what Yarn writes. Understanding this division is the difference between a project that installs reproducibly on every machine and one that drifts silently until something breaks in production.
Key point: package.json declares what you want; yarn.lock guarantees what you get. Never edit the lockfile by hand, always commit it to version control, and treat its changes as the authoritative record of every resolved dependency.
Why yarn.lock exists
The version drift problem. Without a lockfile, yarn install resolves version ranges against the registry at install time. If package.json says "express": "^4.18.0", a fresh install six months later might fetch 4.19.2 while another developer installed 4.18.1 last week. The code runs on different dependency trees, and the bug that appears on one machine but not the other has no obvious cause. The lockfile eliminates this by pinning every transitive dependencyโnot just your direct dependenciesโto an exact version and integrity hash .
The transitive explosion problem. A mid-sized project with 20 direct dependencies can easily have 300 transitive dependencies. You cannot list them all in package.json, and you should not try. The lockfile captures the entire resolved tree, including the exact version of every dependency-of-a-dependency, so the full graph is reproducible without manual intervention .
The CI consistency problem. A CI runner starts from a clean checkout with no cached packages. If the lockfile is not committed, CI resolves version ranges independently and may install versions that differ from what developers tested locally. Committing yarn.lock ensures CI and local environments install byte-identical dependency trees .
The install behavior contract. Yarn’s install logic is strict: if yarn.lock exists and satisfies every range in package.json, the exact locked versions are installed and the lockfile is left unchanged. Only when the lockfile is missing or insufficient does Yarn resolve new versions and update the lockfile . The --frozen-lockfile flag goes further: it fails the install if the lockfile would need to change, making CI builds deterministic .
The trade-off. Committing yarn.lock to version control means merge conflicts when two branches modify dependencies. The resolution is always to regenerate, never to hand-merge. The cost is occasional conflict resolution; the benefit is every other install for the life of the project.
a. Adding, upgrading, and removing dependencies
Yarn’s dependency commands modify both package.json and yarn.lock in a single operation. The CLI is the only safe way to manage dependenciesโediting package.json manually and running yarn install works, but it risks version range typos and inconsistent lockfile updates.
# Add a dependency to "dependencies"
yarn add express
# Add to "devDependencies"
yarn add --dev typescript
# Add a specific version
yarn add express@4.18.2
# Add a tag (e.g., beta)
yarn add react@beta
Each yarn add command writes the version range to package.json and resolves the exact version into yarn.lock . The range uses the caret (^) by default, allowing patch and minor updates but not major. For stricter control, specify a tilde (~1.2.3, patch updates only) or an exact version (1.2.3, no updates).
Upgrading follows the same pattern. yarn upgrade express upgrades within the range declared in package.json. To upgrade beyond the declared rangeโfor example, from ^4.18.0 to ^5.0.0โyou either run yarn upgrade express@latest (which may modify the range in package.json) or explicitly add the new version range .
# Upgrade within declared range
yarn upgrade express
# Upgrade to latest, potentially changing the range
yarn upgrade express@latest
# Upgrade to a specific version
yarn upgrade express@5.0.0
# Remove a dependency
yarn remove express
Removal deletes the entry from package.json and prunes the package and any dependencies that are no longer required from yarn.lock .
b. The lockfile: format, behavior, and integrity
The yarn.lock file is auto-generated and should never be edited directly. It contains a # THIS IS AN AUTOGENERATED FILE header, followed by entries for every resolved package. Each entry maps a version range to an exact version, a registry URL, and an integrity hash .
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
express@^4.18.0:
version "4.18.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#..."
integrity sha512-...
dependencies:
accepts "~1.3.8"
body-parser "1.20.1"
The dependencies block inside each entry lists the package’s own dependencies, which are also resolved and recorded. This recursive structure captures the entire tree without requiring a separate lockfile in every package’s directory. Yarn reads only the top-level yarn.lock during install .
The integrity hash is the security layer. If a registry serves a package whose content does not match the hash recorded in the lockfile, Yarn rejects the install. This prevents supply-chain attacks where a malicious actor replaces a package version with modified code .
c. Lockfile conflicts and regeneration
Merge conflicts in yarn.lock are common when two branches modify dependencies. The file is not designed for manual mergingโresolving conflict markers by hand is error-prone and can produce a lockfile that no longer matches package.json.
The correct resolution is regeneration: resolve package.json first, then delete the conflicted yarn.lock, and run yarn install to generate a fresh lockfile from the merged manifest.
# After resolving package.json conflicts
git checkout --theirs yarn.lock # or delete the file
yarn install # regenerate from package.json
git add yarn.lock
Yarn Classic (1.x) had built-in merge conflict handling that attempted to resolve lockfile conflicts during install, but this behavior is not guaranteed across versions and should not be relied upon . The safe approach in every case is to regenerate from the manifest.
In CI, yarn install --frozen-lockfile (or --immutable in Yarn Berry) prevents accidental lockfile updates. If the lockfile is out of sync with package.json, the install fails rather than silently resolving new versions .
Complete Example Session
# ============================================
# PART 1: ADDING A RUNTIME DEPENDENCY
# ============================================
yarn add express
# package.json: "express": "^4.18.2"
# yarn.lock: express@^4.18.2 resolved to 4.18.2
# ============================================
# PART 2: ADDING A DEV DEPENDENCY
# ============================================
yarn add --dev typescript
# package.json: "typescript": "^5.4.5" (devDependencies)
# yarn.lock: typescript@^5.4.5 resolved to 5.4.5
# ============================================
# PART 3: ADDING A SPECIFIC VERSION
# ============================================
yarn add lodash@4.17.21
# package.json: "lodash": "4.17.21" (exact, no caret)
# yarn.lock: lodash@4.17.21 resolved to 4.17.21
# ============================================
# PART 4: INSPECTING THE LOCKFILE
# ============================================
grep -A 3 'express@' yarn.lock
# express@^4.18.0:
# version "4.18.2"
# resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz"
# integrity sha512-...
# ============================================
# PART 5: UPGRADING WITHIN RANGE
# ============================================
yarn upgrade express
# Upgrades to latest 4.x.x, lockfile updated
# package.json range unchanged: ^4.18.0
# ============================================
# PART 6: UPGRADING BEYOND RANGE
# ============================================
yarn upgrade express@latest
# May upgrade to 5.x.x
# package.json range may change to ^5.0.0
# ============================================
# PART 7: REMOVING A DEPENDENCY
# ============================================
yarn remove lodash
# package.json: lodash entry removed
# yarn.lock: lodash and its unique transitive deps pruned
# ============================================
# PART 8: FROZEN LOCKFILE FOR CI
# ============================================
yarn install --frozen-lockfile
# Fails if yarn.lock would need to change
# Ensures CI uses exactly the committed lockfile
# ============================================
# PART 9: REGENERATING AFTER CONFLICT
# ============================================
# Resolve package.json conflicts first
git checkout --theirs yarn.lock
yarn install
git add yarn.lock
# Fresh lockfile generated from merged package.json
The nine parts above cover the full lifecycle: adding runtime and dev dependencies, pinning exact versions, inspecting the lockfile, upgrading within and beyond ranges, removing packages, enforcing frozen lockfiles in CI, and regenerating after merge conflicts. Each command modifies both files, and the lockfile records the resolved outcome.
Quick Reference
Dependency Commands
| Command | Effect |
|---|---|
yarn add <pkg> | Add to dependencies |
yarn add --dev <pkg> | Add to devDependencies |
yarn add <pkg>@<version> | Add exact version |
yarn upgrade <pkg> | Upgrade within declared range |
yarn upgrade <pkg>@latest | Upgrade to latest, may change range |
yarn remove <pkg> | Remove from manifest and lockfile |
Lockfile Behavior
| Scenario | Yarn Action |
|---|---|
Lockfile satisfies package.json | Install locked versions, no lockfile change |
| Lockfile missing or insufficient | Resolve new versions, update lockfile |
--frozen-lockfile | Fail if lockfile would change |
--no-lockfile | Do not read or generate lockfile |
Version Range Syntax
| Range | Meaning |
|---|---|
^1.2.3 | >=1.2.3 <2.0.0 |
~1.2.3 | >=1.2.3 <1.3.0 |
1.2.3 | Exactly 1.2.3 |
latest | Latest stable (resolved at install) |
Best Practices
โ Do This:
yarn add express
<!-- CLI updates both manifest and lockfile --> # โ
git add yarn.lock
<!-- Commit lockfile to version control --> # โ
yarn install --frozen-lockfile
<!-- CI enforces exact lockfile match --> # โ
yarn upgrade express@latest
<!-- Explicit upgrade when changing major version --> # โ
โ Don’t Do This:
# Edit yarn.lock manually
<!-- Lockfile is auto-generated --> # โ
# Ignore lockfile changes in PR
<!-- Dependency changes must be reviewed --> # โ
# Delete lockfile "to fix" install issues
<!-- Regenerate from package.json instead --> # โ
# Use --no-lockfile in CI
<!-- Produces non-reproducible builds --> # โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Different versions across machines | Lockfile not committed | Commit yarn.lock to git |
| CI installs different versions | Lockfile absent or out of sync | Use --frozen-lockfile in CI |
| Merge conflict in lockfile | Two branches changed deps | Regenerate: delete, yarn install |
| Manual lockfile edit breaks install | Syntax error or stale integrity hash | Revert, regenerate from package.json |
| Transitive dependency not pinned | Lockfile missing entry | Run yarn install to resolve and write |
| Upgrade not applied | Range constraint blocks upgrade | Use @latest or update range explicitly |
Real-World Examples
1. Add Runtime Dependency
yarn add express
2. Add Dev Dependency
yarn add --dev jest
3. Pin Exact Version
yarn add lodash@4.17.21
4. Upgrade Within Range
yarn upgrade axios
5. Upgrade to Latest
yarn upgrade axios@latest
6. Remove Dependency
yarn remove lodash
7. Frozen Install in CI
yarn install --frozen-lockfile
8. Regenerate After Conflict
git checkout --theirs yarn.lock && yarn install
9. Inspect Locked Version
grep -A 2 'axios@' yarn.lock
10. Check for Outdated Packages
yarn outdated
Visual
Two-File Update on Every Command
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ yarn add express โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ package.json (intent) โ
โ โ
โ "dependencies": { โ
โ "express": "^4.18.0" โ
โ } โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ yarn.lock (reality) โ
โ โ
โ express@^4.18.0: โ
โ version "4.18.2" โ
โ resolved "https://..." โ
โ integrity sha512-... โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The manifest records the range; the lockfile records the resolved truth. Both must be committed.
Install Decision Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ yarn install โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ yarn.lock exists and satisfies โ
โ package.json ranges? โ
โโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโ
โ YES โ NO
โผ โผ
โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
โ Install locked โ โ Resolve new โ
โ versions exactly โ โ versions, update โ
โ Lockfile unchanged โ โ yarn.lock โ
โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ --frozen-lockfile? โ
โ โโ YES: fail if lockfile would change โ
โ โโ NO: allow lockfile update โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The --frozen-lockfile flag is the CI safeguard. It converts “Yarn would update the lockfile” into a hard failure.
Summary
| Item | Value |
|---|---|
| Manifest records | Version ranges (intent) |
| Lockfile records | Exact versions and hashes (reality) |
| Add command | yarn add <pkg> |
| Remove command | yarn remove <pkg> |
| Upgrade command | yarn upgrade <pkg> |
| CI install flag | --frozen-lockfile |
| Lockfile editing | Never; auto-generated only |
| Commit to VCS | Always |
| Conflict resolution | Regenerate from package.json |
| Lockfile scope | Entire transitive tree |
Key takeaways:
- Two files, two purposes.
package.jsondeclares version ranges;yarn.lockpins exact resolved versions and integrity hashes . - The CLI is the only safe editor.
yarn add,upgrade, andremoveupdate both files atomically. Manual edits risk inconsistency . - Commit the lockfile. Without it, installs are non-reproducible across machines and CI environments .
--frozen-lockfileenforces determinism. In CI, this flag fails the build if the lockfile would need to change, preventing silent version drift .- Merge conflicts require regeneration. Resolve
package.json, delete the conflicted lockfile, and runyarn install. Never hand-merge conflict markers . - Integrity hashes are security. Yarn verifies every package against the hash recorded in the lockfile, rejecting tampered packages .
Remember: package.json is what you ask for; yarn.lock is what you get. The manifest travels through code review as a declaration of intent. The lockfile travels alongside it as the proof of resolution. Commit both, trust neither to manual editing, and let --frozen-lockfile enforce the contract in every CI run.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!