Disclaimer: Just a heads up, while we’re diving into ways to tackle the problem of dependency depreciation, there’s no one-size-fits-all solution here. It’s a bit of a wild ride dealing with supply chain security and those pesky deprecated dependencies, so don’t expect a quick fix!
The issue of using deprecated dependencies has persisted for quite some time, and it’s gaining increased attention. Many projects continue to incorporate deprecated dependencies.
I was inspired to write this blog after coming across a LinkedIn post by Rory McCune and several other posts over the past few weeks.
So, I decided to share my perspective on how we can mitigate security risks and effectively navigate the challenges of dependency management. I decided to use the npm ecosystem as an example here because, well, it’s what I’m dealing with right now.
Before we dive deep, let’s set some context on what are the challenges associated with these deprecated dependencies?
When the same question was posed to ChatGPT, it eloquently addressed the concerns.
What's the problem with deprecated dependencies?
- Security Vulnerabilities: Deprecated dependencies may contain known security vulnerabilities that are no longer patched or updated. This can leave your project vulnerable to potential exploits.
- Compatibility Issues: Deprecated dependencies may not be compatible with newer versions of other libraries or frameworks, leading to conflicts and issues that can disrupt your project's functionality.
- Lack of Maintenance: Deprecated dependencies are typically no longer actively maintained or updated by their developers. This can lead to problems such as unaddressed bugs or performance issues.
- Inefficient Performance: Deprecated dependencies may not take advantage of the latest performance optimizations, potentially slowing down your project.
- Breaking Changes: Upgrading other parts of your project or platform may reveal breaking changes or incompatibilities with deprecated dependencies, requiring additional development work to resolve.
- Reduced Community Support: As dependencies become deprecated, their communities may shrink, making it more challenging to find support or resources to address issues or questions.
- Technical Debt: Relying on deprecated dependencies can accumulate technical debt in your project, making it more difficult and costly to maintain in the long run.
TL;DR - Deprecated dependencies can cause problems such as,
- Security Vulnerabilities
- Compatibility Issues
- Lack of Maintenance
- Inefficient Performance
- Breaking Changes
- Reduced Community Support
- Technical Debt
So, what’s the plan, you might wonder? Well, that’s exactly what we’ll dive into in the next sections.
Getting Visibility On Deprecated Dependencies
When we execute npm install
, it installs all the required dependencies for the project to function. However, this process also generates warnings related to these dependencies. The issue is that these warnings encompass a wide range of issues (vulnerability warning, peer dependency warning, missing script warning, unmet dependency warning, deprecation warning, etc.), and they often tend to be disregarded simply because they are categorized as ‘warnings’.
Alarm fatigue or alert fatigue describes how busy workers become desensitized to safety alerts, and as a result ignore or fail to respond appropriately to such warnings.
…for more details read here.
The approach to tackle this is to devise a carefully tailored method to draw attention to these issues and provide context about the dependencies. The cool part is that npm, by default, offers the necessary tools; all we need to do is adeptly shape and present it to the users in a manner that highlights the problem effectively.
So, what information do we need to give the consumer?
- What are the deprecated dependencies?
- Is it a direct or transitive dependency?
- Context of the dependencies?
The below script does all of that -
#!/bin/sh
npm install
# Define the jq query and package-lock.json file
JQ_QUERY='.packages | to_entries[] | select(.value.deprecated != null) | "\(.key)@\(.value.version)" | sub("^node_modules/"; "")'
PACKAGE_LOCK_FILE="package-lock.json"
# Run the jq query to get the list of deprecated dependencies
DEPRECATED_DEPENDENCIES=$(jq -r "$JQ_QUERY" "$PACKAGE_LOCK_FILE")
DEPRECATED_DEPENDENCIES_STR=$(echo "$DEPRECATED_DEPENDENCIES" | awk 'ORS=" "')
# Check if DEPRECATED_DEPENDENCIES is not empty before running the loop
if [ -n "$DEPRECATED_DEPENDENCIES" ]; then
echo "$DEPRECATED_DEPENDENCIES" | while IFS= read -r DEPENDENCY; do
npm view $DEPENDENCY
npm list --depth 1000 $DEPENDENCY
done
echo "----------------------\n"
echo "Overall Depreacted Dependencies (Dependency Tree): "
echo $DEPRECATED_DEPENDENCIES_STR | xargs npm list --depth 1000
rm -rf node_modules/ package-lock.json
else
echo "No deprecated dependencies found ✨ Yay! 👏"
fi
and this gives output like this -
- For the direct dependency -
- For transitive dependency -
- Overall Summary (Depedency Tree of Deprecated Dependency) -
Now, let’s delve into the implementation aspect.
One approach that comes to mind is to incorporate a script that runs during each deployment within your CI/CD pipeline. This script would then send a detailed report to users through accessible platforms like Slack or Discord. Users can conveniently review the report and take necessary actions.
If you’re feeling daring, you can even take it to the next level by potentially halting the pipeline when deprecated dependencies are detected. However, I’d like to emphasize that this more stringent approach might not always be the best fit, and its suitability largely depends on your specific use case.
Getting Visibility on Unmaintained Dependencies
Another valuable approach is to leverage open-source tools like vet , which can assist you in identifying projects and dependencies that are no longer actively maintained. This insight can be incredibly informative and help you make well-informed decisions about your dependencies.
Possible Action Items
The actionable steps may vary depending on the situation, but in an ideal scenario, consider the following options:
- Remove the Deprecated Dependency: When feasible, it’s advisable to remove the deprecated dependency from your project to eliminate potential issues.
- Fork and Maintain: In some cases, forking the dependency and taking responsibility for its maintenance can be a viable solution, especially if it’s critical to your project’s functionality.
But let’s face it, we’re not in an ideal world.
So, what should we do?
In the face of ongoing challenges posed by deprecated dependencies in software development, there are practical steps we can take. By adopting a more mindful approach, engaging with the community, and utilizing automated tools, we can better address these issues. Regular audits, occasional forking, and continuous learning can help us navigate this landscape more effectively. With a pragmatic outlook and adaptability, we can work towards a future where deprecated dependencies are managed effectively, allowing our projects to thrive with confidence.
As I wrap up this blog, I’d like to leave you with a thought from my presentation on Dependency Hell, courtesy of @anantshri: ‘We will not be talking about creating Dependency Heaven, but will talk about how to be the Lucifer in the hell.’
See’ya! Until next time!