{"componentChunkName":"component---src-components-blog-template-js","path":"/blog/2025-04-20-dependency-management/","result":{"data":{"markdownRemark":{"frontmatter":{"title":"Dependency Management","date":"2025-04-20"},"html":"<p>Every modern application relies on external packages. A typical Node.js project can have hundreds or even thousands of dependencies when we count transitive ones. Managing them well is important for security, stability, and maintainability.</p>\n<h3>Lock Files</h3>\n<p>A lock file (<code class=\"language-text\">package-lock.json</code>, <code class=\"language-text\">yarn.lock</code>, or <code class=\"language-text\">pnpm-lock.yaml</code>) records the exact version of every dependency and its transitive dependencies that were installed. Without a lock file, running <code class=\"language-text\">npm install</code> on a different machine might resolve to different versions, leading to \"it works on my machine\" problems.</p>\n<p>Lock files should always be committed to version control. They ensure that every developer and every CI environment installs exactly the same dependency tree.</p>\n<h3>Version Ranges</h3>\n<p>The <code class=\"language-text\">package.json</code> file specifies version ranges, not exact versions. A version like <code class=\"language-text\">^2.3.1</code> means any version from 2.3.1 up to but not including 3.0.0. The caret (<code class=\"language-text\">^</code>) allows minor and patch updates. A tilde (<code class=\"language-text\">~2.3.1</code>) is more restrictive, allowing only patch updates within 2.3.x.</p>\n<p>Semantic versioning (semver) promises that patch updates fix bugs, minor updates add features without breaking changes, and major updates may include breaking changes. In practice, not every library follows semver perfectly, which is why lock files are essential.</p>\n<h3>Keeping Dependencies Updated</h3>\n<p>Outdated dependencies accumulate security vulnerabilities and make future upgrades harder. The longer we wait, the bigger the jump and the more likely something breaks.</p>\n<p>Tools like Dependabot and Renovate automate this by opening pull requests when new versions are available. They can be configured to group minor and patch updates together and require review for major updates.</p>\n<p>Running <code class=\"language-text\">npm audit</code> regularly checks for known vulnerabilities in the dependency tree and suggests fixes. For critical vulnerabilities, updating promptly is important.</p>\n<h3>Minimising Dependencies</h3>\n<p>Not every problem needs a package from npm. Before adding a dependency, it is worth considering whether the functionality is simple enough to implement ourselves. A package that pads a string to a certain length or checks if a value is an object might not justify the added dependency.</p>\n<p>Each dependency is code we did not write and do not fully control. It can introduce security risks, licensing issues, or break in unexpected ways when updated. Being intentional about what we depend on keeps the project leaner and more predictable.</p>\n<h3>Monorepo Considerations</h3>\n<p>In a monorepo, dependency management has additional nuances. Shared dependencies should be consistent across packages to avoid version conflicts. Tools like pnpm handle this well with its strict dependency resolution. Hoisting dependencies to the root with workspaces reduces duplication but can sometimes cause issues when a package relies on an implicitly available dependency.</p>\n<h3>Security</h3>\n<p>Supply chain attacks, where a malicious actor compromises a popular package, are a real concern. Reviewing what dependencies do, pinning to exact versions for critical packages, and using tools that verify package integrity help reduce this risk. Keeping the number of dependencies reasonable also reduces the attack surface.</p>"}},"pageContext":{"slug":"/2025-04-20-dependency-management/"}},"staticQueryHashes":[]}