Hacker News new | ask | show | jobs
by orivej 2956 days ago
vgo was explicitly designed to balance out two needs: (1) the need to use known good versions of the dependencies, and (2) the need not to burden dependency consumers with meaningless constraints (especially with an upper limit on the version). This article shows* why the first need is important, but it does not give vgo the credit for satisfying it with its minimal version selection (and, indeed, for providing a more stable and hence reliable solution than maximum version selection), and it misses the value of the second need. In my experience, the upper limit on the minor version is most often arbitrary, and in some cases when it is not, a future minor version reverts the mistakenly introduced incompatibility. Therefore vgo approach has unique advantages over other version selection methods, and it should not be discounted for the lack of a feature necessary to provide them.

* The article says that "Prior to 1.4.0 there was one function of MaxMsgSize" which "had previously set the size on both send and receive" but it does not substantiate this claim, and it may be false since go-grpc 1.3.0 documents that "MaxMsgSize returns a ServerOption to set the max message size in bytes for inbound mesages" https://github.com/grpc/grpc-go/blob/v1.3.0/server.go#L166, and it has not changed in go-grpc 1.12.0 https://github.com/grpc/grpc-go/blob/v1.12.0/server.go#L228 which strongly suggests that this is not a bug.

2 comments

> it does not give vgo the credit for satisfying it with its minimal version selection (and, indeed, for providing a more stable and hence reliable solution than maximum version selection)

I did not argue that because it doesn't do that. In an effort to not spoil things, because Sam Boyer has done a lot of work on this and wants to write about it, I won't say to much.

MVS does use a maximum. It's the major version number in SemVer. It's implicit. You can't override it when dealing with transitive dependencies. For it to always be a safe value people have to always follow SemVer. Unfortunately, they don't. In a perfect world this would work. Unfortunately, people are fallible and we need a system that works in light of that.

vgo trades some safety (by not supporting upper bounds) for some utility (by not artificially limiting the lifetime of a released library). Yet package managers that do support upper bounds do not guarantee safety, because libraries may not specify upper bounds or they may specify too broad bounds. This is a trade-off, and I have not seen anything convincing about why vgo position on this trade-off is unreasonable.

Can you give an example where vgo prevents use of a library where another approach does not? The main difference in the expressive power between vgo and traditional approaches is that the latter can restrict your use of libraries together more. vgo does not need a perfect world: it is practical in the imperfect one.

> MVS does use a maximum. It's the major version number in SemVer.

So, we can not force a library that wants dependency v3 to use dependency v2 (and vice versa), even if the author of the library knows that it works with either v2 or v3. This is a loss of vgo. On the other hand, if another library can only work with v2, and yet another can only work with v3, vgo allows the use of both in the same application. This looks like an acceptable win for the price of that loss.

You are claiming that is a one year old issue in the grpc-go project.

But we are not seeing any change in that code (https://github.com/grpc/grpc-go/blob/master/server.go#L228) in the following releases. And I tried to look for an issue mentionning MaxMsgSize and found nothing. https://github.com/grpc/grpc-go/issues?utf8=%E2%9C%93&q=MaxM...

The fix that would restore the compatibility you claim seems obvious:

  	func MaxMsgSize(m int) ServerOption {
  		return func(o *options) {
  			o.maxSendMessageSize = m
  			o.maxReceiveMessageSize = m
  		}
  	}
This fix could have been applied in a minor release to restore compatibility without breaking the API.

So if this is an issue, why is this not reported?

Just had this issue recently with a Python library. It claimed to be "out of beta". A month later they broke half the public API moving from 0.3.0 to 0.4.0. Although to be fair, this project didn't claim to be using SemVer.
Breaking API between 0.* releases conforms to SemVer: https://semver.org/#spec-item-4
Interesting, I must have overlooked that before. Thanks for the info.
> In my experience, the upper limit on the minor version is most often arbitrary

In my experience, this is the dependency version that was used when testing the library depending on it. As soon as your tool swaps it out for a newer version, you actually run an untested combination. Yes, it should work. But as we all know, it often does not.

And then the tool does not even have a proper feature to enable you fixing it on your side (e.g., by pinning a whole dependency tree).

> And then the tool does not even have a proper feature to enable you fixing it on your side (e.g., by pinning a whole dependency tree).

vgo allows you to pin your transitive dependencies to the exact versions of your choice, as long as non of them require a dependency with a higher version than you prefer. (But then, do other dependency managers let you disregard version constraints of your dependencies?)

AFAIK vgo only allows me to pin individual packages, but not whole trees. How would I pin a dependency tree in vgo?
You can copy the output of "vgo list -m" (the list of transitive dependencies with the selected versions) into the "require" section of "go.mod" and increase the versions that you want to change. (The next invocation of "vgo verify" will delete the lines with versions that you did not change because they are implied by the lines with versions that were not deleted.)
thanks for the detailed explanation. That would at least provide a workaround.