Live data from Hacker News

Backwards compatibility in Go (2015)

blog.merovius.de

31–36 of 36 posts

Re: Backwards compatibility in Go (2015)

#31

Earlier quoted context omitted.

Most of these are treated as backwards incompatibilities by the stdlib folks and most users. The one that's not is that adding a field or method could break importing code. The scenario here is that you embed two types, one from the upgrading lib and one not, and the upgrading lib adds a name that collides with one in the other type. Rather than pick a winner in that conflict (last embed in the type definition wins,…

> The practical backwards compatibility things that I have seen come up Yes, I'm not really talking about practical problems :) This article is mainly a response to various people claiming, "backwards compatibility is easy, it's a breakage if you are breaking the build". FWIW, most of the scenarios you mention I wouldn't really call a backwards compatibility issue either, though. I'd say they are bugs that are expose…

Yeah. I'm trying to say, sure, that's a clever note on what can, in very precise circumstances, happen whenever any field/method is added. But as working engineers, focusing on the practical problems is, well, the practical thing to do.

If what you're thinking is "well in that case, people should agree that adding methods/fields is OK," I think they do, and, e.g. https://golang.org/doc/go1compat spells out that methods may be added and how that can interact with multiple embedding.

Practically, I don't want my upstreams to stop adding methods or fields to structs or to create an extra step they have to go through before adding one. That slows down upstream dev; it's not a net win for me as a downstream or a cost I want to impose on (sometimes-unpaid) OSS maintainers.

I also don't want a tool to flag every upstream field/method addition as a breaking change, because that's noise to me in the common case where the change doesn't break _my_ program. Really, if I want a back-compat test, I should upgrade and try to run my code's tests since that can shake out many more kinds of break, including ones due to my bugs/my dependencies on undoc'd stuff. I should do it whether the API was tweaked or not, because any change can change behavior.

Again, you did make a clever observation of a potential build break; I'm just saying I don't think that Go library authors need to spend more time worrying about potential name collisions with multiple embedding, or that people should specifically build workflows and tools around it.

Re: Backwards compatibility in Go (2015)

#32
post #30

Earlier quoted context omitted.

What I was trying to illustrate is, that the notion of versioning is broken in and off itself. By the lessons of the article, pretty much every API change is a breaking change, so you would constantly need to increment the major version, if you take SemVer seriously, meaning minor versions don't exist, de facto. And if you now imagine that you'd need to touch your code every time one of your dependencies increments t…

