Go 1.5's vendor/ experiment
medium.com
medium.com
Now, they release this half-baked solution which has no true advantages over the existing vendoring tools other than keeping our import path the same in your source file (which just makes things more confusing imho). No solution for dealing with dependency conflicts, not even tools to detect and manage them other than the compiler itself.
On top of that there's even more "magic keywords" that you need to learn which causes stuff not to behave as you expect, just like the magic comments.
I understand it is also a consequence of having a v1.0 and a backwards-compatibility guarantee, but nearly all features added after 1.0 seem to be added with as minimal effort as needed to get something working, which is not a good thing. It all feels tacked on, the easy way, and not thought out.
I expect that they don't even know what works yet, because NIH and "it didn't exist in the 1970ies".
> Another issue (hat tip to onetruekarl) comes up when 2 vendored dependencies also vendor their own copies of another depencency with the same name ...
So I still don't see why people are content with having a vendoring solution that doesn't support multiple versions of the same package and strict ASCII name conflicts. I understand this is an experiment but it begs the question why it doesn't want to at least eventually bite off doing powerful things (it seems to handwave the hard problems with a future spec that kind might solve it - seemingly something probably orthogonal to this experiment).
I have really enjoyed what NPM did with a simple package.json file and enabling recursive dependencies. It seems like if go were to add a simple file that maps package name to package URL with a decent support for different URL protocols you could build the same recursive dependency graph and depend on multiple versions of the same package and also different packages with the same name.
The name conflict is a compiler error message, not anything to do with vendoring.
Having a package.json file is a separate issue, but there is a proposal for that here: https://groups.google.com/forum/#!msg/golang-dev/nMWoEAG55v8...
Having a file like that doesn't solve the vendoring issues though. This new vendoring scheme seems almost identical to NPM's node_modules.
This shouldn't cause an error, because packages can only register globals within their own namespace. You just end up with two copies of it, with different types.
That will collide with any other package that also uses that registration function for the same value of 'name'.
That is basically guaranteed to lead to bugs.
This seems pretty solvable though. If you vendor a package, you need to make sure you don't do this. If we do gb-style everyone vendors stuff themselves (not in libraries), then it's not an issue.
I think you could easily build something like that on-top of 1.5's vendoring.
Having used node and npm on both small (~200 transitive deps) and large (~1000 transitive deps) projects, I think this is an approach that's conceptually not a good idea (outside of possible implementation issues with npm).
With vendoring, end up with a _very_ large tree of transitive vendor'ed dependencies. You have no idea or control over what's in it, and what versions of what packages are in it.
Nearly all of the time, the two allegedly incompatible versions of a library that you install separately turn out to be 2.0.13 and 2.0.14, and you only get two because well, somebody hasn't upgraded yet.
This might seem trivial, but in the end you have a project tree where every dependency gets installed separately for everything that depends on it, so you end up with something like O(number of deps * number of deps) installed libraries, which obviously doesn't scale.
You also end up with the versioning problem. Imagine there's an important reason to upgrade some package P, e.g. a security issue. Now you have to hunt down every single project in your transitive dependencies that depends on it, and ask them to upgrade or fork.
You also still have version issues. If you transitively depend on the same library C in versions Cv1 and Cv2, and you want to pass an object/data structure/... from Cv1 to Cv2 through some path, you open yourself up to all kinds of hilarious version mismatches and failures.
This isn't new - e.g. Eclipse/OSGi did the same thing with separated class loaders, and it was a mess.
I think flattening your dependency/vendor tree to only ever have one version of library C, and hoping/testing for minor version mismatches to work out, is the most viable thing to do.
Are there any statically typed languages that solve the diamond dependency issue elegantly?
The result being that unless you hit a particularly crappy piece of software, it's save to just pick the later version of two dependencies and use that.
It's a problem a human has to get involved with at some point no matter what.
foo v1.0.1
foo v2.0
foo v1.5Of course, the two versions are incompatible, so if one tries to get them to interact (e.g. pass a type from v1.5 into a function from v2.0), there'll be errors. However, these will be static type errors, instead of some strange runtime behaviour.
We ran into this with prometheus.io and godep. Vendoring in our libraries meant that types didn't match between the vendored version that the main server had and the vendored version coming form the library.
If there's a way to solve this, that'd be great but it means you're back to choosing which version is correct.
In general, I think a solution for this is long overdue, and look forward to trying out 1.5 in earnest.