
Keeping your Go modules compatible - stargrave
https://blog.golang.org/module-compatibility
======
poorman
> When you run into a case where you want to add a method to an existing
> interface, you may be able to follow this strategy. Start by creating a new
> interface with your new method, or identify an existing interface with the
> new method. Next, identify the relevant functions that need to support it,
> type check for the second interface, and add code that uses it.

I like this pattern, and I have used it. The issue I have is when I look at
the generated godoc, the original interface is in the signature. I don't know
the function accepts the new interface as well without either digging into the
code or a comment for the function.

~~~
anonymoushn
It seems like this convention invites us to create this sort of issue more
times:
[https://news.ycombinator.com/item?id=6060351](https://news.ycombinator.com/item?id=6060351)

------
enjoylife
I use Go daily and one of the aspects which is always a challenge is equality
checking. From the post,

> There is one subtle way a new field can break user code unexpectedly. If all
> the field types in a struct are comparable—meaning values of those types can
> be compared with == and != and used as a map key—then the overall struct
> type is comparable too. In this case, adding a new field of uncomparable
> type will make the overall struct type non-comparable, breaking any code
> that compares values of that struct type.

From experience, this is non-trivial to avoid in a long lived codebase.

It’s telling that 99% of the codegen tools used in Go, e.g. protobuf, thrift,
Gorm, etc. all output an isEqual method of some kind.

~~~
apta
Anyone who's worked on a large project and code base will appreciate the
features proper mature languages like Java or C# offer.

It's ironic that golang was supposedly designed for "programming in the
large", but it's just a very bad experience for non-trivial code bases.

~~~
kitd
Java has an _equals()_ method. What features are you thinking of?

~~~
pjmlp
\- Type safe enumerations

\- Generics (lets see if flyweight design actually makes it)

\- package names that don't depend on source locations

\- binary packages for proper encapsulation and even faster builds

\- both points together allow for components eco-system to thrive

\- more knobs to turn on when performance matters

\- a proper graphical debugging experience

~~~
kitd
I was really thinking about the problem described by GP. But in any case:

 _\- Type safe enumerations \- Generics (lets see if flyweight design actually
makes it)_

I'll give you both those. Generics would be the killer feature for me.

 _\- package names that don 't depend on source locations_

Java package structure mirrors the file system, no?

 _\- binary packages for proper encapsulation and even faster builds_

Go package binaries are cached after build. In any case, build speed is rarely
an issue for Go.

 _\- both points together allow for components eco-system to thrive_

One of the most impressive thing about Go IMO is the speed at which the
ecosystem has grown and is growing.

 _\- more knobs to turn on when performance matters_

I have found it takes a lot longer to reach the need for performance knobs in
Go than in Java.

 _\- a proper graphical debugging experience_

Not sure what you mean by "proper" and why it has to be graphical, but VSCode
+ Golang extension gives me enough insight into what's going on under the
covers at debug time, equivalent to eg Eclipse + Java debugger.

Just for the record, I love Java and have been programming in it for over 20
years. It has many great features and the ecosystem is unsurpassed.

Go is a much more practical language though IMHO. The time to value is much
shorter. Setting up a module project is one command. Writing & running tests,
complete with coverage and performance, is supported out of the box by the
basic "go" command. The standard lib is broad and deep. And as mentioned the
ecosystem is growing fast.

~~~
pjmlp
> Java package structure mirrors the file system, no?

No, it mirrors the directory structure, from import location, without any
reference to DNS servers or source code repositories.

All in all Go is a Java 1.0, with all plus and minus that it entails.

~~~
ridv
Just want to point out that Go can also be used without dealing with DNS
servers and repos in a similar way to Java.

The big difference is you have to place the code in the $GOPATH with the right
directory structure and disable mod (GO111MODULE=off)

------
naringas
there's something that I just don't get about golang modules, I don't know
what it is but they don't click with me

it might well be something tacit that golangers learn from each other
directly? or otherwise it's so obvious and simple and I just haven't realized?

it feels like it's the kind of thing that just clicks one day possibly after a
few years

~~~
m0th87
You're not alone. I've been writing go since before 1.0, and can handle
modules/packages in _any other language_ fine, yet go's regularly leaves me
stumped.

A little while ago we were hitting yet another issue with dependencies related
to version pinning at work. I tried to divine what the disparate versions
mean, and came up with this flow chart from a legalistic reading of stack
overflow issues [1] and the go modules doc [2]:

    
    
      has the package opted into go modules (i.e. is there a go.mod?)
        yes
          does the repository use semver?
            yes
              is the major version <= 1?
                yes
                  => normal versioning, e.g. github.com/stretchr/testify v1.3.0
                no
                  => when importing, we need add /vN at the end, e.g., import "github.com/my/mod/v4", and in go.mod it will behave like github.com/my/mod/v4 v4.1.0
            no
              => pseudo-versioning will be used, e.g. github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181
        no
          does the repository use semver?
            yes
              is the major version <= 1?
                yes
                  => pseudo-versioning will be used, e.g. github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181
                no
                  => version will be marked as incompatible, e.g. github.com/zeromq/goczmq v4.1.0+incompatible
            no
              => ???
    

I still don't know if this is right. But that's not really the point. I think
the go authors painted themselves into a corner with such an anemic initial
release. Now that they're trying to address shortcomings, the complexity is
blowing up to handle the very large/diverse ecosystem. It reminds me a lot of
java around 1.4 when they added in generics. In the long-run it was the right
move, but man was it painful.

1:
[https://stackoverflow.com/a/57372286](https://stackoverflow.com/a/57372286)
2:
[https://github.com/golang/go/wiki/Modules](https://github.com/golang/go/wiki/Modules)

~~~
deathanatos
I packaged a golang project once. I had to independently discover a lot of
that.

I'll also add that fetching a golang package was complicated. As I remember,
GitHub seemed to be special cased. Other URLs involved an HTTP request with a
?go-get=1 parameter, and then _parsing the returned HTML_ for certain meta
tags¹. (I thought only Python was guilty of that sin!) Some URLs I ended up
just re-writing, because gopkg.in packages effectively redirect to GitHub in
ways that, if abandoned, allow someone else to intercept[1].

And none of this is documented anywhere particularly coherent. Bits here and
there, maybe.

(Why not use go itself? Because this platform's package manager wanted to
verify that the software was being built from the correct source, by hashing
the inputs. It also, then, fetched those inputs, so that there wasn't some
form of bait & switch, and there is no network at build time to further
prevent silliness. I considered vendoring, but it isn't clear to me how
vendoring works with go modules, since it doesn't seem to take into account
the module version? / the path layout only allows for a single version.)

[1]: [https://github.com/go-fsnotify/fsnotify](https://github.com/go-
fsnotify/fsnotify)

¹some servers didn't even return the right Content-Type, either, but perhaps
that's an implementation bug. But I didn't have a spec to know.

------
kstenerud
What the go team fails to realize is that they haven't actually reduced
complexity in their design of go; they've simply MOVED it. And it's been moved
in unintuitive ways as they've painted themselves into corners that should
have been avoidable with some careful planning.

We now have:

\- magic file names

\- magic directory names

\- magic comments

\- magic env vars

\- API design requires gymnastics and juggling since recursive imports are
disallowed

\- A ton of useful code in the standard library remains unlayered and
unexposed for no good reason (pprof, for example)

\- Requiring full paths rather than relative imports complicates everything
when you're fetching it from a different source location (replace directives
and magic ENV vars).

\- Slice implementation means that you need to do double pointer storage to
keep track of the same slice from multiple places

\- Creating arrays with a dynamically defined length is horribly
overcomplicated

\- Their policy on warnings and instead calling every little thing an error is
terrible and wastes a ton of developer time.

\- Lack of runtime const value support is just sloppy.

\- Why do go source files even need a package declaration? It serves no useful
purpose since the compiler already knows enough to spit out an error if you
get it wrong.

\- Refactoring across packages is nearly impossible to automate because
there's no way to declare adherence to an interface.

\- No importing of test code from other test code (which means that the import
code in the compiler has increased complexity to enforce this silly rule).

\- Stack overflows are STILL impossible to track down (after a DECADE!)
because it only prints the top of the stack, which is useless in such a case.

\- Linting is too opinionated and is all-or-nothing.

\------------------

I could go on. I really wanted to like go, and many parts of it do actually
feel nice. But even after a decade it's a huge mess. I've spent more time
writing workarounds for go than for any other language in my 25 year career.
Hell, I even modified the damn compiler out of frustration! [1]

[1] [https://github.com/kstenerud/go#the-go-programming-
language](https://github.com/kstenerud/go#the-go-programming-language)

~~~
dan_hawkins
> \- Why do go source files even need a package declaration? It serves no
> useful purpose since the compiler already knows enough to spit out an error
> if you get it wrong.

I know about one use for it: package mypackage_test allows you to exclude test
code from builds. I agree with the rest of your points though :)

------
anonymoushn
Has something changed such that adding an exported field to a struct is now
safe in the case of clients who are using struct embedding? Previously:
[https://blog.merovius.de/2015/07/29/backwards-
compatibility-...](https://blog.merovius.de/2015/07/29/backwards-
compatibility-in-go.html)

~~~
jbamsterdam
Nothing's changed, and that's still an issue. We didn't address it because the
post was long enough, but as Merovius himself says, it's called out in the Go
1 compatibility promise. So the Go standard library doesn't worry about it,
and neither should you. It's also extremely rare; I've never seen it in 10
years of Go programming.

------
candiddevmike
Reading these blog posts, you get see Go's simplicity start to unravel with
modules. Uncomparable structs? Type checking workarounds? You start to wonder
if these bandaids would be necessary in a generics world.

For the record, I'm indifferent on the chosen module solution being right or
wrong. My ideal module tool is something like npm, but it shows me the impact:
diff the module changes, look at my code, and tell me what's impacted by the
upgrade. Giving developers more information when upgrading libraries will go a
long way towards making "update your libraries" a regular housekeeping task.

~~~
Ericson2314
Gah in every language I want that interface comparison tool, and I keep on
being disappointed.

~~~
Ar-Curunir
rust-semverver does what you want for Rust, I think?

~~~
Nemo157
It would if it worked, but every time I've tried using it I've hit some
combination of crashes and weird false positives.