A good chunk of that is that Go, by design, makes nearly all changes breaking (as you covered pretty well). I doubt they did so intentionally, but they seem to focus their decisions on small-ish scale projects at the expense of large / longer-term ones, and this is the natural consequence. I think the focus here makes sense, and improves lots of useful things in practice (which is why they do it - Go focuses on pragm…

> they seem to focus their decisions on small-ish scale projects at the expense of large / longer-term ones, and this is the natural consequence.

This is - excuse me - a pretty ridiculous claim, given the explicit design goals of go. https://talks.golang.org/2012/splash.article You might disagree, that their choices are furthering these design goals (I don't), but claiming that they focus on small-ish projects is just non-factual, most design decisions are driven by a focus on large (both in problem- and code size) projects.

> A lot of the problems are solved by "hit it until it compiles"

Neither do I want to need to fix compilation errors in software that I use, nor do I want my users to have to do it. This is not a solution to the problems at hand.

> The rest (adding methods -> you may collide with an interface which you didn't before) can probably be detected

No, they can't. From looking at your code you only get an outwards pointing import-arrow. There is no way for you to know which code actually relies on your API and in what way. You might, say, limit your guarantees to packages on godoc.org (which is indeed what I do in practice), but it's still hardly a solution to the problem.

> If you manage to limit most of your changes to "can be automatically changed / detected", you have a fair bit more freedom.

This is indeed the path I'm currently following; for every breaking change, provide an automatic fix. It still isn't an actual solution, though, just a halfway decent workaround. It especially doesn't work without https://github.com/golang/go/issues/18130 and a couple of other needed changes.

Luckily, go has immensely powerful tools to perform these kinds of rewrites (much more powerful than any other language I'm aware of) and I'm thus pretty optimistic that this will provide a good way forward soon (now that the need for gradual repair is officially recognized).

Re: Backwards compatibility in Go (2015)

#33

Earlier quoted context omitted.

> The practical backwards compatibility things that I have seen come up Yes, I'm not really talking about practical problems :) This article is mainly a response to various people claiming, "backwards compatibility is easy, it's a breakage if you are breaking the build". FWIW, most of the scenarios you mention I wouldn't really call a backwards compatibility issue either, though. I'd say they are bugs that are expose…

Yeah. I'm trying to say, sure, that's a clever note on what can, in very precise circumstances, happen whenever any field/method is added. But as working engineers, focusing on the practical problems is, well, the practical thing to do. If what you're thinking is "well in that case, people should agree that adding methods/fields is OK," I think they do, and, e.g. https://golang.org/doc/go1compat spells out that metho…

> But as working engineers, focusing on the practical problems is, well, the practical thing to do.

Well, yeah, I'm an engineer too (just educated as a mathematician) :)

FWIW, I tried to solve an engineering problem, namely "how can we get the advantages of SemVer, while working around human deficiencies in setting and maintaining them". Or "why should a human need to figure out a version number, if a computer can do it for me". But, the thing is, that this turned out not to actually be an engineering problem, but a political problem; if there is no obviously correct interpretation of "breaking change", then for a tool to be acceptable, you'd need to get people to accept it's limited implementation of "breaking". I simply wasn't willing to put up with this political challenge (others where) :)

> If what you're thinking is "well in that case, people should agree that adding methods/fields is OK," I think they do, and, e.g. https://golang.org/doc/go1compat spells out that methods may be added and how that can interact with multiple embedding.

True. AFAIR that section was added around the time I wrote that article (I believe it was in tip a couple of days beforehand).

> Practically, I don't want my upstreams to stop adding methods or fields to structs or to create an extra step they have to go through before adding one. That slows down upstream dev; it's not a net win for me as a downstream or a cost I want to impose on (sometimes-unpaid) OSS maintainers.

I agree.

> I also don't want a tool to flag every upstream field/method addition as a breaking change, because that's noise to me in the common case where the change doesn't break _my_ program. Really, if I want a back-compat test, I should upgrade and try to run my code's tests since that can shake out many more kinds of break, including ones due to my bugs/my dependencies on undoc'd stuff

But you, as the author of a package, are not really the target audience either. You are able to fix compilation bugs, but your users likely are not.

But yeah, you probably also just aren't the addressee of my article. It is mostly addressed to people claiming that versioning in go is a solved problem and SemVer is the solution. It's not.

Your observation is pretty much the point of the article; a breakage happens, iff my code doesn't work anymore with an upgraded dependency, not more, nor less.

> Again, you did make a clever observation of a potential build break; I'm just saying I don't think that Go library authors need to spend more time worrying about potential name collisions with multiple embedding, or that people should specifically build workflows and tools around it.

Here, I disagree. Tooling to work around breakages would be excellent. As you mentioned yourself, for most breakages you just have to make the compiler happy and for most breakages it's pretty trivial to figure out what's needed. And at that point, there really should be a tool to do the job; after all, you should never send a human to do a computer's job. :)

Re: Backwards compatibility in Go (2015)

#34
post #30

Earlier quoted context omitted.

A good chunk of that is that Go, by design, makes nearly all changes breaking (as you covered pretty well). I doubt they did so intentionally, but they seem to focus their decisions on small-ish scale projects at the expense of large / longer-term ones, and this is the natural consequence. I think the focus here makes sense, and improves lots of useful things in practice (which is why they do it - Go focuses on pragm…

> they seem to focus their decisions on small-ish scale projects at the expense of large / longer-term ones, and this is the natural consequence. This is - excuse me - a pretty ridiculous claim, given the explicit design goals of go. https://talks.golang.org/2012/splash.article You might disagree, that their choices are furthering these design goals (I don't), but claiming that they focus on small-ish projects is jus…

Yeah... no, they're not focusing on large. Look at the vendoring hell, the weak typing / nonsense around no-generics vs oh-but-maps-are leading to error-prone switch statements, and lack of control-of-the-language. Larger than Python projects tend to support without pain, absolutely (and that's an important area), but not large. Service-units in a large system, sure, which is likely what many will use it for, but it's just not friendly to millions of lines of code and tens or hundreds of developers. Their solution to things getting out of hand or having performance problems is to divide the system, not to have a system that can handle it.

edit: oh boy, right at the top, a winner: Go's design considerations include rigorous dependency management, ...

Heck no it doesn't. It has been hacked on by the community, which has thankfully adopted things like Glide and GPS that do things properly, because it abandoned this whole area. As it did with e.g. debugging, for which their official stance is "use GDB, which doesn't really work".

Re: Backwards compatibility in Go (2015)

#35

Earlier quoted context omitted.

Yeah. I'm trying to say, sure, that's a clever note on what can, in very precise circumstances, happen whenever any field/method is added. But as working engineers, focusing on the practical problems is, well, the practical thing to do. If what you're thinking is "well in that case, people should agree that adding methods/fields is OK," I think they do, and, e.g. https://golang.org/doc/go1compat spells out that metho…

> But as working engineers, focusing on the practical problems is, well, the practical thing to do. Well, yeah, I'm an engineer too (just educated as a mathematician) :) FWIW, I tried to solve an engineering problem, namely "how can we get the advantages of SemVer, while working around human deficiencies in setting and maintaining them". Or "why should a human need to figure out a version number, if a computer can do…

> I'm an engineer too

Yep! Point is, we all are, so we all have to worry about how things break in practice, not only about the theoretical model of compatibility.

And it turns out we break each others' code lots of ways, sometimes even just changing behavior without touching the API. If I had a tool that detected certain build-time problems and worked around others, I could still end up upgrading to a new version of a library that breaks my product. So we end up with vendoring and such where product maintainers sort it out, and probably will continue to need some humans in the loop as long as releases have bugs.

I do think there's plenty to do on the larger issue of compatibility even if not specifically focused on this particular build-time break. There are tools I'd love to see, e.g. to test my program with its deps updated and maybe even do something git-bisect-ish to find just where things went wrong. (Node has something along those lines named Greenkeeper.) Peter Bourgon's got a group working on Go package management, and there's been work done, both practical (e.g. the tools and practices at https://peter.bourgon.org/go-best-practices-2016/#dependency...) and theoretical (https://research.swtch.com/version-sat). Useful progress and interesting stuff.

Re: Backwards compatibility in Go (2015)

#36
post #34

Earlier quoted context omitted.

> they seem to focus their decisions on small-ish scale projects at the expense of large / longer-term ones, and this is the natural consequence. This is - excuse me - a pretty ridiculous claim, given the explicit design goals of go. https://talks.golang.org/2012/splash.article You might disagree, that their choices are furthering these design goals (I don't), but claiming that they focus on small-ish projects is jus…

Yeah... no, they're not focusing on large. Look at the vendoring hell, the weak typing / nonsense around no-generics vs oh-but-maps-are leading to error-prone switch statements, and lack of control-of-the-language. Larger than Python projects tend to support without pain, absolutely (and that's an important area), but not large . Service-units in a large system, sure, which is likely what many will use it for, but it…

> the weak typing

What do you mean?

> nonsense around no-generics

What nonsense?

> error-prone switch statements

What makes them error-prone?

> lack of control-of-the-language

What do you mean?

> not large. Service-units in a large system, sure, which is likely what many will use it for, but it's just not friendly to millions of lines of code and tens or hundreds of developers.

There are go-projects like this on github. Not to mention the fact that several large companies run central parts of their infrastructure on go (I recently started working on a go-project that qualifies). I think your belief that the flaws of go disqualify it for large projects is biased on a certain, subjective perception to do things, not the objective absolute truth you are trying to make it out to be.

But even if it was correct, at best you are arguing that their efforts to focus on large software was in vain, but claiming that they don't focus on large software at all is, again, simply completely false. Please try to be constructive, here.

> Their solution to things getting out of hand or having performance problems is to divide the system, not to have a system that can handle it.

Can you give an example?

> As it did with e.g. debugging, for which their official stance is "use GDB, which doesn't really work".

I don't believe that's the official stance anymore. And IMHO, most large applications don't particularly profit from an interactive debugger anyhow (I can't just attach a debugger to a server serving thousands of requests per second and have any hope of getting useful answers). The debugging story of go is much more bound to things like "import _ net/http/pprof" and the like and systems like stackdriver.

Anyway. All of this is pretty off-topic, anyway.

Post reply on HN