6 ms·
I've been coding in Go for over five years. I like Go, but I don't love it. It's never my first choice, although I don't advocate for rewrites just to move away
by cybrexalpha 2y ago
I've been coding in Go for over five years. I like Go, but I don't love it. It's never my first choice, although I don't advocate for rewrites just to move away from it.
The tooling is a mess. Go modules still feel like a 'first pass' implementation that never got finished. There's no consistency in formatting or imports (even though Go claims there is). Generics are a good step but are still very primitive (no generics on interfaces, no types as a first-class object).
It still feels very unfinished as an ecosystem. I hope it'll get better as the Go team mature things, like iterating on generics. But I can't see Go modules continuing without a fundamental rewrite.
- acedTrex 2y agoWhat do you mean by no consistency in formatting? go fmt is a solid formatter that does its job
- cybrexalpha 2y agoGo fmt is pretty good, but it's not ideal. My biggest gripe is imports. Go fmt will just sort imports alphabetically in lists that aren't separated by a blank line. Goimports will separate out core from 3rd party imports, unless you run it with the local flag then it'll add a third block of "local" imports. But this spread means that it's not consistent across projects which style is preferred. Some examples, based on cursory looking at big Go codebases: - Kubernetes, one of the biggest public-facing Go projects, uses the 3-block style https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/cronjob/cronjob_controllerv2.go#L19 https://github.com/kubernetes/kubernetes/blob/master/pkg/con... - TIDB uses 2-block style https://github.com/pingcap/tidb/blob/master/pkg/ddl/placement/bundle.go https://github.com/pingcap/tidb/blob/master/pkg/ddl/placemen... - MinIO uses 2-block https://github.com/minio/minio/blob/master/internal/grid/connection.go https://github.com/minio/minio/blob/master/internal/grid/con... In all of those cases if you make a change and just run 'go fmt' it very well could inject any new imports in the first block, which would be wrong and you wouldn't know until project CI picks it up.
- zik 2y agoIt sounds to me like there are some people who don't follow the style, rather than there not being a consistent style.
- cybrexalpha 2y agoWhich of those options do you view as 'the style'? One block, two blocks (core library and others), or three (core library, 3rd party, same-source-tree).
- kevindamm 2y agoThe first rule of any style guide should be "adhere to the conventions of surrounding code." So the answer to your question is it depends on what the convention in the file (and surrounding files) has already chosen. If there is only one grouping and you're adding enough imports that you have to choose between making two or three, then I would say use two because that is what the canonical & normative style guide recommends [0], but if you're working in an organization that has chosen to use goimports or has alternative guidance in its org-specific style guide then go ahead and make it three groups. [0]: https://google.github.io/styleguide/go/decisions#import-grouping https://google.github.io/styleguide/go/decisions#import-grou...
- bartekpacia 2y agoTry gofumpt, it’s my default.
- Mesopropithecus 2y agoFunnily, 1.5 decades after Golang popularized formatters, in 2024 it is the only language that I work in that requires me to think about formatting. Mostly line length, but super annoying.
- tuetuopay 2y agoAfter using rustfmt, I feel gofmt is not up to snuff. My main gripe with it is there are multiple formattings that are valid: there is not one true format. E.g. gofmt does not enforce line length which makes diverging styles possible, like for function declarations.
- jerf 2y agoNot sure what you mean by "no generics on interfaces"? https://go.dev/play/p/jGINeUt1JTE https://go.dev/play/p/jGINeUt1JTE Also echoing not sure what you mean by "no consistency in formatting or imports". It is increasingly difficult to use Go without gofmt getting run, since I have to imagine fewer and fewer people nowadays are using an editor that has neither custom support for their language nor LSP integration, and integration with gopls automatically runs goimports on save.
- cybrexalpha 2y ago> Not sure what you mean by "no generics on interfaces"? I didn't word this very well. You can have a generic interface, and functions on that interface can refer to generic types. But you can't have a generic method on an interface that uses a different generic type. For example you can't have: ``` type YieldThing[T any] interface { Yield() T DoOperation[U any](U) } ```
- jerf 2y agoI figured based on your comment you meant something by it. In which case I'd point out that it goes beyond interfaces and Go just doesn't permit that in general, for those who are playing along. All generics are always fully instantiated. You can have a type Holder[T any, U any] struct { Val1 T Val2 U } but you can never have anything like a variable of type Holder[int], with a type-currying effect or something where the U bit is still unbound, or a bare variable of type "Holder" without any further parameters. All types within the language are always fully instantiated after compile.
- cybrexalpha 2y agoYeah I get it. I've found workarounds in the past. But it's always involved some friction. In general, Go's generics are a huge step forward though. So I'm not that annoyed about it.
- foldr 2y agoThere are good reasons for not allowing generic methods: https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#No-parameterized-methods https://go.googlesource.com/proposal/+/refs/heads/master/des...
- icholy 2y agoNot sure what you're comparing to, but Go modules are probably the best dependency management system in any language.
- shrimp_emoji 2y ago[dead]
- cybrexalpha 2y agoIt does a lot well. For example it correctly pins dependencies by hash in go.sum. It's by no means the worst dependency management system I've ever used. IMO the biggest miss with Go modules is conflating the identity of a dependency with how you get it. This means that renaming a repo not only breaks the module itself (as you self-import other modules in the same source tree using the full path), but all of your dependencies. I've seen repos be renamed from github.com/foo/proj to github.com/bar/proj as part of organisational reshuffles, and then there's a big warning somewhere that says "never make a github.com/foo/proj repo or it'll break GitHub's automatic forwarding for renamed repos, and you'll break every package that depends on us." There are workarounds like using replace directives. But that makes an even worse situation where you can read a source file and assume a dependency is at github.com/foo/proj but actually it's elsewhere. But ultimately a real fix involves touching every single file that imports your dependency. If Go modules left the way of pulling a dependency in go.mod alone it wouldn't. You should use a Go modules proxy to solve this, and a custom import path. But by the time most orgs realise they need this it's too late and adopting one would be a huge change. So you end up with a patchwork of import issues.
- icholy 2y ago> But ultimately a real fix involves touching every single file that imports your dependency. Why is that a problem?
- cybrexalpha 2y agoFor an internal-only dependency it's possible. But if you've got a lot of active branches, or long-lived feature branches, it'll create chaos in merge conflicts. Even worse if you've got multiple supported versions of a product on release branches (e.g., `main-v1.0`, `main-v1.1`, `main-v1.2`, and `main` itself for the yet-to-be-released `v1.3`) you either make backports awful (by only changing the import path on `main`) or have to change even more things (by changing the import path on the release branches too). It's effectively impossible for pubic-facing dependencies. Imagine if https://github.com/sirupsen/logrus https://github.com/sirupsen/logrus wanted to change their Go modules import path, for example to move to another git hosting provider. (Logrus is great by the way, I'm only 'picking' on it as a popular Go library that's used everywhere.) GitHub tells me that almost 200,000 Go projects depend on it (https://github.com/sirupsen/logrus/network/dependents https://github.com/sirupsen/logrus/network/dependents), so all of them would need to change every source file they do logging in (probably most of them) in order to handle that. GitHub seems like it's going to be eternal for now, but when the industry moves on in 10 years time every single Go project is going to break. This would be a problem for any source dependency management solution of course, it's not like any of the others are immune to this issue. But because Go has you encode the Git path in every source file you import it into, the level of change to fix it is an order of magnitude higher.