← Back to Desktop app developers

Skill · Version 1.0.0

Controlled dependency upgrades

Updates one coherent dependency group at a time while limiting adaptations to required compatibility changes and preserving recovery options.

controlled-dependency-upgrades

Team members using this skill

See how it helps

When is it useful?

Use when a dependency update should remain narrow, understandable and reversible.

What does it help you do?

  • Groups coupled packages and diagnoses constraints before changing versions.
  • Regenerates the existing lockfile with the project's native package manager.
  • Adapts required call sites while keeping unrelated refactoring outside the change.

Try asking this

Upgrade our test runner and matching plugins without refreshing unrelated packages or bypassing peer conflicts. Reinstall from the resulting lockfile and run the affected tests. Explain each changed dependency and any plugin API adjustment required for compatibility.

Detailed documentation

Read the skill’s original instructions, examples and checks in their authored language.

Details…Show less

Controlled Dependency Upgrades

When to Use

Use when planning or implementing any dependency update, framework bump, SDK migration, or lockfile refresh.

Core Discipline: Separate Upgrade Mechanics from General Refactoring

  • Tight Variable Control: Upgrade one logical package—or one coupled package family (@angular/*, symfony/*, @prisma/client + prisma, vite + @vitejs/*)—at a time. Never run a blanket upgrade across unrelated dependencies.
  • Boundary with Structural Refactoring: Implement the exact code, type, and configuration adaptations required by the new version contract. Keep structural refactoring or feature changes out of the upgrade diff.

Step-by-Step Execution Workflow

1. Group Coupled Packages Coherently
  • Identify sibling packages sharing an internal version contract:
    • Core library + official plugins/adapters (eslint + @typescript-eslint/*, vitest + @vitest/*, next + eslint-config-next, laravel/framework + first-party laravel/*, pydantic + pydantic-settings, tokio + tokio-util).
    • Runtime package + matching @types/* package (or remove @types/<pkg> when the upgraded version ships built-in .d.ts declarations).
  • Align versions across workspace manifests in monorepos so a single upgrade does not split the internal graph.
2. Update Manifests & Regenerate Lockfile with the Native Tool
  • Edit version ranges deliberately, preserving the project's convention (^, ~, exact pin, or workspace catalog).
  • Use the repository's exact package manager to update only the targeted subgraph:
    • Never delete the entire lockfile (rm package-lock.json / rm pnpm-lock.yaml / rm composer.lock) as a generic fix—doing so silently moves hundreds of unrelated transitive packages.
    • Never pass --force, --legacy-peer-deps, or --ignore-platform-reqs to mask a conflict without diagnosing the root incompatibility.
3. Adapt Call Sites, Configurations, and Types
  • Search the repository for all static/dynamic imports, DI tokens, config files, and CLI invocations of the upgraded package.
  • Migrate removed or deprecated APIs to the upstream-recommended replacement:
    • Update function signatures, option keys, async/Promise handling, error boundaries, and build/linter/ORM/CI runtime configs.
    • Fix new TypeScript, PHPStan, Mypy, or Rust compiler errors properly—never suppress upgrade errors with @ts-ignore, any, @phpstan-ignore, or # type: ignore.
4. Manage Temporary Overrides, Shims & Patches Explicitly
  • When a lagging transitive dependency requires a temporary overrides / resolutions pin or a thin compatibility adapter, keep it minimal and scoped to the specific parent package, and document the upstream blocker plus the exact removal condition (e.g., "Remove once pkg-x >= 3.2.0 ships").
  • Never hand-edit node_modules/, vendor/, or .venv/; use tracked patch tooling (pnpm patch, patch-package, composer-patches, [patch.crates-io]) when a vendor patch is unavoidable.
5. Update Tests Only for Documented Contract Changes
  • Distinguish between tests failing due to an intentional upstream contract change (update test assertions/mocks to match the documented new behavior) vs. an actual application regression (fix the application integration code). Never run --updateSnapshot (-u) without reviewing every diff line.

Included files 1

  • SKILL.md