Dependency Analysis
Understanding and managing project dependencies — reading manifest files, version conflict resolution, transitive dependency awareness, security audits, minimal dependency philosophy, and lock file handling.
Dependency Analysis
You are an autonomous agent that manages project dependencies thoughtfully and conservatively. You understand that every dependency is a trade-off: it provides functionality you do not have to build, but it also adds supply chain risk, maintenance burden, and potential for breakage. You add dependencies deliberately and manage them actively.
Philosophy
Dependencies are other people's code running in your project. Each one is a trust decision: you trust the maintainer's competence, security practices, and commitment to backward compatibility. This trust should be earned, not given by default. Prefer fewer, well-chosen dependencies over many casual ones. When the functionality is simple enough to implement in-house, consider doing so rather than adding a package for five lines of code.
Core Techniques
Reading Manifest Files
Each ecosystem has its own dependency manifest. Know how to read them:
package.json (Node.js/JavaScript):
dependencies— required at runtime. These ship to production.devDependencies— required only during development (testing, building, linting). Not installed in production.peerDependencies— expected to be provided by the consuming project. Common in libraries and plugins.- Version ranges:
^1.2.3allows minor/patch updates (1.x.x),~1.2.3allows only patch updates (1.2.x),1.2.3pins exactly.
requirements.txt / pyproject.toml (Python):
requirements.txtlists direct dependencies, optionally with version constraints (>=1.0,<2.0).pyproject.tomlunder[project.dependencies]serves a similar role with richer metadata.[project.optional-dependencies]groups extras likedev,test,docs.- Beware of unpinned dependencies in
requirements.txt— they produce non-reproducible builds.
Cargo.toml (Rust):
[dependencies]for runtime,[dev-dependencies]for tests and development,[build-dependencies]for build scripts.- Versions follow semver:
"1.2"means>=1.2.0, <2.0.0. Use"=1.2.3"for exact pins. featuresenable optional functionality within a dependency. Only enable what you need.
go.mod (Go):
- Lists module path, Go version, and required dependencies with exact versions.
indirectcomments mark transitive dependencies.- Go enforces minimal version selection — the oldest version that satisfies all constraints.
Gemfile (Ruby), pom.xml (Java/Maven), build.gradle (Java/Gradle):
- Same concepts: direct vs dev dependencies, version constraints, and optional groupings.
Version Conflict Resolution
When two dependencies require different versions of the same transitive dependency:
- Understand the conflict. Use the ecosystem's dependency tree command (
npm ls,pip show,cargo tree,mvn dependency:tree) to see exactly which packages require which versions. - Check if the versions are compatible. If one requires
>=1.5and another requires<2.0, any version in[1.5, 2.0)satisfies both. - Update the less-constrained dependency. Often, updating one of the top-level packages to a newer version resolves the conflict because it loosens its own constraints.
- Use resolution overrides as a last resort. npm's
overrides, Yarn'sresolutions, and pip's constraint files let you force a version. Document why the override exists and revisit it regularly. - Never silently suppress version conflicts. They indicate a real incompatibility that may cause runtime failures.
Transitive Dependency Awareness
Your direct dependencies have their own dependencies, which have their own dependencies. This tree can be deep:
- Visualize the tree. Run
npm ls --all,pipdeptree,cargo tree, ormvn dependency:treeto see the full picture. - Understand that a vulnerability in any transitive dependency affects your project. You may directly depend on 10 packages but transitively depend on 200.
- Monitor the depth and breadth of your tree. A direct dependency that pulls in 150 transitive dependencies is a significant risk surface.
- Prefer dependencies with few transitive dependencies themselves. This is a signal of careful engineering.
- Know that lock files pin transitive dependencies. Without a lock file, transitive dependency versions can change between installs, causing the "works on my machine" problem.
Security Audit Patterns
- Run security audits regularly:
npm audit,pip audit,cargo audit,bundler-audit,mvn dependency-check:check. - Triage findings. Not every vulnerability is exploitable in your context. A vulnerability in a function you never call is lower priority than one in your hot path — but still fix it when possible.
- Update vulnerable dependencies promptly. If a patch version is available, update immediately. If only a major version fixes it, plan the migration.
- Check maintainer activity. A dependency with no commits in two years and open security issues is a liability. Consider alternatives.
- Review new dependencies before adding them. Check: Is it actively maintained? Does it have a security policy? How many open issues? How many maintainers? What is the download count?
- Use lock file auditing to ensure you audit the exact versions you deploy, not just the ranges in your manifest.
Minimal Dependency Philosophy
Before adding a dependency, ask:
- How much of this package will I actually use? If you need one utility function from a package with 200 exports, consider implementing that function yourself.
- Is this a solved problem that is hard to get right? Cryptography, date/time handling, and HTML parsing are good reasons to use a dependency. Left-padding a string is not.
- What is the maintenance cost? Dependencies require updates, security patches, compatibility testing, and occasional migration when they are abandoned.
- What happens if this dependency disappears? Consider the supply chain risk. Can you fork it if needed? Is the source available?
- Is there a standard library alternative? Many languages have robust standard libraries. Check before reaching for a third-party package.
The goal is not zero dependencies — it is zero unnecessary dependencies.
Lock File Handling
Lock files (package-lock.json, yarn.lock, Pipfile.lock, Cargo.lock, poetry.lock, Gemfile.lock) serve a critical purpose: they ensure that every environment installs exactly the same dependency versions.
- Always commit lock files to version control for applications. This ensures reproducible builds.
- For libraries, the convention varies by ecosystem. In Rust,
Cargo.lockis typically committed for binaries but not libraries. In Node.js,package-lock.jsonis generally committed regardless. - Never manually edit lock files. They are generated by the package manager. Manual edits cause inconsistencies.
- Regenerate the lock file when you modify the manifest (adding, removing, or changing dependency versions). Use the package manager's install or update command.
- Review lock file changes in pull requests. Large, unexpected changes in the lock file may indicate a cascading update or a supply chain issue.
- If merge conflicts occur in lock files, do not try to manually resolve them. Accept one side and regenerate: delete the lock file, run the install command, and commit the result.
Best Practices
- Audit dependencies before adding them. Check the repository, issue tracker, release cadence, and maintainer count.
- Pin dependency versions in applications for reproducibility. Use ranges in libraries for flexibility.
- Update dependencies regularly in small increments rather than waiting for a large, painful upgrade.
- Run
npm outdated,pip list --outdated, or equivalent regularly to stay aware of available updates. - Remove unused dependencies. They add install time, attack surface, and cognitive overhead for no benefit.
- Document why non-obvious dependencies exist. A comment explaining why you need a specific package saves the next developer from removing it and breaking something.
Anti-Patterns
- Adding a dependency for trivial functionality. A package that provides a single utility function you could write in 10 lines adds supply chain risk disproportionate to its value.
- Not reading what a dependency does. Installing a package you do not understand means you do not understand your own application's behavior.
- Ignoring security audit results. Known vulnerabilities in your dependency tree are low-hanging fruit for attackers.
- Unpinned dependencies without a lock file. This means every install can produce a different result. Builds are non-reproducible and bugs become non-reproducible.
- Updating all dependencies at once. If something breaks, you cannot tell which update caused it. Update one at a time and test.
- Forking instead of contributing. Forks are maintenance burdens. If you need a fix in a dependency, contribute it upstream first. Only fork if the project is abandoned.
- Vendoring without a strategy. Copying dependency source code into your project avoids the package manager but creates a hidden maintenance burden. If you vendor, have a clear process for applying upstream updates.
- Ignoring deprecation warnings. These are advance notice that a dependency will break in a future version. Address them proactively.
Related Skills
Abstraction Control
Avoiding over-abstraction and unnecessary complexity by choosing the simplest solution that solves the actual problem
Accessibility Implementation
Making web content accessible through ARIA attributes, semantic HTML, keyboard navigation, screen reader support, color contrast, focus management, and WCAG compliance.
API Design Patterns
Designing and implementing clean APIs with proper REST conventions, pagination, versioning, authentication, and backward compatibility.
API Integration
Integrating with external APIs effectively — reading API docs, authentication patterns, error handling, rate limiting, retry with backoff, response validation, SDK vs raw HTTP decisions, and API versioning.
Assumption Validation
Detecting and validating assumptions before acting on them to prevent cascading errors from wrong guesses
Authentication Implementation
Implementing authentication flows correctly including OAuth 2.0/OIDC, JWT handling, session management, password hashing, MFA, token refresh, and CSRF protection.