The key thing to remember is the transitive nightmare of dependencies that you let Maven manage is largely created by the way Maven works!
So if you don't do it that way, just you need to manage the dependencies yourself, but they are much much simplier!
In a bit more detail - at the top of your source code is:
import someorg.somepackge.class;
If you have the source code for someorg.somepackage in your sourcepath/classpath ( and the source of any transitive dependencies ) then the compiler will magically find all the transitive dependencies for class at the class level at compile time. [1]
This results in the minimal number of classes you have to ship and as a result the minimal number of transitive dependencies.
Now your build tool/script ( whatever tool you use ) will need to bring in those dependencies from a versions repo somewhere - but that can be your own source code repo ( vendoring I think it's called ).
Yes - you need need to keep that build config yourself - but frankly that's time well spent as it results in you have proper control over your dependencies, and they don't balloon out of control.
Occasions like some random third party has added dependency D to C which is brought in via an A->B->C chain and then you have some library version clash are much much less as a result.
That's not to say I don't occasionally use jar files in the classpath - but that's typically only if that library is self contained, single focus and small - and again you can put that in your build script.
In my view, Maven magic is part of the problem, not a solution - and to sum up why - it hides the cost of the dependencies - if people had to manually add the chain of true dependencies when they decided to use a apache utility class, they might think twice about whether that chain of dependencies is well designed or not.
[1] With the reflection proviso I mentioned above.
So if you don't do it that way, just you need to manage the dependencies yourself, but they are much much simplier!
In a bit more detail - at the top of your source code is:
import someorg.somepackge.class;
If you have the source code for someorg.somepackage in your sourcepath/classpath ( and the source of any transitive dependencies ) then the compiler will magically find all the transitive dependencies for class at the class level at compile time. [1]
This results in the minimal number of classes you have to ship and as a result the minimal number of transitive dependencies.
Now your build tool/script ( whatever tool you use ) will need to bring in those dependencies from a versions repo somewhere - but that can be your own source code repo ( vendoring I think it's called ).
Yes - you need need to keep that build config yourself - but frankly that's time well spent as it results in you have proper control over your dependencies, and they don't balloon out of control.
Occasions like some random third party has added dependency D to C which is brought in via an A->B->C chain and then you have some library version clash are much much less as a result.
That's not to say I don't occasionally use jar files in the classpath - but that's typically only if that library is self contained, single focus and small - and again you can put that in your build script.
In my view, Maven magic is part of the problem, not a solution - and to sum up why - it hides the cost of the dependencies - if people had to manually add the chain of true dependencies when they decided to use a apache utility class, they might think twice about whether that chain of dependencies is well designed or not.
[1] With the reflection proviso I mentioned above.