DEV Community

Cover image for "The Strawberry in the Cake" - Challenges of Libraries and Dependency Management
Danilo P. De Luca
Danilo P. De Luca

Posted on

2 1 1 1 1

"The Strawberry in the Cake" - Challenges of Libraries and Dependency Management

Imagine a beautiful cake topped with a fresh, juicy strawberry. The strawberry adds the perfect touch of sweetness, visual appeal, and flavor to the cake. It's the highlight, the centerpiece of the cake's presentation. However, there's a catch—strawberries have a much shorter shelf life than the cake itself. While the cake might stay fresh and edible for days, the strawberry begins to rot much sooner - have you ever eaten a piece of cake that the strawberry had gone rotten? Uhh, that's horrible!

Now, let's connect this to software development, for this, do you remember any time that you had to update library or a dependency version and got into what we can call "Dependency Hell"? Its pretty similar to this:

  1. The Cake represents your main application or system. It's the core product, which should ideally have a long lifecycle and remain stable and maintainable.

  2. The Strawberry symbolizes a third-party library, a dependency, or a microservice your application relies on. These can bring exceptional value, just like the strawberry enhances the cake - I can remember the first time that the Project Lombok was introduced to me, back in 2016, while I was working on a #java huge application, that was fantastic! Nowadays, it might not be worth it, as much of it was introduced in the latest updates in #Java, turning the cake good enough to not need a strawberry.

But, there is a problem:

  1. Just like a strawberry spoils faster than the cake, libraries, and dependencies often have shorter lifecycles than your application (should it be easier to change a library or your entire application?)

  2. If your application (The Cake) is tightly coupled to a specific version of a library (the strawberry), you might face significant issues when the library's lifecycle ends, for example when it has an ABI Breaking change - this is a common terminology when working with C++ in the past, today we mostly use an API Versioning, Breaking Changes, Contract Breakage, and for me, in particular, its one of the main issues when getting into the famous Dependency Hell 🔥

Thinking about it there are different perspectives on avoiding this issue:

1. When Creating Your Libraries

When you are building a library that others will rely on - your friend will use it, other teams in your company will use it, or you are creating it but don't know yet who will use it (that happens sometimes hehe).

- Backward Compatibility: Ensure new versions of it maintain compatibility with previous versions wherever possible. This is not that easy, and sometimes we do need to release a breaking change, for this it should be carefully planned and communicated.

- Semantic Versioning: Follow semantic versioning principles (MAJOR.MINOR.PATCH) to help consumers understand the impact of updates.

- Independent Upgradability: Design your library so it can be updated independently of the system that depends on it. For example, avoid hard-coded assumptions about the consumer's environment.

- Share what has been done: The famous CHANGELOG.md provides clear documentation and migration guides for new versions.
- Focus on Security: Regularly review and address security vulnerabilities in your libraries. You don't your library to be a "trojan" into other people's applications, do you?

2. When Using a Third-Party Library

- Evaluate the Community and Longevity: before adopting a library, assess its community support, maintenance activity, and long-term viability.

- Regular Updates: Stay proactive in updating libraries to their latest stable versions. Many updates include critical bug fixes and security patches.

- Monitor for Vulnerabilities: Use tools like Dependabot or Seemplicity to automatically detect and report vulnerabilities in tour dependencies.

- Avoid Over-Reliance: Use libraries judiciously. Instead of importing an entire library for one small function, consider writing your own implementation or using lightweight alternatives (yeah! Why not coping and paste it into your project? 🤔)

- Plan for Contingency: Have a fallback strategy in case a library becomes deprecated or unsupported. This could involve forking the library or switching to a maintained alternative.

- Abstract Dependencies: I would say that this is the hardest one and at the same the most important, remembering concepts of Hexagonal Architecture, create an abstraction layer between your application and the library. This decouples your code from the library’s API, making it easier to replace or upgrade the dependency. (A master chef knows that when placing a strawberry on a cake, it should be covered with sugar syrup. This helps delay the strawberry from rotting and also prevents it from damaging the cake).

Note that, both of them work together, so when creating your library you might use a third-part library, then you need to be worried about it.

Some key takeaways from it:

  • Design systems that can handle library updates or replacements without significant disruption.
  • Avoid tightly coupling your application to specific versions of dependencies.
  • Emphasize backward compatibility when creating your own libraries to ensure they don't impose unnecessary constraints on their users.
  • Keep in mind that even if the strawberry is appealing, it might not be worth jeopardizing the cake (remember the to Avoid over-reliance?)

Don't let the "strawberry" define the lifecycle or quality of the "cake." Build systems that are resilient, adaptable, and not overly reliant on individual components.
Do you know other situation that we could use the concept of "The Strawberry in the Cake"? Share it in the comments, I would love to hear about more examples!

DEV Challenges are live now!

DEV Challenges Hub

Check out all the ways to participate, certify your skills, and win prizes.

Visit the Challenges Hub

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay