Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: x/vgo: add // Unstable convention #25524

Closed
jeanbza opened this issue May 23, 2018 · 14 comments
Closed

proposal: x/vgo: add // Unstable convention #25524

jeanbza opened this issue May 23, 2018 · 14 comments

Comments

@jeanbza
Copy link
Member

jeanbza commented May 23, 2018

Note: This proposal is only pertinent in a vgo-world.

Proposal

Support a convention for describing structs, methods, functions, and interfaces as unstable with // Unstable. This convention might later extend to IDE support, Godoc formatting, and so on.

Rationale

Backwards compatibility is important for the Go ecosystem, but absolute backwards compatibility is a heavy burden for maintainers of libraries. Consider the case where a library author wishes to extend their library with a new function, but is unsure what the exact function signature will look like (alternatively, the behavior):

  • Good path cmd/cgo: fails with gcc 4.4.1 #1: author creates new package and module versioned to 0.0.1 to signify "experimental", and sticks it there. Each time they iterate on the API of this function they bump the minor version, breaking users (since it's in a non-v1 package and therefore non-stable, users should breakages).
  • Good path net: LookupHost is returning odd values and crashing net tests #2: author adds it to the existing package. Each time they iterate on the API of this function they bump the major version.
  • Proposal: Ok path: author adds it to the existing package, marking it as // Unstable. Each time they iterate on the API of this function they bump the minor version, breaking users (since it's marked unstable, users expect breakages).
  • Bad path: author says, "Bah!" and sticks it in the existing package. Each time they iterate on the API of this function they bump the minor version, breaking users (since there is no signal that this is non-stable, users don't expect this and get burned).

The first two paths are very onerous; creating new packages and unnaturally segregating code is not a great experience, and bumping major versions for small things is not a great experience either. On the other hand, the "Bad" path of choosing to break users is not a good experience for users of libraries. This proposal gives library maintainers some room in the middle.

@gopherbot gopherbot added this to the Proposal milestone May 23, 2018
@Merovius
Copy link
Contributor

Why does this need any sort of convention or tooling-support? i.e. why not just state in the godoc comment, that an API is unstable?

@jeanbza
Copy link
Member Author

jeanbza commented May 23, 2018

Why does this need any [...] tooling-support?

For the same reason that // Deprecated needs tooling support - to give users a warning about the potential downside of using a method/function/etc.

Why does this need any sort of convention [...]

To enable tooling, and to standardize on a single place where a user would discover instability notices. At the moment it varies library-to-library; some at the top of a package, some in the comments, some in the comments with different verbiage, some in a README, and so on.

@Merovius
Copy link
Contributor

to give users a warning about the potential downside of using a method/function/etc.

I'm sorry, I was being imprecise: Under what circumstances would you want to show these warnings? i.e. what are the use cases for tooling taking advantage of this annotation? I don't want build-warnings whenever I build a project.

@jeanbza
Copy link
Member Author

jeanbza commented May 24, 2018

I don't want build-warnings whenever I build a project.

Agreed - I also don't want build warnings. This proposal does not suggest that; merely a convention for marking instability. This probably amounts to a blog post and some note in golang.org. The downstream change is twofold:

  • Hopefully IDEs will provide some visual indication to users about the instability, as most do for deprecation.
  • Library maintainers adopt standard terminology and location for marking package instability.

Under what circumstances would you want to show these warnings?

These are not warnings. I loosely used the phrase "to give users warning" earlier, apologies for any confusion. I meant that in a loose sense, not in the literal "I'm proposing a build warning feature" sense.

@agnivade agnivade changed the title proposal: add // Unstable convention proposal: x/vgo: add // Unstable convention May 24, 2018
@Merovius
Copy link
Contributor

Hopefully IDEs will provide some visual indication to users about the instability, as most do for deprecation.

I consider "unstable" and "deprecated" fundamentally different things. Using something unstable is a valid choice and once made, there isn't really anything distinguishing that code from other code. Using something deprecated should never happen voluntarily and it's warranted to be more alarming about changing it. i.e. "Deprecated" is absolutely always actionable, whereas "Unstable" seems to me to ~never be actionable. Except for the initial discussion of "should we use this", which seems just as well served by an unstructured mention in godoc to me (because before using an API, you will likely read its docs).

Not all properties of Go code have to be machine-discoverable. On the contrary, we probably want to limit the amount of machine-readable special-format comments to prevent it from becoming a meta-language. IMO "Unstable" doesn't pass the bar on usefulness.

That being said, I only have mild opinions about this, so I probably have said what I came to say and stay out of the rest of this issue :)

@bcmills
Copy link
Contributor

bcmills commented May 24, 2018

