Most modern applications are built on top of hundreds of third-party packages. Every time you run npm install, yarn add or dotnet add package, you're pulling someone else's code directly into your production environment.
This means your application is only as secure, stable, and trustworthy as the dependencies you choose. The recent NPM worm incident was a perfect example: a single compromised package rapidly infected thousands of downstream projects.
Choosing the right dependencies, and maintaining them properly, is one of the most important things you can do before an exploit hits the ecosystem.
The reason packages exist is simple, no team should be reinventing everything from scratch. They let you stand on the shoulders of people who've already solved problems.
Developers rely on packages to:
While packages help teams move faster, they also introduce real and often underestimated risks. Every dependency added to your project is a new entry point for bugs, vulnerabilities or supply-chain attacks.
⚠️ Some of these key risks include:
The biggest risk with bringing in external packages is that you usually don't review the code you're pulling in. Yet it is still allowed to execute with the same permissions and privileges as the rest of your application.
Choosing the correct package is about more than just finding the one that works, it's about selecting dependencies you can trust, maintain and confidently ship to production. A good dependency should show clear signs of life, healthy governance, and predictable maintenance.
Before installing or adding a package to a project, developers should consider the following:
A package should be actively maintained, without this code quickly becomes incompatible, insecure and outdated. Packages that evolve with the greater ecosystem are more likely to curb security vulnerabilities and remain compatible with your solutions. The moment a security flaw is discovered and no-one is actively patching it, you inherit the maintenance burden.
When checking maintenance activity, look for:
❌ Figure: Bad example - No recent activity
✅ Figure: Good example - More recent activity
Packages produced by reputable individuals or organisations tend to follow better development practices, have formal processes for handling disclosures, and respond quickly to breaking issues.
Look for:
A well documented package is a strong indicator of maturity, professionalism and long-term maintanability. Good documentation shows that the maintainers care about the developer experience and understand how their package is used in real-world applications.
Popularity isn't a guarantee of quality, but it is a signal of real-world usage, maturity, and testing. A widely used package is more likely to be:
❌ Figure: Bad example - Decent amount of uses
✅ Figure: Good example - A lot more popular, battle-tested 💪
Healthy packages ship predictable updates. Regular releases indicate active stewardship and highlight:
❌ Figure: Bad example - Released ages ago!
✅ Figure: Good example - More recent release
A good package should be open and reviewable, transparency allows for:
Being able to review how a package works allows developers to evaluate its safety. Closed or opaque packages prevent verification, hide risky behaviour and make debugging significantly harder.
A single tiny package might introduce dozens of transitive dependencies, each new dependency carries:
Most importantly, risk compounds. A package might look harmless but its dependency chain may be spiraling and fragile.
Modern JavaScript applications are heavily influenced by dependency size. Tools like:
These tools help you estimate the cost of adding a new library to your bundle.
Tree-shaking is another important factor. It removes unused exports from a package during build time, reducing shipped code. Unfortunately, there's still no universal way to know whether a library tree-shakes correctly, you generally need to:
Even today, the only reliable method is real-world experimentation.
Unlike JavaScript, .NET doesn’t have ecosystem tools that report the “bundle size” impact of installing a NuGet package. You can inspect IL size or dependency graphs, but there is no direct BundlePhobia-style analyser.
To reduce the final application size, .NET provides a built-in feature called the Trimmer (ILLink). Trimming removes unused code from your published output and can significantly shrink deployments. However, trimming must be used carefully—apps that rely on reflection, dynamic activation, or certain serializers may require additional annotations to avoid breaking behaviour.
Trimmer support has improved in .NET 8 and .NET 9, but the same rule still applies: understand your dependencies before enabling trimming in production builds.
Licenses define what you can and cannot do with the code. Legal issues can emerge years after the code is shipped. Using restrictive or unclear licenses can put your organisation at risk, especially in commercial or closed-source products. The safest time to avoid licensing issues is before you install the dependency.
✅ Figure: Good example - Licensing mentioned
Lastly before deciding to install the library, check with another developer that is experienced in the scope of your project (e.g. look for a senior JavaScript developer's opinion if the project is an Angular project). Having a 2nd qualified person to agree with your decision is a good indicator that you are picking a good library
Always keep track of the reasoning when developers decided to go with a particular library instead of another one. This helps future developers working on a project to maintain the project.
Future developers will have better context and will be able to make a better decision should there be any situational or business requirement changes. A package audit log is a great way to record all the decisions.
✅ Figure: Good example - A markdown file should include your reasons to assist future developers
Choosing a good package is only half the job, maintaining them is an ongoing responsibility. Dependencies evolve constantly; new features are added, security patches are released, breaking changes appear, and ecosystem standards shift.