adding-dependencies
riekelt/principal-engineer
Systematic framework for adding, vetting, updating, and removing dependencies with cost-aware decision-making.
What is adding-dependencies?
A structured approach to dependency management that prioritizes exhausting existing solutions before taking new ones. Use this skill whenever adding, updating, or removing any external dependency—packages, libraries, SDKs, GitHub actions, base images, or vendored code—to ensure each hire is justified, vetted, pinned, and recorded.
- Work through a seven-level ladder to exhaust existing solutions before taking new dependencies
- Vet new dependencies by assessing transitive cost, maintainer health, API surface, and security history
- Pin versions in lockfiles and treat updates as deliberate changes with test coverage
- Declare and honor the project's dependency posture (self-contained, curated allowlist, or vet-and-add)
- Record dependency decisions with rationale and removal conditions using decision-tracking
- Remove dependencies when their job disappears or when owning the code becomes cheaper than the hire
How to install adding-dependencies
npx skills add https://github.com/riekelt/principal-engineer --skill adding-dependencies- The principal-engineering skill should be installed first, as this skill builds on its foundations
How to use adding-dependencies
- 1.When considering a new dependency, work down the seven-level ladder: need itself, existing codebase, standard library, platform primitives, existing project dependencies, a few lines of your own, then new dependency only as a last resort
- 2.For any new dependency, cost the whole hire: transitive tree, install weight, license compatibility, security advisories, and supply-chain score
- 3.Check the pulse: recent releases, issue response time, and maintainer capacity; flag single-owner load-bearing packages as risks
- 4.Read the actual API surface and failure modes you will call, not just the README
- 5.Record the decision: what the dependency is for, what alternatives were weighed, and the condition for removal
- 6.Commit lockfiles and pin versions; treat updates as deliberate changes with their own commits and test runs
- 7.Remove dependencies when their job disappears or when owning the code is cheaper than the hire
Use cases
- Adding a new npm package or library to a project and deciding whether it's justified or if existing code can be reused
- Updating a major version of a load-bearing dependency and assessing breaking changes before committing
- Evaluating whether to vendor third-party code versus taking a transitive dependency already in the tree
- Removing an unmaintained dependency and planning the transition to owned code or a replacement
- Declaring a project's dependency tolerance policy when none exists and tracking the decision
- Principal engineers and tech leads setting dependency standards
- Backend and full-stack developers managing package trees and lockfiles
- Teams establishing or auditing dependency governance and security posture
- Anyone responsible for long-term maintainability of a codebase
adding-dependencies FAQ
Use it whenever you are about to add, update, vet, or remove any dependency—even tiny utility packages. Use it also when a project needs to declare its dependency posture. This is how trees grow responsibly, one decision at a time.
A seven-level priority list: (1) the need itself (is it real?), (2) existing code in this codebase, (3) the standard library, (4) platform primitives, (5) a transitive already in the tree, (6) a few lines of your own, and only then (7) a new external dependency. Stop at the first level that holds.
Before adding a dependency, assess its transitive cost, install weight, license, security history and advisories, release frequency, maintainer health (recent activity, issue response, merge capacity), and the actual API surface you will call. Prefer single-purpose tools over frameworks.
Unpinned versions and missing lockfiles are time bombs: they let the dependency's maintainer control your build at CI time. Pinning ensures reproducible builds and makes updates deliberate, testable changes rather than surprises.
If it is load-bearing, treat it as a risk item with an owner and a plan—never a hope. Consider vendoring it with clear provenance, owning the code yourself, or migrating to a maintained alternative. Remove it only when its job is truly gone.
Full instructions (SKILL.md)
Source of truth, from riekelt/principal-engineer.
name: adding-dependencies description: "Use when about to add, update, vet, or remove a dependency - a package, library, SDK, GitHub action, base image, or vendored code - or when a project's dependency posture needs declaring. Encodes the exhaust-what-you-have ladder, the vetting questions, and pin-and-prove updating. Use even for a tiny utility package: that is exactly how the tree grows."
Adding dependencies
REQUIRED BACKGROUND: the principal-engineering skill.
Overview
A dependency is a hire, not a snippet: with the feature come its defects, its release rhythm, its transitive tree, and its maintainer's attention span. Core principle: exhaust what you already have, vet what you take, pin what you took, and record why.
Exhaust what you already have
Work down the list and stop at the first level that holds; good answers often compose two levels, an existing dependency for the hard part with a few lines of your own around it:
- The need itself. Speculative need is no need; skip it and say so in a line.
- This codebase. A helper, type, or pattern a few files away does the job; reuse it. A second copy of something the repo already contains is the defect
keeping-one-source-of-truthbans for data. - The standard library. Read its index before installing a package that duplicates it.
- The platform. A database constraint over application code, a native control over a widget library, the runtime's own primitive over a wrapper.
- A dependency the project already carries. Its transitive tree is already paid for. Two boundaries. A transitive you start using is a NEW direct dependency: declare it, pin it to the already-resolved version, and vet it lightly since the code already ships. Check the class the reuse crosses: a dev-tool's transitive promoted into runtime shifts the cost to every consumer's install, not just CI.
- A few lines of your own. Owning twenty lines beats owning a stranger's repository, when twenty lines is truly all it takes.
- Only past all six: a new dependency, vetted below.
Vetting the one you take
- Cost the whole hire: the transitive tree it drags in, the install and build weight, the license against the project's, the security history (advisories, and a supply-chain score where a scanner runs).
- Check the pulse: recent releases, how issues get answered, how many people can merge. A load-bearing package with one exhausted owner is a risk you are choosing.
- Read the part you will call: the API surface you depend on and its failure modes, not the README's promises. Grounding applies to other people's code too.
- Prefer the tool with one job over the framework with forty; the other thirty-nine come along anyway, in weight and in attack surface.
- Record the decision. A new dependency is a decision: what it is for, what else was weighed, and the condition under which it leaves (via
recording-decisionswhere installed).
Dependency posture
Dependency tolerance is the project's to declare, like its risk tiers: fully self-contained (some apps rightly ban external code wholesale), a curated allowlist, or vet-and-add. The project's rules or CLAUDE.md state which; when nothing does, ask what the system must never depend on and treat the answer as the declaration. A posture is honored even when inconvenient; changing it is a recorded decision, not an npm install.
An undeclared posture is not a hard gate: when nobody can answer today, proceed under a stated assumed posture, record the assumption in the decision, and track the declaration question with an owner.
Pin and prove
- Commit lockfiles and pin versions; an unpinned
latestin CI or a base image is a time bomb with someone else's clock. - An update is a change like any other. Read the release notes BEFORE a major bump (breaking changes and migrations first). Run the tests against it (
testing-changes). Give a major bump its own commit so the blame trail stays readable; grouped patch bumps may travel together. - Vendored code carries its origin and version in the tree; you cannot update what you cannot date.
Removal
- A dependency whose job disappeared leaves in the same change that removed the job; a package kept "in case" is the speculative need from level 1, in reverse.
- When the dependency is down to one small call site, consider owning those lines instead.
- Unmaintained but load-bearing is a risk item with an owner and a plan, never a hope.
Common mistakes
- The tiny-utility reflex: trees do not grow by big decisions; they grow one small package at a time, each individually reasonable.
- A framework installed to call one function.
- Depending on a package's internals or private paths; only the public contract is a promise.
- Importing a transitive dependency as if it were yours: undeclared today, gone on the next lockfile refresh. If you need it, declare it.
- Adding a dependency to avoid reading the code already in the repo (level 2, skipped).
- Vendoring without provenance, then wondering which version the copy was.
- Updating everything at once, then bisecting to find which bump broke the build.
Related skills
More from riekelt/principal-engineer and the wider catalog.

grounding-before-coding
Map real code and data before writing—ground every claim in evidence before any change.

guarding-architecture
Encode structural invariants as named, enforced contracts to prevent architectural decay.

handling-failures
Enforce loud, explicit failure handling—no silent swallows, typed errors, or undocumented degradation.

keeping-one-source-of-truth
Enforce single ownership of every fact in code and data to prevent drift and duplication.

operating-safely
Safety guards for destructive operations, secrets handling, and concurrent-session work on live systems.

principal-engineering
Evidence-based engineering discipline for safe, verifiable changes to code, data, and infrastructure.