It has a crucial limitation compared to "real" dependency managers like Ivy, Maven, NPM, Leiningen, pip, et al.: you can't declare a dependency on a particular version of a module, only on the trunk. So you have to either maintain local mirrors of every single module, or just give up on reproducible builds and accept that upstream might introduce a bug or a breaking change at any time.
The Go developers are aware of this limitation, and their typical recommendation on the mailing list is that you should never need to use anything other than the latest version of any third-party library, e.g.: https://groups.google.com/d/topic/golang-nuts/3gEseiCwdf0/di...
It's because it isn't a trivial problem to solve. What happens when A requires B1 and C, but C requires B2?
> The Go developers are aware of this limitation, and their typical recommendation on the mailing list is that you should never need to use anything other than the latest version of any third-party library
It's either that, or you pin the version you want in your own fork of the package. Which is not an unreasonable solution to the bitrot problem.
Regardless, this limitation doesn't stop the `go` tool from being awesome. It's easily one of the reasons why I use Go in the first place.
Certainly there will be cases where there are conflicts between different versions of packages, but Go's current behavior doesn't prevent them; it just makes them happen unpredictably. Instead of building against a specific known version, you're just using whichever one happened to be the most recent as of the first time you referenced it.
If B1 and B2 are really incompatible, then yes, there's a problem; but not being able to solve the problem completely in all cases doesn't mean we need to throw our hands in the air and not even try to handle the simple cases. I think it's telling that Google uses a custom build system for their Go projects, rather than "go get".
(And forking all of your dependencies is an option, but it's not quite that simple. In order to get truly reproducible builds, you have to mirror all of their dependencies as well, and patch all of your forked versions to fix their imports.)
> If B1 and B2 are really incompatible, then yes, there's a problem
It's not just incompatibility at the package level. Allowing them to simply coexist in the `GOPATH` ecosystem is problematic. It isn't technically difficult to offer a package tool that can handle different but compatible versions of a library, but it certainly increases the complexity of the system. The simple directory structure of `GOPATH` and its correspondence with import URLs would be lost.
> In order to get truly reproducible builds
Yes, if that's what you need then you'll have to pay that price. But not everyone needs that. For software I release, I only pin dependencies when they have a track record of breaking or don't update frequently enough for my purposes. In practice, it's pretty rare.
> If B1 and B2 are really incompatible, then yes, there's a problem; but not being able to solve the problem completely in all cases doesn't mean we need to throw our hands in the air and not even try to handle the simple cases.
That's ironic because that's exactly how I see the `go` tool. Package management is extremely difficult to get right, so instead of throwing their hands in the air, the Go devs put out a tool that is simple to use and has proven to work very well in practice.
The fact that the `go` tool is so simple is one of the reasons why I love it so much. If and when I need to be 100% confident in my builds, I'll happily pay the price to make it so.
> (And forking all of your dependencies is an option, but it's not quite that simple. In order to get truly reproducible builds, you have to mirror all of their dependencies as well, and patch all of your forked versions to fix their imports.)
It's not only an option, it's a practical necessity to freeze the dependency tree locally so you can run a rebuild when someone's knocked the remote repository offline. Doesn't everyone do this?
I agree that it is not trivial but the ruby bundler project seems to manage it reasonably well, why can't go?
Mind you ruby also has a central repository in the form of rubygems which allows for this kind of versioning.
Go, lacking the same thing, relies on people just entering github URLS which seems insane to me but could work I guess if everyone agrees to tag their projects using semantic versioning where appropriate. Then you could do something like person/project@v1.2.3
Mind you, of any of the companies on the planet I could think of that has the resources to to create, manage and maintain a rubygems equivalent for go, Google would be top of the list.
> Go, lacking the same thing, relies on people just entering github URLS which seems insane to me but could work I guess
There's no need to guess. It works. Almost the entire Go ecosystem uses it.
> Then you could do something like person/project@v1.2.3
This has been suggested. But it's just a mechanism for versioned dependencies; it doesn't address the transitive dependency problem that I cited.
As of right now, if you need to pin a dependency to a particular version, then you fork it yourself. I welcome this infrequent pain with open arms to the benefit of keeping the `go` tool simple.
It's not like people haven't tried to setup a central repository with versioned packages [1], but it just hasn't caught on.
> There's no need to guess. It works. Almost the entire Go ecosystem uses it.
For a given definition of "works". Personally speaking, my "good" has not been met but of course, each to their own. The "Fork it if you want version x" workaround also would not satisfy me.
Google has the resources, reach and authority to easily setup a central repository. If they did then I would not be surprised to see people start using it like CPAN and rubygems.
You say you do not want the go tool to become complex but there is no reason the dependency management side of things cannot be done with a separate tool to keep things segregated.
The current tool seems to roughly match the ruby 'gem' command (albeit with-out a central repository). I am surprised google has not created an analogue of the ruby 'bundler' gem with its 'bundle' command.
> You say you do not want the go tool to become complex but there is no reason the dependency management side of things cannot be done with a separate tool to keep things segregated.
> It's either that, or you pin the version you want in your own fork of the package. Which is not an unreasonable solution to the bitrot problem.
And if that package references another package on github understandably I need to pin it too, but do I also need to modify all of the imports in the original third party package I wanted to reference the pinned dependency?
I guess I don't understand why it's expected that I would run production code that imports from a third party automatically. I don't want to deploy updates some random person committed today to some third party package I'm using or some package it depends on, so if I want to use a third party package I have to recursively pin it and its dependencies and recursively modify import statements.
burntsushi is completely right. Declaring dependencies on specific versions is peachy until it isn't.
Along those lines, did you know that Maven2 (and possibly in Maven 3) it's possible to have dependencies that are pulled into your bundle that are at a lower version that specified in a top level pom? Yup, versioning is really hard.