Re: [go-nuts] gocompat - Backwards compatibility checker for Go APIs

332 views
Skip to first unread message
Message has been deleted

Axel Wagner

unread,
Dec 3, 2015, 2:24:07 PM12/3/15
to s2gatev, golang-nuts
Hi Stanislav,

first of all, awesome work :) I am sure the community will make use of
this :)

I started doing the same thing about six month ago. It came to a halt,
however, when I started thinking about, what "compatibility" actually
means. My personal conclusion was, that it is not possible to give an
answer to that question that is both useful and unarguably correct [0],
so I abandoned my efforts.

From that context, I would be interested in knowing what definition of
compatibility you based your tool on, i.e. what changes would be flagged
as "breaking" and what changes would be flagged as "non-breaking" and
what was the reasoning behind that decision?

Best,

Axel

[0] http://blog.merovius.de/2015/07/29/backwards-compatibility-in-go.html

s2gatev <ma...@s2gatev.com> writes:

> Hello,
>
> *Disclaimer:* This is my first post in the #golang-nuts list so please bear
> with me if I am not following conventions - I will learn.
>
> I have had this idea for some time and finally decided to sit down and code
> it. I have not researched if any similar solutions already exist.
>
> *The idea:* Checking interface backwards compatibility of a project based
> on its exported vars, structs, interfaces, funcs, etc. and their types. The
> tool generates an index of these objects based on a Go codebase and this
> index could be later compared/updated to newer versions of the codebase.
> *The repository:* s2gatev/gocompat <https://github.com/s2gatev/gocompat>
>
> Does this seem useful at all to you? I would be happy if I could receive
> your thoughts, suggestions, ideas, criticism, similar projects, etc.
>
> Stanislav
>
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
Message has been deleted

Ian Lance Taylor

unread,
Dec 3, 2015, 3:38:15 PM12/3/15
to s2gatev, golang-nuts
On Thu, Dec 3, 2015 at 2:06 AM, s2gatev <ma...@s2gatev.com> wrote:
>
> Disclaimer: This is my first post in the #golang-nuts list so please bear
> with me if I am not following conventions - I will learn.
>
> I have had this idea for some time and finally decided to sit down and code
> it. I have not researched if any similar solutions already exist.
>
> The idea: Checking interface backwards compatibility of a project based on
> its exported vars, structs, interfaces, funcs, etc. and their types. The
> tool generates an index of these objects based on a Go codebase and this
> index could be later compared/updated to newer versions of the codebase.
> The repository: s2gatev/gocompat
>
> Does this seem useful at all to you? I would be happy if I could receive
> your thoughts, suggestions, ideas, criticism, similar projects, etc.

You might get some ideas from cmd/api in the main Go repository. It's
used to verify the Go 1 compatibility guarantee.

Ian

Axel Wagner

unread,
Dec 3, 2015, 4:34:38 PM12/3/15
to s2gatev, golang-nuts
Hi Stanislav,

s2gatev <ma...@s2gatev.com> writes:

> Hey Axel,
>
> Thanks for getting in touch!
>
> I am doing basic compilation compatibility verification. This means that a
> newer version of the interface is compatible with the older if it doesn't
> break compilation of code that uses it. Here are the rules I based the
> compatibility checker on:
>
> 1. Packages should have the same names in the newer version.

That is obviously a given :)

> 2. Package should export vars and consts of the same names and types in
> the newer version. A var could become a const and vice versa, this won't
> break compatibility.

A reverse dependency could assign to a var, so making a var into a const
might break compilation.
A const might be used in a context where a var is disallowed (e.g. in an
array bound) so turning a const into a var might also break compilation.

> 3. Packages should export functions and methods with the same names,
> receiver type, parameter types and result types. A parameter type could
> become var arg (e.g. "int" -> "...int"), this won't break
> compatibility.

It might prevent a type from implementing an interface that it
implemented befor. Or someone might store a func in a variable of a
declared type. Both of these would then break, when you change an arg
into a var arg.

> 4. Packages should export types with the same name and definition.
> Structs should export fields with the same names and types. Interfaces
> should export methods with the same parameters and result types.

Unless that's a "subset", instead of an "equal", this seems reasonable
:)

> 5. I will be experimenting with some interface rules that allow you to
> "bump" a type to a less-strick interface type and this shouldn't break
> compatibility.

