dependency-upgrade
Manage major dependency version upgrades with compatibility analysis, staged rollout, and comprehensive testing. Use when upgrading framework versions, updating major dependencies, or managing breaking changes in libraries.
$ Instalar
git clone https://github.com/rohanod/ai-agent-thingies /tmp/ai-agent-thingies && cp -r /tmp/ai-agent-thingies/skills/dependency-upgrade ~/.claude/skills/ai-agent-thingies// tip: Run this command in your terminal to install the skill
name: dependency-upgrade description: Manage major dependency version upgrades with compatibility analysis, staged rollout, and comprehensive testing. Use when upgrading framework versions, updating major dependencies, or managing breaking changes in libraries.
Dependency Upgrade (Bun Edition)
Master major dependency version upgrades with Bun's blazing-fast package manager. This skill emphasizes one-at-a-time upgrades to prevent cascading failures and maintain control over breaking changes.
When to Use This Skill
- Upgrading major framework versions (React 17→18, Next.js 13→14, etc.)
- Updating security-vulnerable dependencies
- Modernizing legacy dependencies
- Resolving dependency conflicts
- Planning incremental upgrade paths
- Managing breaking changes systematically
Core Philosophy: One at a Time
NEVER upgrade all dependencies at once. This leads to:
- Impossible-to-debug failures
- Mixed breaking changes
- Unclear rollback points
- Test failures you can't isolate
ALWAYS upgrade one major dependency at a time:
- Pick ONE package
- Upgrade it
- Run tests
- Commit
- Repeat
Semantic Versioning Refresher
MAJOR.MINOR.PATCH (e.g., 2.3.1)
MAJOR: Breaking changes - requires code changes
MINOR: New features - backward compatible
PATCH: Bug fixes - backward compatible
^2.3.1 = >=2.3.1 <3.0.0 (allows minor & patch updates)
~2.3.1 = >=2.3.1 <2.4.0 (allows only patch updates)
2.3.1 = exactly 2.3.1 (locked version)
Dependency Discovery
Check What's Outdated (Fast!)
# See all outdated dependencies
bun outdated
# Output format:
# Package Current Update Latest
# react 17.0.2 17.0.2 18.2.0
# typescript 4.9.5 4.9.5 5.3.3
Filter by Package Pattern
# Check specific packages
bun outdated react react-dom
# Check all @types packages
bun outdated '@types/*'
# Check all eslint-related packages
bun outdated 'eslint*'
# Exclude certain patterns (! prefix)
bun outdated '!@types/*'
Workspace Support (Monorepos)
# Check one workspace
bun outdated --filter='@myapp/ui'
# Check multiple workspaces
bun outdated --filter='@myapp/*'
# Check all workspaces recursively
bun outdated --recursive
Upgrade Strategies
Strategy 1: Interactive Mode (Recommended for Beginners)
# Bun's built-in interactive selector
bun update -i
# This shows a checkbox list:
# ◯ react (17.0.2 → 18.2.0)
# ◯ typescript (4.9.5 → 5.3.3)
# ◯ eslint (8.0.0 → 8.56.0)
#
# Select ONE, press Enter, test, commit, repeat
Strategy 2: Manual One-at-a-Time (Recommended for Complex Upgrades)
# Step 1: Check what's available
bun outdated
# Step 2: Upgrade ONE package to latest (major version)
bun update react --latest
# Step 3: Install
# (Already installed by update command)
# Step 4: Run tests
bun test
# Step 5: Check types
bun tsc --noEmit
# Step 6: Run build
bun run build
# Step 7: If all pass, commit
git add package.json bun.lockb
git commit -m "chore: upgrade react to 18.2.0"
# Step 8: Repeat for next package
bun update react-dom --latest
Strategy 3: Using npm-check-updates (More Control)
# See what updates are available (no changes)
bunx npm-check-updates
# Interactive mode - choose which to upgrade
bunx npm-check-updates -i
# Update ONE package in package.json only
bunx npm-check-updates -u react
# Then install it
bun install
# Test, commit, repeat
Upgrade Workflow (Step-by-Step)
Phase 1: Preparation
# 1. Ensure clean git state
git status
# 2. Create upgrade branch
git checkout -b upgrade/react-18
# 3. Baseline: Run full test suite
bun test
bun run build
# 4. Tag current state (for easy rollback)
git tag pre-react-18-upgrade
# 5. Check what's outdated
bun outdated react react-dom
Phase 2: Research Breaking Changes
# Read the changelog (CRITICAL!)
# For React: https://react.dev/blog/2022/03/29/react-v18
# Common sources:
# - Official changelog/migration guide
# - GitHub releases
# - BREAKING.md or MIGRATION.md files
# Example: Check React changelog
bunx open-cli https://github.com/facebook/react/blob/main/CHANGELOG.md
Questions to answer:
- What APIs were deprecated/removed?
- What new APIs replace old ones?
- Are there codemods available?
- Do peer dependencies need updating?
Phase 3: Execute Upgrade (ONE Package)
# Upgrade the package
bun update react --latest
# Check what version was installed
bun pm ls react
# Expected: react@18.2.0 (or latest)
Phase 4: Fix Breaking Changes
# Run TypeScript to find type errors
bun tsc --noEmit
# Common React 17→18 changes:
# - ReactDOM.render → ReactDOM.createRoot
# - Automatic batching changes
# - Stricter StrictMode
# - New hooks (useId, useTransition, etc.)
Phase 5: Test Thoroughly
# Run unit tests
bun test
# Run type check
bun tsc --noEmit
# Run build
bun run build
# Check bundle size (important!)
bunx size-limit
# Run linter
bun run lint
# If you have E2E tests
bun run test:e2e
Phase 6: Commit and Continue
# If all tests pass
git add package.json bun.lockb
git commit -m "chore: upgrade react to 18.2.0
- Updated from 17.0.2 to 18.2.0
- Migrated ReactDOM.render to createRoot
- All tests passing"
# If tests fail
git reset --hard
# Fix issues, then commit
# Move to next package (e.g., react-dom)
bun update react-dom --latest
# Repeat phases 4-6
Advanced Techniques
Checking Peer Dependencies
# Bun automatically checks peer dependencies
# If there's a conflict, you'll see:
# warn: "react-dom@18.2.0" has peer dependency "react@^18.2.0"
# To see full dependency tree
bun pm ls react
bun pm ls --all | grep react
# Check why a package is installed
bun why react
Dealing with Transitive Dependencies
# See what depends on an outdated package
bun why old-package
# If a dependency of a dependency is outdated:
# 1. Check if updating the parent fixes it
bun update parent-package --latest
# 2. If not, use resolutions in package.json
{
"resolutions": {
"vulnerable-package": "^2.0.0"
}
}
Using Codemods for Automated Fixes
# React upgrades
bunx react-codemod \
--parser tsx \
--transform new-jsx-transform \
src/
# Rename unsafe lifecycles
bunx react-codemod \
--parser tsx \
--transform rename-unsafe-lifecycles \
src/
# Next.js upgrades
bunx @next/codemod <transform>
# General purpose codemod runner
bunx jscodeshift -t transform.js src/
Batch Checking (Not Upgrading!) Multiple Packages
# Check React ecosystem together
bun outdated react react-dom @types/react @types/react-dom
# Check TypeScript ecosystem
bun outdated typescript @types/node ts-node
# But STILL upgrade one at a time!
Security Audits
# Check for vulnerabilities
bun audit
# Auto-fix vulnerabilities (safe minor/patch updates)
bun audit --fix
# For major version security fixes, upgrade manually
bun outdated
bun update vulnerable-package --latest
Rollback Strategies
Quick Rollback
# If you tagged before upgrading
git reset --hard pre-react-18-upgrade
# Restore dependencies
bun install
Selective Rollback
# Revert package.json changes
git checkout HEAD -- package.json bun.lockb
# Reinstall
bun install
Emergency Rollback (Production)
# Deploy previous version
git revert <upgrade-commit-hash>
# Or redeploy from previous tag
git checkout v1.2.3
Common Upgrade Patterns
Pattern 1: Framework + Ecosystem
# Example: Upgrading React
# Order matters! Upgrade in dependency order:
# 1. TypeScript first (if used)
bun update typescript --latest
bun tsc --noEmit
git commit -am "chore: upgrade typescript to 5.3.3"
# 2. React core
bun update react --latest
bun test
git commit -am "chore: upgrade react to 18.2.0"
# 3. React DOM (must match React version)
bun update react-dom --latest
bun test
git commit -am "chore: upgrade react-dom to 18.2.0"
# 4. React types
bun update @types/react @types/react-dom --latest
bun tsc --noEmit
git commit -am "chore: upgrade react types"
# 5. Testing library
bun update @testing-library/react --latest
bun test
git commit -am "chore: upgrade testing library"
# 6. React Router (if used)
bun update react-router-dom --latest
bun test
git commit -am "chore: upgrade react-router-dom to 6.x"
Pattern 2: Build Tools
# Example: Vite upgrade
bun update vite --latest
bun update @vitejs/plugin-react --latest
bun run build
git commit -am "chore: upgrade vite and plugins"
Pattern 3: Linting/Formatting
# These are usually safe to upgrade together
# (but still test!)
bun update eslint --latest
bun update prettier --latest
bun run lint
git commit -am "chore: upgrade linting tools"
Testing Checklist
After EACH upgrade:
- [ ] TypeScript compiles (`bun tsc --noEmit`)
- [ ] Unit tests pass (`bun test`)
- [ ] Build succeeds (`bun run build`)
- [ ] Dev server works (`bun run dev`)
- [ ] Linter passes (`bun run lint`)
- [ ] No console errors/warnings
- [ ] Bundle size acceptable
- [ ] Key features tested manually
- [ ] E2E tests pass (if applicable)
Tools Comparison
Bun Native (Recommended)
Pros:
- Blazing fast (written in Zig)
- Built-in, no extra dependencies
- Interactive mode with
-i --latestflag for major versions- Workspace support
- Clean, readable output
Cons:
- Newer than npm-check-updates
- Fewer configuration options
Use for: Most upgrades, especially in Bun projects
bun update -i
npm-check-updates (Alternative)
Pros:
- Battle-tested, mature
- Extensive filtering options
.ncurc.jsconfig files- Large community
- Advanced target modes (minor, patch, latest)
Cons:
- Extra dependency (though bunx makes it easy)
- Slightly slower
Use for: Complex filtering needs, legacy projects, or when you need .ncurc configs
bunx npm-check-updates -i
Recommendation: Default to bun update -i for simplicity and speed. Use npm-check-updates when you need advanced filtering or config files.
Automation (With Caution!)
Automated Checks (Safe)
# .github/workflows/dependencies.yml
name: Dependency Check
on:
schedule:
- cron: '0 9 * * 1' # Monday 9am
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- run: bun outdated
Dependabot for Minor/Patch Only
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
# Limit concurrent PRs
open-pull-requests-limit: 3
# Group minor/patch updates
groups:
dependencies:
patterns:
- "*"
update-types:
- "minor"
- "patch"
CRITICAL: Configure Dependabot to NEVER auto-merge major updates. Always review and test major version changes manually.
Troubleshooting
Peer Dependency Conflicts
# Error: "react-router-dom@6.0.0" has peer dependency "react@^18.0.0"
# Solution 1: Update peer dependency first
bun update react --latest
# Solution 2: Use overrides in package.json (last resort)
{
"overrides": {
"react": "^18.0.0"
}
}
Lock File Conflicts
# If bun.lockb is corrupted
rm bun.lockb
bun install
# If git merge conflict in lockfile
# Accept one version, then regenerate:
git checkout --theirs bun.lockb
bun install
Bundle Size Increased Significantly
# Check what increased
bunx vite-bundle-visualizer
# Or for Next.js
bunx @next/bundle-analyzer
# Consider:
# - Tree shaking configuration
# - Dynamic imports
# - Check if new version includes more by default
# - Rolling back if increase is unacceptable
Breaking Changes Are Too Complex
# Don't skip major versions!
# Go incrementally: 16 → 17 → 18
# Example: React 16 → 18
bun update react@17 react-dom@17 --latest
bun test
git commit -am "chore: upgrade react to 17"
bun update react@18 react-dom@18 --latest
bun test
git commit -am "chore: upgrade react to 18"
Tests Pass Locally but Fail in CI
# Clear CI cache
# In GitHub Actions: delete and re-create caches
# Ensure lockfile is committed
git add bun.lockb
git commit -m "chore: update lockfile"
# Use frozen lockfile in CI
# In CI script:
bun install --frozen-lockfile
Useful Scripts (Add to package.json)
{
"scripts": {
"deps:check": "bun outdated",
"deps:interactive": "bun update -i",
"deps:audit": "bun audit",
"deps:why": "bun why",
"upgrade:react": "bun update react react-dom @types/react @types/react-dom --latest",
"upgrade:next": "bun update next @types/node --latest",
"upgrade:typescript": "bun update typescript @types/node --latest",
"test:upgrade": "bun test && bun tsc --noEmit && bun run build && bun run lint",
"ncu": "bunx npm-check-updates",
"ncu:interactive": "bunx npm-check-updates -i",
"ncu:check": "bunx npm-check-updates --target latest"
}
}
Then use like:
# Interactive upgrade
bun run deps:interactive
# Check React updates
bun run deps:check react react-dom
# Full upgrade test suite
bun run test:upgrade
Common Dependency Upgrade Orders
React Application
1. typescript → TypeScript compiler
2. react → Core React library
3. react-dom → React DOM bindings
4. @types/react, @types/react-dom → Type definitions
5. @testing-library/react → Testing utilities
6. react-router-dom → Routing (if used)
7. Build tools (vite, webpack, etc.)
8. Linting (eslint, prettier)
Next.js Application
1. typescript → TypeScript compiler
2. next → Next.js framework
3. react, react-dom → Updated by Next.js peer deps
4. @types/node, @types/react → Type definitions
5. Build plugins (if any)
6. Linting tools
Node.js/Bun Backend
1. typescript → TypeScript compiler
2. Core framework (express, fastify, hono, etc.)
3. Database drivers
4. Authentication libraries
5. Type definitions
6. Testing tools
7. Linting tools
Real-World Example: React 17 → 18
# Step 1: Check current state
bun outdated react react-dom
# Output:
# react 17.0.2 17.0.2 18.2.0
# react-dom 17.0.2 17.0.2 18.2.0
# Step 2: Read migration guide
# https://react.dev/blog/2022/03/29/react-v18
# Step 3: Create branch
git checkout -b upgrade/react-18
git tag pre-react-18
# Step 4: Upgrade React
bun update react --latest
# ✓ Installed react@18.2.0
# Step 5: Upgrade React DOM (must match)
bun update react-dom --latest
# ✓ Installed react-dom@18.2.0
# Step 6: Update types
bun update @types/react @types/react-dom --latest
# Step 7: Fix breaking changes
# Change in src/index.tsx:
# OLD:
# ReactDOM.render(<App />, document.getElementById('root'))
#
# NEW:
# const root = ReactDOM.createRoot(document.getElementById('root')!)
# root.render(<App />)
# Step 8: Test
bun test
bun tsc --noEmit
bun run build
bun run dev # Manual testing
# Step 9: Commit
git add .
git commit -m "chore: upgrade react to 18.2.0
- Updated react and react-dom from 17.0.2 to 18.2.0
- Migrated from ReactDOM.render to createRoot API
- Updated type definitions
- All tests passing"
# Step 10: Update testing library (if needed)
bun update @testing-library/react --latest
bun test
git commit -am "chore: upgrade testing library for React 18"
Best Practices Summary
- One at a Time: Never upgrade multiple major dependencies simultaneously
- Read Changelogs: Always read breaking changes before upgrading
- Test Thoroughly: Run full test suite after EACH upgrade
- Commit Frequently: One commit per major dependency
- Use Branches: Create feature branches for upgrade work
- Tag Before: Tag your repo before starting upgrades
- Check Peer Deps: Ensure peer dependencies are compatible
- Watch Bundle Size: Monitor bundle size changes
- Incremental Majors: Go 16→17→18, not 16→18
- Have Rollback Plan: Know how to revert quickly
- Use
--latest: Required to upgrade beyond semver range - Interactive Mode: Use
-ito see and select updates
The Golden Rule
If you upgrade 10 packages and tests fail, you have no idea which one caused it.
If you upgrade 1 package and tests fail, you know exactly what to fix.
Always upgrade dependencies one at a time. Your future self will thank you.
Repository
