In his first article on Dependency Management, Alex explains why dependency management is a crucial process in SDLC (Software Development Life Cycle). In this post, he will dive deeper into the subject matter and share best practices.
Best Practices
We suggest you commence with the following essential guidelines to establish a robust dependency management process.
Rely on packages from trusted sources
It is highly important to add dependencies from trusted sources to your project. Using specialized tools for managing dependencies will guarantee the integrity of the used dependencies and can offer you a broader picture of the interdependencies with other packages. A simple way to achieve that is to use the technology-specific package manager.
Table 1. Example of package managers for most popular programming languages
Programming language | Package manager | Description |
Python | Pip | Pip is the default package manager for Python. It allows you to install, upgrade, and manage Python packages easily from the Python Package Index (PyPI). |
Poetry | Poetry is a modern dependency management and packaging tool for Python. It simplifies package management, dependency resolution, and project distribution, making it a popular choice for Python developers. | |
Node.js | npm | npm (Node Package Manager) is the package manager for Node.js and JavaScript. It provides access to a vast ecosystem of open-source libraries and tools for front-end and back-end development. |
Java | Maven | Apache Maven is a widely used build and dependency management tool for Java projects. It helps manage project dependencies, build processes, and project lifecycles. |
Ruby | RubyGems | RubyGems is the package manager for Ruby. It allows you to install and manage Ruby libraries, known as “gems,” for your Ruby applications easily. |
PHP | Composer | Composer is the de facto package manager for PHP. It simplifies including external libraries and dependencies in your PHP projects. |
C# (.NET) | NuGet | NuGet is the package manager for the .NET ecosystem. It manages libraries and packages for .NET projects, including ASP.NET, Xamarin, and more. |
Go | GoModules | Go Modules are integrated into the Go programming language. They simplify dependency management, making importing and using external packages in Go projects easy. |
Swift | SPM | Swift Package Manager (SPM) is the official package manager for the Swift programming language. It helps Swift developers manage and distribute macOS, iOS, and more packages. |
C++ | Conan | Conan is a popular package manager for C++. It simplifies the management of C++ dependencies, enabling developers to integrate and manage libraries in C++ projects easily. |
Take advantage of the version pinning
In a fast-changing environment where you have new releases of dependencies every day, it is important to know exactly what version of your dependencies will end up in each build or environment. With version pinning in place, you can achieve:
- Predictable builds: version pinning ensures that your software builds are consistent regardless of when or where it is built.
- Reproducible behavior: With all the dependencies in check, nothing will change without your interaction. You can reproduce the behavior of a particular version of your product at any point in time in any environment.
- Better security: Pinning versions help you stay informed about known vulnerabilities in your dependencies. Security scanners and tools can provide alerts when vulnerabilities are discovered in the pinned versions, allowing you to take proactive measures.
- Reduced risk of introducing breaking changes: When you rely on specific versions of dependencies, you will shield your product from the breaking changes introduced in newer versions.
Here are some best practices for version pinning and the importance of adhering to them:
- Always specify the exact version numbers of your dependencies in your project’s configuration files. Avoid using wildcard or range operators (e.g., “*,” “^,” “~”) as they can lead to unexpected behavior.
- Many package managers provide lock files (e.g., package-lock.json, lock, Pipfile. lock, poetry. lock) that freeze the exact versions of your dependencies. Commit these lock files to your version control system to ensure everyone working on the project uses the same version.
- The pinned versions will be specific for each component. In a shared environment, it is mandatory to avoid installing dependencies globally or system-wide. Instead, use virtual environments or package isolation mechanisms provided by your package manager to ensure each project has its own isolated set of dependencies.
- After pinning dependencies, conduct thorough testing to ensure your application still functions as expected. Automated testing suites and continuous integration pipelines can help with this process.
Keep the dependencies updated
The external components provide vital functionalities, speed up development, and allow developers to focus on solving specific problems rather than reinventing the wheel. However, as your project grows, so does the web of dependencies entangled within it. This is where the practice of keeping dependencies updated becomes indispensable.
It is important to update the dependencies to the newest validated release because:
- The digital landscape is rife with threats. Outdated dependencies are prime targets for malicious actors. By staying updated, you fortify your project against known vulnerabilities and significantly reduce the risk of exploitation.
- Software is rarely perfect. New versions of dependencies often include bug fixes, improving the stability and performance of your application. Ignoring updates means missing out on these enhancements.
- As the software ecosystem evolves, dependencies may introduce breaking changes. Updating your dependencies ensures that your software remains compatible with the latest versions of other libraries and frameworks, preventing compatibility issues.
- Updates often include optimizations and performance improvements. These improvements can lead to faster execution, better resource utilization, and an overall more efficient application.
- Over time, managing a project with outdated dependencies becomes increasingly challenging. The gap between your versions and the latest releases widens, leading to technical debt and difficulty catching up.
- Open-source projects tend to prioritize support for the latest versions. Staying updated means benefiting from community contributions and support, a valuable resource for developers.
- Some updates address licensing changes or legal issues. Updating your dependencies helps maintain compliance with licensing requirements, safeguarding your project from legal pitfalls.
Now that we’ve established the importance of keeping dependencies updated let’s explore how to put this into practice:
- Stay attuned to updates by closely watching official repositories, reading release notes, or employing specialized dependency management tools that notify you of new versions. This practice ensures that you don’t miss critical updates.
- Establish a routine for evaluating your project’s dependencies. Conduct weekly or monthly reviews to assess their status. During these reviews, scrutinize for newer versions and gauge their relevance to your project’s needs.
- Embed automated testing into your continuous integration (CI) pipeline to validate the compatibility and functionality of your software with updated dependencies. This ensures that updates don’t inadvertently break your application.
- Utilize specialized dependency scanning tools to identify known security vulnerabilities within your dependencies. These tools help you prioritize updates, focusing on security concerns.
Updating dependencies is a proactive measure to ensure your software remains secure, stable, and efficient. Regularly reviewing and updating dependencies will mitigate security risks, prevent compatibility issues, and keep your software competitive in an ever-evolving landscape. It’s an integral part of responsible software development and maintenance, ultimately contributing to the longevity and vitality of your projects.
Manage intertwined dependencies
Use tools that analyze your project’s dependencies for vulnerabilities and license compliance. Open-source tools like OWASP Dependency-Check or OWASP Dependency Track can help identify and mitigate potential issues.
OWASP Dependency-Check is an open-source software component analysis tool designed to help developers and organizations identify and manage security vulnerabilities in their software dependencies. It scans project dependencies, including libraries and frameworks, and compares their versions against known vulnerabilities in the National Vulnerability Database (NVD) and other vulnerability data sources. By integrating Dependency-Check into their development pipelines, teams can proactively detect and mitigate security risks related to third-party dependencies, enhancing the overall security posture of their software projects.
OWASP Dependency-Track is a comprehensive component analysis platform that extends the capabilities of OWASP Dependency-Check. It provides a centralized, web-based dashboard for tracking and managing software dependencies across the entire software development life cycle (SDLC). Dependency-Track offers features like continuous monitoring of dependencies, risk scoring, policy enforcement, and vulnerability tracking. This tool allows organizations to gain visibility into their software supply chain’s security and compliance status, enabling proactive risk management and ensuring that projects remain in compliance with security policies and standards. Dependency-Track empowers teams to make informed decisions about which dependencies to use and how to address security issues effectively.
Keep a decision log
In every project, the development team will need to choose various technologies, frameworks, or libraries that can help them solve the same problem. Because these decisions will impact the product in the long term, it is important to maintain clear and up-to-date documentation regarding your project’s dependencies. This helps new team members understand the project’s architecture and simplifies troubleshooting.
Furthermore, after a while, some decisions will probably be revisited, and the documentation will help bring to light the factors that influenced that decision or the other options investigated.
Group your dependencies
Organize your dependencies into logical groups, such as development, production, or testing dependencies. This makes it easier to manage and test different aspects of your application.
Automate, automate, automate!
Last but most certainly not least is the automation of dependency management.
Our next article will address this topic: “How to renovate hundreds of repositories overnight.”
By Alex Coman
Software Architect
Note: banner image by @markusspiske – unsplash.
STAY TUNED
Subscribe to our newsletter today and get regular updates on customer cases, blog posts, best practices and events.