I'm unsure what you mean here. If you mean, for example, to add a method
to an interface, thus making it less strict (which is one way to read
that, maybe), this could also break compilation, because a reverse
dependency created an implementation of that interface and passed that
and suddenly the implementation doesn't satisfy it anymore. In the other
direction (i.e. removing a method, thus making the requirements on the
implementor less strict, which could be another way to read it), a
reverse dependency might use the type and try to call the removed method
and then gets broken. Either way, I don't see how any change to an
interface type can be guaranteed to not break any reverse dependency.

> If I understand you correctly you are thinking of deeper (than compilation)
> compatibility semantics.

No, not at all :) That's after all the halting problem and as such
theoretically impossible to solve.

To explain my position: I've been of the opinion (for a while now) that
*every* API change might break compilation in a strongly typed language
such as go. This would then mean, as a corollary, that a strict "we can
never break compilation" doesn't give a useful definition of
compatibility, as every change would then be a breaking change. So, to
get to a useful definition, you must allow *some* potential compilation
breakages -- and to be clear, that is absolutely fine, I can get behind
the quantitative difference between removing a type, which will likely
break something and adding a field to a struct, which will possibly, but
unlikely break something. But you must split compilation breakages into
two sets "probably breaks something" and "probably doesn't break
something", instead of "possibly breaks something" and "guaranteed to
not break anything".

My opinion is that currently the go community is lacking a consistent
agreement what these "probably non-breaking" changes are, that need to
be allowed. The go compatibility guarantee makes a start here by
excluding dot-imports, which then allows at least addition of new
Identifiers in the set of "probably non-breaking". I believe a tool such
as yours (or the go api checker that is used on the core repo) could go
a long way to *create* such a specific agreement which is
computationally checkable. Which is why I'm interested
in having a good documentation of what it's specific mapping of changes
into "probably breaks" and "probably doesn't break" is.


In particular, please don't understand my rattling of your particular checks
as devaluing your tool or discouraging further work on it :) I'm happy
to help as good as I can in enumerating the potential situations in
which a change could be breaking to assess the likelihood of breakages.

Best,

Axel
Message has been deleted
Message has been deleted

Eric Johnson

unread,
Dec 4, 2015, 8:24:04 PM12/4/15
to golang-nuts, ma...@s2gatev.com


On Friday, December 4, 2015 at 12:31:28 AM UTC-8, s2gatev wrote:
Hey Axel,

Thanks for the detailed answer! I am really happy to have your opinion on the topic as obviously you've spent some time thinking about it.

I read your blog post on the topic and think it has a place in this discussion.

The way I see it, it really is the case that every API change could potentially break something. Perhaps it is a matter of trade-off - how hard would it be for the reverse dependency to implement a new method? How hard would it be to deal with the removed type?

I'm starting to think that it'd probably be a good idea to have the tool present options for configuration:
  • Allow adding new vars/consts/types/funcs.
  • Allow adding fields to structs.
  • Allow adding methods to structs.
  • Allow adding methods to interfaces.
  • Allow adding var arg to struct.
  • etc.
This way it could be tweaked for the specific project (there are for sure needs that I will not think of while making the tool so configuration seems like the way to go).

Like many tools that look for code quality, probably best to default to something reasonable.

By default, allow:
  • New vars, consts, types, funcs.
  • Adding methods to structs (possibility of collision is very low)
  • Adding fields to structs (possibility of collision is very low)
At least, that's my take.

Eric.


This however contrasts with your opinion on having a community agreement on breaking/non-breaking changes. I'd like to explore this and have your thoughts on the topic. Why do you see this as a community responsibility and not a project-specific decision? Note: I'm not excluding both living simultaneously.

Regards,
Stanislav

Tommi Virtanen

unread,
Dec 9, 2015, 2:36:30 PM12/9/15
to golang-nuts, ma...@s2gatev.com
On Thursday, December 3, 2015 at 12:38:15 PM UTC-8, Ian Lance Taylor wrote:
You might get some ideas from cmd/api in the main Go repository.  It's
used to verify the Go 1 compatibility guarantee.

At least a few years back, the core "api" tool didn't work outside of stdlib, and I'm assuming that's still the case. The patch to make it work is pretty small -- here's an old (2+ years) branch that adds that support:


Reply all
Reply to author
Forward
0 new messages