Unstable APIs are volatile: any caller of an unstable API can break — even fail to compile! — at any future release of that API, and compilation failures affect the whole package. If you want to be strict about semantic versioning, it's ok to export an unstable API from a stable package, but it's not ok to use an unstable API in a stable package‌ — even if it is only used in other unstable APIs. (For that reason, Google's Guava project strongly recommends that libraries do not use “beta” APIs.)

So a warning is actually pretty useful, if all of these conditions hold:

  • The package using the unstable API is not main, and is not an internal package for a main. (Within main, you can always just stick to the last version that supported the API: that won't block anybody else from upgrading.)
  • The version in which the unstable API is used is not itself unstable: that is, not a pre-release or a v0.

That said, I don't see a way to detect the latter condition during development, because version tags are usually applied after the fact. Perhaps that implies that the warning should be in some version-tagging tool rather than the IDE? (As far as I understand, cmd/api is one such tool.)

@cznic
Copy link
Contributor

cznic commented May 24, 2018

So a warning is actually pretty useful, if all of these conditions hold:

If a warning is actually pretty useful, the compiler should emit it, shouldn't it?

Oh wait.

@bcmills
Copy link
Contributor

bcmills commented May 24, 2018

It's also worth noting that Unstable is just one endpoint in a continuum of stability policies. “Unstable” is one extreme, and “frozen” is the other. Some examples in the middle include:

  • Go 1” APIs, which allow certain additions (such as methods and struct fields) but guarantee stable type signatures.
  • “Call-only” APIs, which guarantee compatibility with call sites but not exact signatures. For example, functions in such an API can add a trailing variadic parameters, change parameters from concrete types to interfaces or from interfaces to narrower interfaces, change return values from interfaces to wider interfaces or concrete types, etc. Abseil is an example of a (non-Go) library with such a policy.
  • Named-constant APIs, which guarantee that named constants will continue to work for particular parameters but do not guarantee that their underlying types or values will remain stable. (Portable system calls tend to follow a similar policy, but across platforms rather than versions.)

@pongad
Copy link
Contributor

pongad commented May 24, 2018

I completely agree with earlier posts about build warnings being an overkill. I'm not even sure if using unstable API should even be a vet warning. I think it makes sense to warn when using deprecated APIs; presumably you can use something else to silence the warning. When using unstable API, there might not be anything for you to move to.

I think there's value in godoc.org displaying "Deprecated" and "Unstable" in bold though. It should catch attention without hurting the eyes too badly. Even if that's all we get from the convention, I think it's a net win overall.

@bcmills Just as we have // Deprecated: description, we can also describe the "degree of unstability" right? We can even make a wiki page for common ones, then write

// Unstable: Method calls should keep working, but exact signature might change. See <wiki link>

I'm slightly concerned that this might encourage people to make unstable APIs and not stabilize them. Does anyone else feel the same way?

@jeanbza
Copy link
Member Author

jeanbza commented May 24, 2018

I'm slightly concerned that this might encourage people to make unstable APIs and not stabilize them. Does anyone else feel the same way?

I do, although I figure this is something that will happen whether there is an // Unstable convention or not. At least with // Unstable you know what you're dealing with.

@Merovius
Copy link
Contributor

@bcmills

For example, functions in such an API can add a trailing variadic parameters, change parameters from concrete types to interfaces or from interfaces to narrower interfaces, change return values from interfaces to wider interfaces or concrete types, etc.

nit: I wouldn't include changing return types in this list due to type-inference. A classical example would be, to change an error return to a FooError return - if it's used in a context of

x, err := Foo()
// ...
y, err := Bar()

this change would break, because the types of err in the second assignment won't match.

@bcmills
Copy link
Contributor

bcmills commented May 24, 2018

nit: I wouldn't include changing return types in this list due to type-inference.

I was wondering if anybody would call me on that one. 🙂

It turns out that “call-only” is actually (at least) two points on the spectrum. One is “call-only, but inferred types are stable”, and the other is “call-only, and don't make assumptions about inferred types”.

@rsc
Copy link
Contributor

rsc commented Jun 4, 2018

In Go, // Deprecated does not mean "going to break or disappear in the future". It just means "there's something better you should be aware of." That is, the compatibility rules still apply to code with that comment. The comment really is informational.

I question the wisdom of // Unstable without serious enforcement behind it, like some kind of explicit opt-in to the unstable parts of an API. I do like what I understand Rust's approach to be, where individual parts of an API can be marked stable or not, but that's a much larger discussion to have at some later point and not much related to vgo at all.

I'm not sure there's much proposal to evaluate here and am inclined to just close this.

@jeanbza
Copy link
Member Author

jeanbza commented Jun 4, 2018

That's reasonable. I agree that stronger enforcement would be better - like some explicit opt in - but it's unclear to me what form that should be in. I'll close this issue in the meantime.

@jeanbza jeanbza closed this as completed Jun 4, 2018
@golang golang locked and limited conversation to collaborators Jun 4, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants