Tuesday, 14 August 2007

A badly packaged project will break builds!

I have stumpled across many comments on different blogs bashing maven for being a bad build tool. To me it seems that an era of "hate maven" is rising and many people seems to just blindfolded follow the horde of maven bashers.

It seems to me that people tend to forget what maven brings to a project.

  • Convention above configuration.
  • Dependency management.
  • A bunch of default plugins ready to be used. (You can argue the quality of all opensource plugins, but I really think it is getting better)
A thing like dependency management is one of the greatest benefits. Luckily I have forgot the old days where precious time was spend looking for dependencies need by other dependencies.

Although maven provides many benefits I ran into one of the things that one could call a bad side effect of using maven and its transitive dependency management.

The springframework is distributed in two different ways.
As a single artifact containing the complete spring distribution and as a much more fine grained distribution where each artifact contains a logical spring module.
As it all seems fine and interface21 probably think they are pleasing the broader audience by providing the different distributions, it brings a bigger problem to the surface.

By providing the same code in different artifacts you introduces the risk of a project to unintentionally depend on the same software in different versions.

In my case just adding the acegi 1.0.4 to my maven pom.xml broke our build - and I did not even configure or use any of the acegi code!

Ofcourse I wasn't late to blame maven. As the problem showed up by just modifying my pom.xml the problem couldn't be blamed on anybody else but maven.

Some exercises with the -X option shed some light on my problem:
Acegi 1.0.4 depend on the fine grained distributions of spring 2.0.4 modules, but as it happens I already had a dependency to the full spring 2.0.5 artifact. Dependency resolution interprets the to versions of the same software as different artifacts - and if you stick strictly to the maven dependency resolution rules they sure are different!

A dependency is defined uniquely as follows:

As the version changes, maven takes care of using the latest version. But with different artifactnames containing the same code, you are most likely leaving your users of your library with a classloading issue just around the corner.

Hence the cause of the problem was lying outside maven.

Now you would probably tell me that I should only use the fine grained spring dependencies - but I was probably lazy and sure others out there are lazy too.

My experience sums up to the following rules of thumb:
  1. Apply DRY to your artifacts distribution.
    If you repeat yourself in your artifacts you introduce risks (not the same type of risk though) as if you repeat your code.
  2. Choose your groupId wisely and do not change it again - ever.
After blaming Spring I can tell that Spring isn't the only one to break dependency rules of thumb.
Ehcache has changed its groupId between version 1.2.3 to 1.2.4 and bingo! You have to different artifacts from a dependency resolution point of view.

Even with the quirks I just described I still think maven dependency management it is fare better than the good old plain Ant days!

If you suspect that you have run the same problem as I did, I suggest you run maven with the -X option as it prints the complete dependency resolution tree. From there you can find what transitive dependencies to exclude.
If you prefer Ant you should take look at the dependency management tool Ivy, but that subject is worth its own blogpost...


Tech Per said...

Isn't the "we hate maven" bashing period you mention over by now? I think we have come to "we like maven better than ant most of the time" period now :-)

If you abstract your actual problem away, don't you like fine-grained dependencies better than a mighty spring-all.jar?

Jacob von Eyben said...

@tech per:
Yes I do prefer fine-grained dependencies but as I wrote, people (read: I do) tend to "cut corners" from time to time when possible - in this case it came right back at me.

But the problem as I see it lies in the packaging of spring. They should not even distribute an all mighty spring-all.jar in the first place.
Using an all xyz-all.jar is like peeing to keep you warm.
As your project developes your pom.xml will quickly end up with a lot of transitive dependency 'excludes'.