Dependency Analysis
0
Upvotes
2
Views
0
Downloads
1,354
Words
Description
Dependency Analysis reads manifests, resolves version conflicts, audits security, and applies a minimal-dependency strategy, including lock-file handling.
When to Use
analyze project dependencies | review manifest files for dependencies | resolve dependency version conflicts | audit dependencies for security risks | optimize dependencies to minimize bloat
Use Cases
Audit dependencies for known security risks. | Resolve conflicting version ranges across packages. | Identify unused deps to prune bloat. | Review lockfiles for reproducible builds.
SKILL.md Content
---
name: dependency-analysis
description: "Dependency Analysis reads manifests, resolves version conflicts, audits security, and applies a minimal-dependency strategy, including lock-file handling."
metadata:
tags: "autonomous-agents, dependency-management, manifest-reading, version-resolution, security-audit, transitive-dependencies, lockfiles"
source: "https://skilldb.dev/skills/autonomous-agent-skills/dependency-analysis"
pack: "autonomous-agent-skills"
category: "Autonomous Agents"
---
# Dependency Analysis
## When to use this skill
Use when the user says things like:
- "analyze project dependencies"
- "review manifest files for dependencies"
- "resolve dependency version conflicts"
- "audit dependencies for security risks"
- "optimize dependencies to minimize bloat"
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.3` allows minor/patch updates (1.x.x), `~1.2.3` allows only patch updates (1.2.x), `1.2.3` pins exactly.
**requirements.txt / pyproject.toml (Python):**
- `requirements.txt` lists direct dependencies, optionally with version constraints (`>=1.0,<2.0`).
- `pyproject.toml` under `[project.dependencies]` serves a similar role with richer metadata.
- `[project.optional-dependencies]` groups extras like `dev`, `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.
- `features` enable optional functionality within a dependency. Only enable what you need.
**go.mod (Go):**
- Lists module path, Go version, and required dependencies with exact versions.
- `indirect` comments 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:
1. **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.
2. **Check if the versions are compatible.** If one requires `>=1.5` and another requires `<2.0`, any version in `[1.5, 2.0)` satisfies both.
3. **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.
4. **Use resolution overrides as a last resort.** npm's `overrides`, Yarn's `resolutions`, and pip's constraint files let you force a version. Document why the override exists and revisit it regularly.
5. **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`, or `mvn dependency:tree` to 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:
1. **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.
2. **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.
3. **What is the maintenance cost?** Dependencies require updates, security patches, compatibility testing, and occasional migration when they are abandoned.
4. **What happens if this dependency disappears?** Consider the supply chain risk. Can you fork it if needed? Is the source available?
5. **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.lock` is typically committed for binaries but not libraries. In Node.js, `package-lock.json` is 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.