5 ms·
> The simplicity of Go did not result in simpler systems. I echo this 100%. I worked at an employer who heavily used golang. The resulting projects were honest
by gher-shyu3i 5y ago
> The simplicity of Go did not result in simpler systems.
I echo this 100%. I worked at an employer who heavily used golang. The resulting projects were honestly a mess, yet they pushed through.
What's ironic, is that they ended up reinventing the wheel on so many different things, including DI and an entire application framework. Millions of dollars spent writing and maintaining these libraries, where Java already had them ages ago.
As you point out, the modeling capability of golang is extremely subpar. It results in verbose code that is very sparse, lots of code to implement something that would have taken a few lines in a language like Java and C#, let alone something more dense like Scala.
The way interfaces are handled in golang makes it very annoying to try to find out which types implement said interface. It puts a lot of pressure on the IDE to search the entire code base, and you end up with types you don't even care about. It clearly shows that the golang authors did not have IDEs in mind when writing the language, which is just absurd as it's supposedly a language designed for "programming in the large". Anyone who used "goland" knows what I'm talking about. Try refactoring a type, and have the IDE scan the entire code base to look for comment strings, which it has to because golang has no notion of doc strings like Java or C#.
Add features like records, pattern matching, enums (the amount of code that had to be written to implement something to emulate enums was just absurd and frustrating to deal with, not to mention error prone).
The concurrency aspect of golang is decent (though it will be even better in Java). Other than that, the language doesn't really have anything going for it other than having a large brand name backing it.
- konart 5y ago>It puts a lot of pressure on the IDE to search the entire code base, and you end up with types you don't even care about. It clearly shows that the golang authors did not have IDEs in mind when writing the language, which is just absurd as it's supposedly a language designed for "programming in the large". Anyone who used "goland" knows what I'm talking about. Try refactoring a type, and have the IDE scan the entire code base to look for comment strings, which it has to because golang has no notion of doc strings like Java or C#. Even if this was the case years ago - it is not now with an LSP.
- gher-shyu3i 5y agoIt still is, it's inherent to the way interfaces are designed. Did you use golang on a monorepo for example?
- konart 5y ago>Did you use golang on a monorepo for example? No. I have met monorepo on my path only once (not a golang one, it was mix of java and jruby) and I hope I will never see one again.
- gher-shyu3i 5y agogolang fails miserably in a monorepo setting, which is ironic because it was designed at Google, and Google are known for their monorepo.
- konart 5y ago>which is ironic because it was designed at Google, and Google are known for their monorepo. Google is huge. I'm fairly sure they use different approaches to code organisation in different teams\departments. Anyhow - I'd rather thank them for that. Now chances I'll have to deal with monorepo are lower. I'm going to think of this as a feature, not a bug, lol.
- eaa 5y agoMay I ask, what do you mean and why do you say so? What is wrong with golang in monorepos?
- gher-shyu3i 5y agoBecause interfaces are nominally typed, it's very simple to accidentally implement interfaces that are defined in other parts of the code base, even though they are not related to your project. This makes searching for implementors of a given interface in a project show implementors you don't care about, which makes navigation and discoverability very tedious. Furthermore, golang does not have a proper format for docstrings like Java or C#. When refactoring, the IDE tries to be smart and parse all comment strings as they are doc strings, and when you want to rename a struct, you're going to get a huge dialog with so many entries which can be false positives, and you have to go through them manually to check whether or not they apply. Same as with the interface issue, similar struct names may appear all across the code base which makes this refactoring very tedious. There are other issues of course, like the fact that golang has no constructors, and structs are defined adhoc, meaning adding a new field to it needs more hunting across the code base to make sure you don't miss defining and break something.
- godocwhat 5y agoGo absolutely has doc strings, I'm really not convinced you have any idea what you're talking about (For comparison I've been writing Go professionally for six years)
- sagichmal 5y ago> I worked at an employer who heavily used golang ... they ended up reinventing the wheel on so many different things, including DI and an entire application framework. Sounds like they were trying to force the language to be something it's not. DI frameworks (I think what you mean) are basically incoherent in Go, dependency injection is naturally modeled with interfaces. In fact any inversion-of-control style "framework" is a mismatch to the language, it (intentionally) lacks the features that frameworks rely on to deliver benefits that outweigh their costs. > The way interfaces are handled in golang makes it very annoying to try to find out which types implement said interface. The desire to do this reflects a misunderstanding of interfaces.
- closeparen 5y agoTake a look at the major DI frameworks for Go, Wire [0] and FX [1]. They just plumb the gaps between methods that return types and methods that want them. Although technically they work for concrete types, 99% of the time it's going to be an interface, so that you can substitute a mock for testing. Prior to DI frameworks we used global variables, often initialized in a package's init() method. >The desire to do this reflects a misunderstanding of interfaces. Generally you have a few layers: gRPC/HTTP/Kafka handler, business logic, and database or external service access. Layers are unit tested individually against mocks of the layers below. Because you're going to inject a mock, you can't depend on a concrete type, so you depend on an interface. Often when you're developing you want to know what the concrete implementation of a lower layer does, so it's useful to have "go-to-definition" see through the interface declaration to its implementations. I think the implicit satisfaction of interfaces is very cool and I wish I had it in every language. I wouldn't give it up just to simplify the IDE's job. But the IDE having this functionality does matter. [0] https://github.com/google/wire https://github.com/google/wire [1] https://github.com/uber-go/fx https://github.com/uber-go/fx
- sagichmal 5y ago> Take a look at the major DI frameworks for Go, Wire [0] and FX [1] I'm well aware of them. (There are several more, too, which see at least as much use as these two, unfortunately.) They also reflect a misunderstanding of the language. Wire less so than FX. (I'm also aware Wire is written by Googlers, no need to point that out.) > Prior to DI frameworks we used global variables, often initialized in a package's init() method. I don't understand how these things are related. DI frameworks aren't necessary for Go-idiomatic dependency injection, and global variables were never an appropriate way to manage dependencies. > Generally you have a few layers: gRPC/HTTP/Kafka handler, business logic, and database or external service access. Yes! > Layers are unit tested individually against mocks of the layers below. Yes! > Because you're going to inject a mock, you can't depend on a concrete type, so you depend on an interface. Yes! > Often when you're developing you want to know what the concrete implementation of a lower layer does, so it's useful to have "go-to-definition" see through the interface declaration to its implementations. ...no? There are basically two situations where you use an interface in this way. One, most often, in an application where the "production" code uses one implementation, which you know a priori and therefore have no need to go spelunking. Or, two, in a library where you by definition cannot know what implementation your users will provide to you. In the first case, your mock would model the implementation, which I guess is trivial. In the second case, your mock would reflect your expectations of the interface's implementations, explicitly without knowledge of any implementation.