How do big companies tackle with the package dependencies conflict problem?

Viewed 562

enter image description here Just as shown in the picture, one app (Java) referenced two third-party package jars (packageA and packageB), and they referenced packageC-0.1 and packageC-0.2 respectively. It would work well if packageC-0.2 was compatible with packageC-0.1. However sometimes packageA used something that could not be supported in packageC-0.2 and Maven can only use the latest version of a jar. This issue is also known as "Jar Hell".

It would be difficult in practice to rewrite package A or force its developers to update packageC to 0.2.

How do you tackle with these problems? This often happens in large-scale companies.

I have to declare that this problem is mostly occurred in BIG companies due to the fact that big company has a lot of departments and it would be very expensive to let the whole company update one dependency each time certain developers use new features of new version of some dependency jars. And this is not big deal in small companies.

Any response will be highly appreciated.

Let me throw away a brick in order to get a gem first.

Alibaba is one of the largest E-Commerces in the world. And we tackle with these problems by creating an isolation container named Pandora. Its principle is simple: packaging those middle-wares together and load them with different ClassLoaders so that they can work well together even they referenced same packages with different versions. But this need a runtime environment provided by Pandora which is running as a tomcat process. I have to admit that this is a heavy plan. Pandora is developed based on a fact that JVM identifies one class by class-loader plus classname.

If you know someone maybe know the answers, share the link with him/her.

6 Answers

We are a large company and we have this problem a lot. We have large dependency trees that over several developer groups. What we do:

  • We manage versions by BOMs (lists of Maven dependencyManagement) of "recommended versions" that are published by the maintainers of the jars. This way, we make sure that recent versions of the artifacts are used.

  • We try to reduce the large dependency trees by separating the functionality that is used inside a developer group from the one that they offer to other groups.

But I admit that we are still trying to find better strategies. Let me also mention that using "microservices" is a strategy against this problem, but in many cases it is not a valid strategy for us (mainly because we could not have global transactions on databases any more).

This is a common problem in the java world.

Your best options are to regularly maintain and update dependencies of both packageA and packageB.

If you have control over those applications - make time to do it. If you don't have control, demand that the vendor or author make regular updates.

If both packageA and packageB are used internally, you can use the following practise: have all internal projects in your company refer to a parent in the maven pom.xml that defines "up to date" versions of commonly used third party libraries.

For example:

    <framework.jersey>2.27</framework.jersey>
    <framework.spring>4.3.18.RELEASE</framework.spring>
    <framework.spring.security>4.2.7.RELEASE</framework.spring.security>

Therefore, if your project "A" uses spring, if they use the latest version of your company's "parent" pom, they should both use 4.3.18.RELEASE.

When a new version of spring is released and desirable, you update your company's parent pom, and force all other projects to use that latest version.

This will solve many of these dependency mismatch issues.

Don't worry, it's common in the java world, you're not alone. Just google "jar hell" and you can understand the issue in the broader context.

By the way mvn dependency:tree is your friend for isolating these dependency problems.

I agree with the answer of @JF Meier ,In Maven multi-module project, the dependency management node is usually defined in the parent POM file when doing unified version management. The content of dependencies node declared by the node class is about the resource version of unified definition. The resources in the directly defined dependencies node need not be introduced into the version phase. The contents of the customs are as follows:

in the parent pom

<dependencyManagement> 
    <dependencies > 
      <dependency > 
        <groupId>com.devzuz.mvnbook.proficio</groupId> 
        <artifactId>proficio-model</artifactId> 
        <version>${project.version}</version> 
      </dependency > 
    </dependencies > 
  </dependencyManagement>

in your module ,you do not need to set the version

<dependencies > 
    <dependency > 
      <groupId>com.devzuz.mvnbook.proficio</groupId> 
       <artifactId>proficio-model</artifactId> 
    </dependency > 
  </dependencies > 

This will avoid the problem of inconsistency .

This question can't be answered in general. In the past we usually just didn't use dependencies of different versions. If the version was changed, team-/company-wide refactoring was necessary. I doubt it is possible with most build tools.

But to answer your question.. Simple answer: Don't use two versions of one dependency within one compilation unit (usually a module)

But if you really have to do this, you could write a wrapper module that references to the legacy version of the library.

But my personal opinion is that within one module there should not be the need for these constructs because "one module" should be relatively small to be manageable. Otherwise it might be a strong indicator that the project could use some modularization refactoring. However, I know very well that some projects of "large-scale companies" can be a huge mess where no 'good' option is available. I guess you are talking about a situation where packageA is owned by a different team than packageB... and this is generally a very bad design decision due to the lack of separation and inherent dependency problems.

First of all, try to avoid the problem. As mentioned in @Henry's comment, don't use 3rd party libraries for trivial tasks.

However, we all use libraries. And sometimes we end up with the problem you describe, where we need two different versions of the same library. If library 'C' has removed and added some APIs between the two versions, and the removed APIs are needed by 'A', while 'B' needs the new ones, you have an issue.

In my company, we run our Java code inside an OSGi container. Using OSGi, you can modularize your code in "bundles", which are jar files with some special directives in their manifest file. Each bundle jar has its own classloader, so two bundles can use different versions of the same library. In your example, you could split your application code that uses 'packageA' into one bundle, and the code that uses 'packageB' in another. The two bundles can call each others APIs, and it will all work fine as long as your bundles do not use 'packageC' classes in the signature of the methods used by the other bundle (known as API leakage).

To get started with OSGi, you can e.g. take a look at OSGi enRoute.

Let me throw away a brick in order to get a gem first.

Alibaba is one of the largest E-Commerces in the world. And we tackle with these problems by creating an isolation container named Pandora. Its principle is simple: packaging those middle-wares together and load them with different ClassLoaders so that they can work well together even they referenced same packages with different versions. But this need a runtime environment provided by Pandora which is running as a tomcat process. I have to admit that this is a heavy plan.

Pandora is developed based on a fact that JVM identifies one class by class-loader plus classname.

Related