The term “dependencies” can be fundamentally defined as the files, packages, libraries, or plugins integrated into a project to implement a specific task or set of tasks. It is a modern way to leverage code from various developers, but it also creates a reliance on external code for the project to operate properly.
For example, let's suppose you're adding encryption to your application and you include a third-party library to get the job done. The library responsible for handling encryption will be a dependency for your application in this particular case.
If you're working on a large project codebase that introduces new versions of packages for each module, the branches on your dependency tree will continue to multiply until maintenance becomes a total headache. Updating and managing a dependency package, which in turn depends on another package or library, is a complicated process. Dependencies can become locked together and tightly coupled in this way.
This article will cover different kinds of dependencies, how they affect your apps, and how you can keep them updated with minimal effort.
Dependency Issues
Applications may encounter dependency issues for the following reasons:
- Utilization of third-party databases
- Using external web-based services
- Implementing libraries to measure app adoption
- Using outdated versions of third-party libraries
- Poorly implemented functionalities by inexperienced developers
Version Lock
Unhandled dependencies create a situation where application developers are unable to upgrade a certain package without publishing the latest versions of every dependent package. This creates a loop commonly known as version lock.
External Dependencies
On one hand, open source libraries help in speeding up the development process because they have been designed for targeting common problems. But on the other hand, external dependencies can have notable negative effects, such as:
- The addition of unknown malicious code
- Performance issues due to hidden bugs in external code
To avoid these drawbacks, dependencies must be carefully implemented and proactively managed.
Types of Dependencies
Dependencies come in many varieties across all platforms. Running and maintaining applications on modern development channels usually requires at least a few dependencies. Major dependency types are:
Direct Dependencies
These include the direct integration of libraries or plugins within an application. In other words, they have been actually integrated into the app's code. You should avoid using different direct dependencies for the same functionality (such as multiple JSON parsing libraries).
Transitive Dependencies
Commonly referred to as a "dependency of a dependency", a transitive dependency is a library called by one of your direct dependencies. Conflicts originate when a direct dependency does not support updated versions of its transitive dependencies. For example, let's say that Library A is dependent on version 0.1 of Library B, but your application requires version 0.2 of Library B. How can you know if Library A is compatible with version 0.2 of Library B?
It is recommended to avoid using implicit transitive dependencies and to explicitly maintain all transitive dependencies that are in use by your direct dependencies.
Development Dependencies
Two types of dependencies are included in package.json
, one of which is devDependencies
. These dependencies are only executed and consumed by files in the development phase. They can be used on remote hosts, e.g. linter packages and presets.
Runtime Dependencies
These include both frontend dependencies (executed in the end user's browser) and backend dependencies (running in the backend for http communication).
Classifying Dependencies by Development Phase
Various types of dependencies can be classified in accordance with different phases of the development process.
- Mandatory dependencies are hard logic that are built into a project's requirements during the client approval phase of development.
- Discrete dependencies deal with the way a sequence is defined for two activities by developers, as there can be multiple way to define sequences between activities.
- External dependencies usually do not involve the project team, while internal dependencies between two separate functions must be handled by the project team itself during development and testing phases.
Consequences of keeping outdated dependencies
Developers sometimes do not update dependencies out of a fear that they will break their apps, but outdated dependencies can have serious negative impacts on applications. Improperly configured dependencies expose you to the following risks:
Security Vulnerabilities
External libraries and frameworks can contain malicious attacks and pass them on to your development environment, exposing your application to vulnerabilities and potentially passing the attacks on to others in the form of security breach.
Reduced Performance
Outdated dependencies can cause you to miss out on performance improvements in an application. The inclusion of external code already creates overhead in your app, so you want to ensure that your external code is running optimally. For example, if you use array.prototype.concat
instead of load.concat
, it degrades performance.
Coupled Processes in Apps
Circular dependencies must be handled effectively because they introduce conflicts in various processes of apps. Older or end-of-life versions of libraries should be replaced on time. Updating dependencies also helps in bug fixes.
Overhead Work
Delayed dependency upgrades can cost you a lot more effort in the future if you handle them as small, recurring tasks. For example, direct jumping from version 1 to version 4 can drag you into huge overhead maintenance, which could have been avoided if you had upgraded to version 3 and then to 4 within proper time frames.
Expanded Attack Surface
Outdated assets and libraries are not effective for any application or project. Implementations need to be either upgraded to recent versions or totally taken out so that they don't expose your development environment to outside attacks. Unused dependencies are considered abandoned, as they no longer serve any purpose in the app's functionality.
The above-mentioned points show that poor handling of dependencies can cause security risks as well as inconvenience.
Open Source Dependencies
Open source dependencies are generated when developers choose to rely on multiple open source libraries. Dependency management tools (Maven, Bower) also pull in external third-party dependencies. If you do not track these open source dependencies on time, you might lose control over your application.
It is tough to update these types of dependencies, but it's not impossible to do with the use of modern tools. Firstly, you must ensure compliance with licenses of all open source libraries you integrated in your application, and do the same for any direct and transitive dependencies.
Secondly, use the latest tools to track bugs and malicious items in open source dependency that you wish to use. This will prevent from later attack.
Thirdly, perform regression testing while updating versions of libraries or dependencies. For example, if you update to a recent patch without testing, you can't know that the current version is compatible with your application and will not create unforeseen circumstances in future.
Lastly, tools like NSP, RetireJS, Hakiri, and dependency-check and platforms like Crowdbotics can help you automatically maintain open source dependencies with support for many frameworks.
What, When, and How to Update
It's a big challenge to analyze which portion needs updating and what the safest approach is. Following a few tips can help:
- Wait for a specific reason to update any dependency rather than updating as soon as it's available. The advantage is that you buy time to plan for the changes that are coming in the new version.
- Ensure that resource requirements are met prior to updating a dependency. New versions contain new features, so you must meet hardware and software requirements before upgrading. One challenge here is that each operating server (Ubuntu, macOS, centOS) supports different commands that you'll need to learn so has not to damage the current version.
- If an update requires you to reboot your application, first redirect your traffic to an alternative. It costs you time and additional resources, but if a new script corrupts your application, then you'll be able to restore your original app in short order.
- Prioritize updating dependencies that are the most critical to your app and make informed choices by analyzing their dependency histories.
- It's safer to manually edit
package.json
throughnpm update
. Using commandnpm install
at the start produces a fresh project with the latest versions according to the version ranges defined in yourpackage.json
. But it does not support re-runningnpm install
. For that, we can usenpm update
, which automatically checks for up-to-the-minute versions and installs them. Furthermore,npm outdated
checks for updates which exist beyond the semantic versioning range defined earlier inpackage.json
:
Package | Current | Wanted | Latest | Location
Lodash | 3.2.0 | 3.10.6 | 4.10.1 | npm-test
One disadvantage of using npm update
for major version updates is that it might come with inadequate changes that break the code and thus require refactoring.
Dependabot: Dependency Management Tool
Dependabot is a dedicated tool for automatically managing your dependency changelog. It creates automated pull requests whenever there is new version for all dependencies you created in package.json
. You are provided with options to verify that the updated dependency did not break any code. Keep in mind that it only works for GitHub.
Crowdbotics uses Dependabot to automate dependency upgrades so that you can focus more on productivity without an extra overhead. This is a benefit of our full-code approach, which gives you the opportunity to configure your preferences in unique ways. Crowdbotics provides alerts for vulnerable dependencies, automatic updates, and automatic package management out of the box.
Final Thoughts
Even when you have detailed documentation on how to use and update your dependencies, a few precautions might be necessary to avoid disruption:
- Learn about the new version of dependency that you might include and gradually update it from version to version.
- Try not to use dependencies for all small utilities if first-party code can handle them just as well.
- Use a platform like Crowdbotics to keep your dependencies updated on a consistent basis without any added effort.
Are you building an application with multiple third-party integrations? All applications hosted with Crowdbotics receive ongoing maintenance, support, and security updates for third-party dependencies. Get in touch with us today to receive a detailed quote and build timeline.