Photo by Joshua Sortino on Unsplash
For any non-trivial software application, keeping dependencies like libraries and frameworks up-to-date is an endless but essential task. Why is it so important and challenging?
What are dependencies?
Dependencies are reusable code packages that your application relies on to function. Some common examples are libraries for handling dates, connecting to databases, compiling CSS, etc. Almost all applications depend on external code written by others.
Why keep dependencies updated?
There are a few key reasons:
Security - Outdated dependencies often contain vulnerabilities that can put your application at risk of being hacked or compromised. Keeping them updated applies security patches.
Bug fixes - Newer versions of dependencies fix bugs and issues that could cause problems in your app. Staying updated applies those fixes.
New features - Upgrading gives you access to new features and improvements in the dependencies. It allows you to build on the latest capabilities.
Compatibility - Old dependencies may stop working with newer operating systems, browsers, or other software your app interacts with. Upgrading avoids compatibility problems.
Licensing - In some cases, old dependencies go out of support and lose their licensing. Upgrades keeps things compliant.
Best practices - Using outdated dependencies is seen as technical debt. Keeping current follows modern standards.
Why is it hard to stay updated?
While essential, dependency upgrades can be tedious and risky:
Time consuming - Researching updates, reviewing changes, testing impacts takes developer time.
Regression risk - New versions could introduce new bugs that break existing functionality. Extensive re-testing is required.
Compatibility issues - Updated dependencies may drop support for features your app relies on, requiring code changes.
Cascading updates - Upgrading one dependency may require upgrading others, leading to cascading changes.
Intricate coordination - Updates often need to be done incrementally across related packages while avoiding conflicts.
A Real-World Example
Here's a real-world example of the effort required to maintain dependencies in a Rails template application:
Date Range: June 29 - August 12 (6 weeks)
Total PRs: 113
Duplicate/Closed PRs: -19
Net New PRs: 94
Weekly Breakdown:
- Week 1: 16 -> 13
- Week 2: 23 -> 19
- Week 3: 18 -> 15
- Week 4: 26 -> 21
This shows the real ongoing effort required, even for a simple application, to stay on top of dependencies and security.
For large legacy applications with years of complex interdependencies, these challenges compound quickly. Keeping all dependencies fully updated can be a continuous uphill battle.
Automation and intelligence to the rescue
Thankfully there are now tools and services that help automate and guide developers through dependency upgrades:
Dependency monitoring - Services like Dependabot and Depfu watch your dependency manifests, detect outdated packages, and automatically raise pull requests to update them.
Security monitoring - Tools like Snyk scan dependencies and alert you to vulnerabilities, licensing issues, or maintenance status.
Impact analysis - Some services can simulate the impact of dependency upgrades by diffing API changes, detecting incompatible changes, and estimating migration effort. Tools like Infield provide some of these with others on their roadmap.
Step-by-step guidance - For complex upgrades, tools like Infield provide interactive step-by-step guidance tailored to your codebase to smooth the transition.
While still requiring planning and testing, these automation and intelligence tools greatly improve the dependency upgrade process. The days of completely manual upgrades are fading.
There's hope ahead
Software will always need diligent maintenance. But the pain of constant dependency upgrades is being relieved through automation. Developers now have an expanding toolbox to tame the beast. The future looks brighter!
Please comment and let me know if you’d like more content like this or similar.