I came to think that transitive dependencies are mostly evil. Really. They can be useful sometimes, if you keep them on a
tight leash. But chances are you’re going to shoot yourself in the foot sooner or later using them. First let me illustrate with severals use cases and examples that I’ve experienced first hand.
First example, you have a simple web project and use several libraries. And 3 or 4 of them happen to depend on Xerces as almost the whole word depends on Xerces for some reason. Now you buid a WAR file and your build system is nice enough to put all your dependencies, including transitive ones, in its WEB-INF/lib directory. Nice enough? Think again. You’re going to end up with 3 or 4 different versions of Xerces in there. Which one is going to be selected at runtime? Well, do you want to bet on whether your app server follows alphabetical order or not?
Second example, you’re using XDoclet. Or the Spring Framework. Or any other framework including several modules, some necessary, some optional. All those modules sort of depend on each other. So chances are that by pulling one, you’re going to pull everything. And usually people don’t think much about the way others are going to download their own project outisde of the regular distribution, they just include everything they can think of as their dependencies. And you end up with a lot of garbage that you will never use and beat the record of the biggest software distribution ever built.
Another example. You depend on project A. Project A depends on B. And B depends on C. So far so good. Now it turns out that the repositories containing all these dependencies are mostly a mess. So somebody just comes up and removes the version of C that B depends on. Your build is broken.
A last one. This one would be actually pretty funny if it wasn’t so pathetic. On Apache Ode we have a JBI wrapper to allow deployment in a JBI container. We’re using a Maven plugin from ServiceMix to build Service Assemblies. Now this plugin happens to depend on the whole ServiceMix engine because it also includes tasks to auto deploy and run the server directly. So we end up pulling all the ServiceMix project just for a plugin. Now here is the best part: ServiceMix uses Ode. It’s part of its dependencies. So when we build our stuff for the first time, we end up downloading all ServiceMix plus all OUR stuff that we’re currently building. How crazy is that?
Conclusion
Given all this mess, what’s a build system to do? I think the transitive dependency problem has no solution, there are some techniques that can be used to keep some control but deep down it’s really flawed. Because the dependencies that are right for you can’t be guessed, just like the code you’re writing can’t be all generated.
However I think we’re still going to add it into Raven. Yep, you heard me well. And there are 2 reasons why:
- It can save you a lot of time at the early stage of a project or for prototyping. It’s really nice to get a setup quickly up and running.
- I already know that it’s going to be the most asked for feature. Implementing every single stupid feature that people ask for is a bad idea. However this feature is ony partially stupid and it can also make sense (see above).
So to give you weapons against chaos, pain and despair (exageration is my friend), I’d like to keep transitivity under control to allow you to opt out at any time. To do that, the transitivity would be toggable, you’d be able to turn it off and then specify everything explicitly. When you choose to do so, as we’re all a bunch of lazy asses, Raven would let you generate a dependency declaration with all the transitiveness you need. You’d then clean it up a bit to fit your needs, adding rationality to an insane accumulation of libraries, and when it’s all pretty, include it in your build.
So any other strong opinion on transitive dependencies? Ideas?
Pictures by Kevin Day and Dadooron.