5 ms·
I once wrote a static checker for Java. It found all breaking changes in APIs of all my dependencies. Any time functions were removed, parameters were removed,
by voodootrucker 9y ago
I once wrote a static checker for Java. It found all breaking changes in APIs of all my dependencies. Any time functions were removed, parameters were removed, type signatures changed, it would catch them.
It would walk the maven dependency tree to find these, any time if found two versions of a library (say app depends on A and B, A depends on Guava 17, B depends on 18), it would log it as an resolvable issue.
On the project I tested it against, I remember it logging something like 250-1000 issues. I gave up. I'm not even sure where the code is now. Java is supposedly statically checked, but it lacks a real linker, as anyone who has ever seen a run time exception of "XXX is not a function" can tell you.
As far as Maven being "unpredictable", how it would resolve transitive dependency conflicts prior to 2.0 was undefined behaviour, and post 2.0 it is bizarre/lazy at best. It doesn't use the newest version, and doesn't flag major version conflicts. It just topologically sorts the graph and picks the highest dependency. The logic is bizarre. I have no idea if gradle handles it better, but they both face an unsolvable problem.
Shade is a pretty terrible work around. Auto-sandboxing conflicting dependencies in their own class loaders would be the most reasonable solution. As long as return values weren't passed between libraries.
Edit to add:
And this is not a theoretical problem either, as soon as you bring in a big dependency like Spring or Spark you are already 20+ levels deep in the tree, with dozens of these sorts of library conflicts, and hundreds of broken method signatures, just waiting to step on the wrong land-mine. (Not that I bet large ecosystem is any better)
- euvitudo 9y agoI use maven-enforcer-plugin to force resolution of dependency conflicts. It's a pain at first to go through and add all the exclusions. But then I don't have dig through the dependencies in, e.g., a war, to find duplicate versions of a package. Edit: Then I cross my fingers hoping there isn't a breakage or unexpected behavior somewhere.
- voodootrucker 9y agoMaven enforcer is a good idea for sure, but as I recall it just notifies you of a version issue, and allows you to explicitly select one or the other. Sadly, these breaking changes can be mutually exclusive: e.g. a method was renamed that A & B both use, so no matter which version you pick something fails. I remember once having to do a binary search through versions of Guava to find one between 15 and 18 that happened to be the one that was "just right".
- watwut 9y agoHow did your static checker distinguished between API and internals? How do you even do that automatically?
- voodootrucker 9y agoIf I recall correctly, simply by checking only public methods, and assuming protected / private / package private were internals.
- watwut 9y agoYou got a lot of false positives then. Protected does not imply internal and public does not imply it is not internal. The issue here is javailable packaging system, people need to do non-API public methods.
- oweiler 9y agoThat was one of the reasons I've switched to Gradle. By default it uses the latest version, and you can even define the strategy for resolving dependencies.
- marcodave 9y ago> big dependency like Spring a big offender here are the spring boot starter meta dependencies, which are a very opinionated set of mutually compatible dependencies, but God forbid that you want to use a newer version in your own project, e.g elasticsearch 5 vs the "officially compatible" 2.4. welcome to jar hell