Cover Image for Navigating the Complex World of Core Dependency Updates

Navigating the Complex World of Core Dependency Updates

Bartek Witczak
Bartek Witczak

Managing core dependencies is a task that often walks a fine line between necessity and challenge. It's crucial to understand the dynamics of dependency updates. And it's crucial both for developers & managers. I'll dive into the world of dependency management, unearthing strategies and insights. I'll describe various possibilities, but you need to choose your own way. As always, context is the king - consider requirements, resources, and risk tolerance.

Personal Insight: I recall the struggle we faced while updating monorepo framework. I remember that one being especially painful. We had a need to update nx dependency to kickstart a new project within monorepo. In ever-changing JS world, we have been few versions behind in the matter of months.

Understanding Semantic Versioning

Semantic versioning is more than just a set of numbers assigned to your dependencies. It's a roadmap to understanding the impact of each update. Given as MAJOR.MINOR.PATCH, these versions convey critical information:

  1. MAJOR version changes signify incompatible API changes.
  2. MINOR version updates add functionality in a backward-compatible manner.
  3. PATCH version is for backward-compatible bug fixes.

Example: Version 15.8.2 breaks down as:

  • Major: 15
  • Minor: 8
  • Patch: 2

If only everyone keep library up to semantic versioning...

Challenges in Dependency Management

The path to effective dependency management in software projects is riddled with obstacles. Here's a closer look at the typical challenges encountered:

  • Inconsistencies in Semantic Versioning: Not all projects follow the semantic versioning guidelines. This can lead to compatibility issues and unexpected behavior, making updates more complex than they appear on paper.
  • Navigating Through Changelog: Updating across versions means delving into detailed changelog. This is time-consuming, but essential. You need to access how each change is going to affect codebase.
  • Initial Build Failures: It's common for projects not to build successfully post-update. This initial hurdle necessitates a thorough debugging process to resolve new compatibility issues.
  • Lack of Code Generation Tools: Frameworks and libraries seldom give us generation tools. It leads to manual work & error-prone process.
  • No Standard Update Guide: There's no one-size-fits-all guide. Everyone has a different version. You could be updating from the recent one or few versions behind.
  • Ensuring Compatibility Across Projects: In complex environments, like monorepos, you need to ensure that new updated play well with all exisiting systems. You need to watch out not to disrupt the functionality of others.

Looking at that list, why do we even bother to update (core) dependencies?

Why Update Dependencies?

Updating dependencies is NOT just a matter of staying current. It's a strategic decision that impacts the project's health and future. Here are some key reasons why updating dependencies is essential:

  • Performance Improvement: New versions of dependencies often come with optimizations that can enhance the performance.
  • Access to New Features: Updating dependencies allows you to leverage new functionalities and improvements that can add value.
  • Staying Ahead of End-of-Life Support: Relying on outdated versions can be risky, especially if they are nearing the end of their support lifecycle. Regular updates ensure continued support and security.
  • Mitigating Critical Bugs: Older versions might contain bugs that can be detrimental to your software's stability and security. Updating them is crucial for maintaining a secure and reliable system.

Understanding these reasons helps in formulating a strategy that aligns with your project's goals and needs.

Strategies for Managing Core Dependencies

When it comes to managing core dependencies, there's no one-size-fits-all approach. Here are some strategies you can consider:

  • Do Nothing Until Necessary: This strategy involves minimal intervention. We update dependencies only when it's absolutely required. It could be due to critical flaws, security issues, or essential new features.
  • Incremental Updates: We keep the current major version. We keep track & regularly implement minor updates and patches. This approach balances stability with gradual improvement.
  • Proactive Updating: We regularly update to the latest version, including major updates. This strategy keeps your project at the forefront but requires a robust testing and solid process.

Each strategy comes with its pros and cons. The choice depends on your project's specific requirements, resources, and risk tolerance.

Let me dive into two contrasting approaches. Let's analyze these in detail.

Don’t Touch It If Ain’t Broken

image

This strategy is about minimal intervention, updating only when absolutely necessary. When to Update? Only when updates are critically needed. There are serious flaw. To name a few: the project no longer builds, there are security bugs. Or on the positive side things like new functionality or performance improvement.

Pros ✅:

  • Resource Efficiency: Time and resources are not spent on updates that don't offer clear immediate value.
  • Stability: Less frequent updates mean fewer disruptions to the existing system.

Cons ❌:

  • Challenging Major Updates: When an update becomes necessary, the process can be time-consuming and complex.
  • Potential Process Gaps: There might be missing steps in the process. Usually an incomplete testing pipeline or a lack of knowledge.
  • Unusual Process: Since updating dependency is seldom, no one is fluent with update.

Update Fast, Update Frequently

image

In contrast, this strategy involves regular updates to stay current. This approach is based on the idea that eventually, an update will be necessary, so it's better to do it regularly.

Pros ✅:

  • Well-Established Processes: Regular updates require robust automation and testing. It means that process is familiar & well-defined.
  • Proactive Approach: Regularly updating helps in avoiding the buildup of issues. It makes adapting to new versions easier.
  • Leverage New Functionality First: Developers can use new functionality as soon as it's released. It opens possibilities for new ventures & use-cases.

Cons ❌:

  • Time Investment: It requires consistent time investment, which might not yield immediate benefits.
  • Risk of New Bugs: Frequent updates, especially to new major versions, can introduce new bugs into the system. That is especially risky, since you're on the bleeding edge.

Both strategies have their merits and challenges. The choice between them should be based on the specific project. CI & CD is critical in any case. The more you can trust in CI, the easier it's to make decision.

TBC...

That's all for today. It's pretty long already. In the next part, I'll share some tips to mitigate risks, case studies & FAQ for updating dependencies. Let me know if you have any questions or comments 💪

image