usrnm 6 hours ago

Adding an element to a nil slice works, but adding an element to a nil map panics. time.Time uses its own epoch incompatible with anything else. defers are function-scoped instead of block-scoped. The list goes on, these are just the first ones that came to my mind. And they are not some magical tradeoffs, they are just poor choices. I like go, but no, it is not well-designed

  • mirekrusin 6 hours ago

    Also missing null safety in modern language is weird.

    • physicsguy 5 hours ago

      And enums.

      • roncesvalles 4 hours ago

        The appeal of Go is that it has just the bare minimum feature set to be useful for modern backend engineering. It's a language that you can completely fit within your head within a few months of using it, as opposed to certain languages like C++ (or say C# to a lesser but increasing extent) that most will never know completely even after decades of working with it. It's basically the RISC of programming languages.

        And if I had to guess, it doesn't have enums so that it can remain flexible when serializing/deserializing enum-like types over the wire. Imagine you can't parse an incoming payload containing an enum field because your service is one version older than the one that extended the enum type (or the enum type is defined in a package dep... you get the idea). Enums are actually a terrible idea now that I think about it.

        • anal_reactor 3 hours ago

          > The appeal of Go is that it has just the bare minimum feature set to be useful for modern backend engineering. It's a language that you can completely fit within your head within a few months of

          To me, golang symbolizes the shift of philosophy of Google as a company. It changed from "it's a smart nerd company for smart nerd people" to "golang will allow us to hire cheaper devs because golang will prevent them from making mistakes". I mean, this makes sense from business perspective, I won't deny this fact, but it's the programming equivalent of Ferrari making a SUV: tremendously profitable, but sad to see.

          BTW

          > golang doesn't support overloading because overloading is bad. Having said that, it's 'go', not 'golang', like the verb 'go', which already has a thousand meanings depending on the context

          I find that hilarious

        • theThree 4 hours ago

          The appeal of Go is goroutine.

      • theoryofx 5 hours ago

        You can point out flaws so it's not well designed? By this logic nothing is.

        And pointing out missing features in a programming language is just about the weakest criticism possible.

        Piling on lots of features is easy and fun. That's why almost all language designers do it. Most popular languages destroy themselves with features.

        Rust is in the process of gaining every single feature anyone can imagine. Following C++ right off the complexity cliff.

        Go is one of the very few languages to show incredible restraint in adding features because its designers understood the combinatorial complexity problem, among other things.

        Not having unnecessary features is one of the best features of Go.

        • powerhugs 5 hours ago

          I very much agree. The fewer ways a programmer can become creative, the easier it is to fully understand their code.

          In a business environment, this is a obvious win.

      • fullstackchris 5 hours ago

        package main

        import "fmt"

        // Define an enumeration for status type Status int

        const ( Pending Status = iota InProgress Completed Failed )

        func (s Status) String() string { return [...]string{"Pending", "InProgress", "Completed", "Failed"}[s] }

        func main() { var s Status = InProgress fmt.Println(s) // Output: InProgress }

        • physicsguy 5 hours ago

          I see this suggested all the time as though it’s supposed to be a replacement. It isn’t sufficient, it’s a poor emulation of what enums can be used for and doesn’t offer the same protections, since Status can be set to any integer value without casting or any knowledge that it’s broken.

          In your suggested method it doesn’t even catch that case and provide an error if the value isn’t in the range.

          • powerhugs 5 hours ago

            It seems like none of the other comments here use or are even aware of `go generate`. Go has excellent code generation features [1].

            I think parent should be using `stringer` [2] instead.

            1: https://go.dev/blog/generate

            2: https://pkg.go.dev/golang.org/x/tools/cmd/stringer

            • physicsguy 5 hours ago

              I'm well aware of Stringer but I don't again think it's a good replacement of having enums built in!

              • powerhugs 4 hours ago

                > I don't again think it's a good replacement of having enums built in!

                I didn't say anything along those lines, in fact I was commenting generally about this issue that you guys and the other commenters mentioned.

                I was just suggesting a solution to the the problem stated.

        • alkonaut 5 hours ago

          Next day I have this (added one entry). I forgot to update the string list elsewhere however.

          const ( Pending Status = iota InProgress Completed Cancelled Failed )

          func (s Status) String() string { return [...]string{"Pending", "InProgress", "Completed", "Failed"}[s] }

          func main() { var s Status = Cancelled fmt.Println(s) // Output: ??

          If the compiler - not some linter - protests that the list of names is different from the number of items in the enum, then I think this is at least a half-decent design of an enum type. Not a great one (because the author still had to repeat the names), but at least something that isn't a fundamentally broken design of an enum type.

          But if the compiler is silent, and the output of the Println(Cancelled) is "Failed" then I'm not angry, I'm disappointed.

          Edit: https://go.dev/play/p/MMPMh7_U81-

        • Svip 5 hours ago

          OK, but I can set s to 1000, and it still compiles.

        • pjmlp 5 hours ago

          Also known as design hack, workaround for what other languages support natively since 1970's.

          Only one step better than Assembly or Fortran.

  • shakiXBT 2 hours ago

    If language specific quirks lead to a language being "not well-designed", how many languages do you think are actually well-designed (if any)?

  • xarope 5 hours ago

    I got caught with maps initially, and had to question why everything else was just "var x string", which also gives it a value, but maps needed a "var x = make(make[string]string)", otherwise on first assignment it panics.

    There's a good explanation somewhere, ah here it is: https://go.dev/blog/maps

    But you get used to it, and move on, especially with the sort of TDD workflow that go test encourages

    • usrnm 4 hours ago

      I know why it works the way it works, it doesn't make it a good design, which is the point of this discussion

  • roncesvalles 4 hours ago

    I don't think that's weird. You can only append to a slice using the `append` built-in function. `append` may initialize and return a new slice at any call. It would be weird for it to not do so when passed a nil slice.

    • usrnm 3 hours ago

      Adding elements to a map allocates memory and moves stuff all the time under the hood, it's no different from append. If panicking on nil is weird for append, it's just as weird for maps

      • jitl 33 minutes ago

        append returns a new pointer in case the slice moved, map assignment does not return a new pointer. There’s no way for the operator to change the reference you hold from a nil to a pointer to the newly allocated value. When memory is allocated under the hood on assignment to a non-nil map, your reference to the object remains the same, but some internal pointers change.

        You can write a func Set[k, v](map map[k]v, k k, v v) map[k]v yourself that returns a map reference if you want this style of “maybe return a new pointer”.

  • q3k 5 hours ago

    > time.Time uses its own epoch incompatible with anything else.

    Could you please elaborate?

    • theoryofx 5 hours ago

      time.Time uses its own epoch internally but has methods like .Unix() which convert to Unix epoch time. Never seen this be a problem.

      • usrnm 4 hours ago

        It means that zero value time.Time when converted to unix time gives you a negative number corresponding to a date two thousand years ago. With the way go insists on using zero values for expressing optionality this is a rather common mistake. Interacting with anything outside the go ecosystem is done in terms of unix time and this makes it very error-prone. And I've seen this trip people off in real life. And yes, you can argue that this is programmer's fault, but this is exactly what makes it a bad design: it makes people's lives more difficult for no real purpose, just because somebody at Google wanted to feel smart that day.

    • indulona 5 hours ago

      i think he means that internally, time objects simply use unix nano and the location dictates the offset. since it is internal thing, not sure why that is relevant.

relistan 5 hours ago

Seems many people mistake “well-designed” to mean “perfect”. Read the article: he doesn’t claim that. No language is perfect, they are all trade offs.

Furthermore, it’s fine to not like the design. It might not be to your taste or your requirements. But that doesn’t mean it wasn’t well designed. I can point to lots of things that I don’t like the design of that work great for others.

Go is super productive, has a great ecosystem, has great concurrency, produces good machine code, compiles as fast as anything out there, has great tools, and is growing in popularity. Flaws? Of course. Perfect? No.

But, it’s well designed.

  • guappa an hour ago

    > has a great ecosystem

    A great ecosystem is one where there is good 1 library to do a thing and you just pick that one.

    A bad ecosystem is one where there are 25 libraries to do some things and 0 to do some other things, and none of the 25 libraries are very good.

  • kerkeslager 4 hours ago

    > No language is perfect, they are all trade offs.

    Uh, no they're actually not. A tradeoff means you accept some downside and get some upside in return.

    Initially go was designed without generics. The tradeoff here was you got a single pass compiler which was faster. Well, actually, you could have a single-pass compiler with backpatching, but I get it, that's hard. It's a crap tradeoff, but okay, I can accept it's a tradeoff. And everybody at the time said, "Go doesn't need generics, they complicate the language." So we casted our nested types in and out of object and lost all type safety in that code, but hey, tradeoffs.

    Except everyone kinda realized it was a crap tradeoff and so `go generate` was proposed as a solution. In a 1984-esque revision of the past, suddenly, having a second pass is okay! Never mind how important it was that go is a single-pass compiler. Nevermind that this is basically macros with all their pitfalls. But now we can generate 3 different versions of the parameterized type, one for each nested type, so we get type safety. 2 passes, macros, downsides, type safety, upside, and a pretty big upside at that.

    But, here's the problem: now you have two different systems that are used for doing a lot of the same things that don't interoperate with each other very well. And that's not a tradeoff, because there's no upside. It's not "I don't like this but it works great for others" because it doesn't work great for anyone. It's not a tradeoff, it's a side effect of having chosen wrong the first time.

    And... now Go has generics. And yeah, generics aren't really a tradeoff, they're just obviously pretty good. But, now we've got 3 different systems for doing a lot of the same things, which again, don't interoperate all that well with each other.

    And here's the thing: from a language design perspective, this was extremely obvious that Go was going to need generics. Generics are pretty much the clearest win from the ML-family languages which had already made it into C# and Java by the time Go was conceived. It's absurd that this feature wasn't included in Go from the beginning.

    • relistan 3 hours ago

      > Uh, no they're actually not. A tradeoff means you accept some downside and get some upside in return.

      Yes, there is always a trade off. For everything, everywhere, all the time. You might not like the decision but trading flexibility for simplicity was a win for people learning the language. A lot of good software was built without generics.

      It was a trade off, you just don’t like it.

      • guappa an hour ago

        Replying to that extremely well argumented comment with what is basically "NO I AM RIGHT!" is disrespectful.

lordofgibbons 5 hours ago

I used to love Go and its simplicity. It's still my least disliked backend language. But after more than a decade of using it nearly full time (but not exclusively), I've fallen out of love with it.

I want sum types. I want non-nullable types. I want enums (preferably, based on sum types).

I'm okay with the explicit error handling, but would be fine with Rust style error handling too.

Having these features would change the language so much that you'd probably want to re-implement the stdlib, so it probably won't happen.

Surprisingly though, while I absolutely love Go's concurrency model, being able to kick off new goroutines with a dedicated "go" keyword and special channel operators have not been a big deal at all. It's usually abstracted away by some library, or you set it up once in your application and never touch it again.

  • praptak 5 hours ago

    > But after more than a decade of using it nearly full time (but not exclusively), I've fallen out of love with it.

    I believe this is true for anyone and any language. Spend a few years in a language on stuff that you have to do (which is the main differentiator between work and hobby projects) and two things happen: the benefits are taken for granted while the warts are magnified fivefold.

    The corollary to this is the quote "I won't use something until you tell me why it sucks" (can't find source). If you use something long enough you know the ways it sucks. This makes your criticism more valuable than enthusiastic praise from a hobbyist.

  • tankenmate 5 hours ago

    In my opinion your comments on Go concurrency misses a point, the fact that you do it once "and never touch it again" is a very very strong selling point, it probably means it was very well designed; not a weakness but a strength.

    • lordofgibbons 4 hours ago

      I didn't mean it's a weakness. I mean it's not as amazing of a selling point as it was made out to be when the language first came out.

      If you're using something only rarely and not touching it again, there isn't much of a difference between go.New(func(){}) from some "go" package, and go func(){}.

      To reiterate, I'm not against the "go" keyword. It's whatever ¯\_(ツ)_/¯

egeozcan 6 hours ago

I like Go, I like TypeScript, and I like C.

All of them have significant design flaws and quirks, many of which people here write pages of complaints about. But in most cases, it's easy to learn how to avoid them (granted, that's more of a stretch for C, but plenty of people manage, and that's enough).

I'm not against pointing out issues, especially for actively developed languages like Go. However, it's important not to turn criticism into bashing the language or the people using it. That just comes off as snobbish and isn't constructive.

Each of these languages is well-designed for specific needs, and not so much for others. And that's okay.

I think people mostly try to scare others away from using these technologies to push something they believe is better. But instead of that, you could perhaps write a competing article praising and comparing your favorite stack and that would be much more useful, in my opinion.

  • grey-area 5 hours ago

    I wish more people thought this way. Why does the language you choose have to be your religion and lifestyle, when it’s just a tool? They all have flaws and areas they are useful in.

    • newdee 4 hours ago

      People do this with all sorts of things, but ultimately want validation that they made the “right” choice. At least that’s the most charitable assumption I can make.

  • kerkeslager 4 hours ago

    It's one thing to say these are decent tools, but to say something is well designed is a much more specific claim.

    I have some fondness for C, but I would not say it is well designed compared to modern alternatives. The reason I use it is largely portability which is more a function of its age and ubiquity than its design. You can of course make the argument that it was well-designed compared to the languages of the time when it was designed, and I don't really know enough to disagree with you there.

    TypeScript, I will say, I think is actually very well-designed. It has it's problems but it's a massive step forward from anything that existed before it.

    > I think people mostly try to scare others away from using these technologies to push something they believe is better. But instead of that, you could perhaps write a competing article praising and comparing your favorite stack and that would be much more useful, in my opinion.

    Strong disagree. Polyanna programming where we pretend all the tools in the world are so great results in horrible codebases, and then the programmer who thinks every tool is awesome becomes frustrated, doesn't know why, and moves on blissfully to a new project/company where they begin to plant the seeds of chaos anew. But someone has to deal with that crap codebase they left behind, and at this point in my career that person is often me. Work on codebases for more than a few years and the only way to stay sane is to recognize problems in code so that they can be fixed before they become bigger problems, and doing that requires you to be critical.

    Working on a team, the biggest uphill battle is often preventing people from inserting every random tool they get excited about. Usually it's libraries, not languages, that are the problem, but I've seen both.

Animats 6 hours ago

Go is an OK language, and that's the point. An average programmer can write web services in Go without too much trouble.

Goroutines mean the programmer doesn't have to struggle to avoid blocking, or decide what should be "sync" and what should be "async".

Garbage collection means the programmer doesn't have to think much about ownership.

The error handling is a bit verbose. If Go had generics from the beginning, it could have benefited from something like Rust's Result type and "?" operator. That's effectively the same thing as Go's usual error handling, but more concise.

  • theoryofx 5 hours ago

    The criticism that Go is for "average programmers" almost universally comes from people overestimating how good they are.

    I've worked directly with hundreds of highly paid programmers over decades. I can count on my fingers the number that would be at all constrained by using Go where it's a suitable choice (an important caveat).

    The rest would have their work highly improved by using Go as much as possible, precisely because it's harder to write bad code in Go compared with most languages.

  • 9rx 6 hours ago

    > and "?" operator.

    To be fair, Go almost go the "?" operator like Rust. The proposal was well received. It wasn't the lack of "Result" that held it back, it was that nobody could figure out how to deal with the handling problem. It's not entirely clear what the Rust-equivalent is for all the other moving pieces associated with "?" (e.g. its defined traits). The latest "?" proposal forces a handling body on each use to address that issue. But at that point all you've done is added another way to write "if". Is that a win?

kgf1980 6 hours ago

I’m not a coder day-to-day (I was in the past, mainly C#) but I still write toy stuff to make my life easier, and the stuff I write in Go I’m more likely to be able to return to a year later and be able to quickly understand what I was doing, why I was doing it and fix/enhance what I need to.

  • prisenco 6 hours ago

    The readability of Go is why I defend it all day every day.

    The fact that a programmer with no Go experience can look at a codebase and quickly get a sense of what's going on is a massive advantage for large teams, code handoffs or long term maintainability.

    Go is nobody's favorite language but that's why it's my favorite language.

    • WesBrownSQL 6 hours ago

      100% this one of the big reasons I fell in love with go. Single executable, no runtime stuff to install and crazy portable. I can look at anyone's go code and very quickly get up to speed. I don't worry about how they format it. Patterns are pretty universal which makes it easier. Does it have its warts? Oh, you better believe it but I know that any code base I go through will pretty much have the same warts the same way. Does go fit every need? Nope on that front too but man its pretty good and what it was designed to do.

    • LtWorf 6 hours ago

      > can look at a codebase and quickly get a sense of what's going on

      I don't even need to look. Error checking is going on :D

      • nejsjsjsbsb 5 hours ago

        As it should be! Shame they didn't make the error type a sum type that forced you to check it isn't an error. I prefer that to exceptions. But the product type that Go uses is ok.

        • guappa an hour ago

          70% of any code is handling errors. It's awful.

          And no exceptions make it really easy to ignore errors instead. Which is much worse.

aaronbrethorst 6 hours ago

What “sorta just happened” is Swift 6. I knew Swift, I loved Swift. Swift designed itself right away from what I was willing to put up with anymore.

Go in comparison seems unnecessarily pedantic, occasionally too verbose, and occasionally unnecessarily terse. But it never makes me lolsob minus the lol

  • rob74 6 hours ago

    > Swift designed itself right away from what I was willing to put up with anymore.

    I swear I can't tell if this phrase is supposed to be positive or negative? Then again, I'm not a native speaker...

    • UltraSane 5 hours ago

      This feels like SAT/ACT reading comprehension question! I think that "But it never makes me lolsob minus the lol" means he meant he doesn't like the changes Swift 6 made.

  • cageface 5 hours ago

    I think SwiftUI is to blame. In order to make a nice terse DSL for WWDC talks they really contorted the language design.

zxvkhkxvdvbdxz 5 hours ago

> If you want to add two days you can't just call Add(0 /years/, 0 /months/, 2 /days/), you need to use AddDate.

Sorry for nit-picking, but 2 days is a duration of time, and you can add one using Add, like this:

    t.Add(time.Hour * 24 * 2)
I agree about FFI being a weak point in go when you need to interact with other languages, but it can hopefully be mitigated by using tinygo, which is still being developed.
  • ben0x539 5 hours ago

    Does this do what you want across/during DST switchover?

    • theshrike79 2 hours ago

      What do you want to happen during a DST switchover in this case?

ninetyninenine 6 hours ago

I hate go. Specifically I hate go packages. It was poorly designed. Any time you create a package in go you can’t have circular dependencies. Which makes sense right? When you make a package in rust or node or python those packages can’t have circular dependencies.

If you have two files in go or any other language, circular dependencies are usually permitted which also makes sense. It’s only packages that don’t permit this.

But guess what? You create a package in go simply by putting everything in a folder. So people when writing a go project they often make dozens of packages just by the is ocd need of organizing things into folders.

But when you organize things into folders the files in those folders aren’t project files anymore. They are different packages and you can’t have circular dependencies.

And that’s a big problem as this design conflicts with programmer instincts on how to organize things.

We organize things by meaning and name and subject matter. But simply using folders in go we are forced to organize things via dependencies and this causes a clash and makes organizing things 10 times harder than it should be in go.

Imagine I have a chicken and an egg. Chicken goes in the animal package and egg goes in the food package. It is now fundamentally impossible to make a chicken lay an egg and an egg hatch a chicken at the same time. Our tendencies to organize things via meaning has clashed with this requirement of organizing things via dependencies.

Does it make any sense at all? We probably don’t want packages to be interdependent in this way. But the stupid thing is just be creating a freaking folder we create this problem in go and it doesn’t make sense because if the files are outside of a folder you don’t hit this problem.

I would say go is actually poorly designed. I can’t think of any popular language that has this strange inconsistent problem.

It actually causes huge headaches in organizing things in go but idiot developers when they hit this circular dependency problem they literally believe the documentation when it tells them that they aren’t organizing things correctly and they need to think harder about design. So in a lot of go shops they spend an inordinate amount of time naming folders and organizing shit just to get their ocd need of organizing things by name to jive with gos requirement to organize things by dependency.

In the real world things can be in different categories and also interdependent at the same time. Go’s universe doesn’t allow this.

I’m here to tell you that It does make sense to put eggs in the food folder and chickens in the animal folder. Go is just stupid in this area, not you. Don’t believe the idiot who wrote that in the docs.

  • 9rx 5 hours ago

    > Imagine I have a chicken and an egg. Chicken goes in the animal package and egg goes in the food package. It is now fundamentally impossible to make a chicken lay an egg and an egg hatch a chicken at the same time.

    I am familiar with people eating unfertilized eggs. I am even familiar with balut. But I am not familiar with the delicacy of eating an egg as it hatches. If you eat an egg as it hatches, wouldn't that actually be eating a chick (the chicken) rather than an egg?

    I suspect the real problem here is that your domain model is ill-conceived. In fact, while Go has no lack of faults, I suspect the vast majority of the complaints directed towards it come down to the limited feature set not enabling papering over bad design decisions.

    • ninetyninenine 5 hours ago

      Your joking. I have a hundred food items and a hundred animal items. Cows produce milk. Chickens produce eggs. Eggs produce chickens.

      It makes 100 percent sense to make two folders. Food and animals.

      ‘I suspect your “domain model” is ill conceived’??? You buy into that? Don’t follow what those idiots tell you.

      You realize that we use these actual separations between food and animals in the real world? And now because of some design philosophy the domain model is ill conceived? The result is a more convoluted domain model to make things flow.

      Actually I think this is an easier way to explain it to you. The “domain model” or aka categorization of food and animals works in reality and every other language because it’s not a bad design decision. The reason why it doesn’t work in go is because go is the bad design decision.

      • 9rx 5 hours ago

        > You realize that we use these actual separations between food and animals in the real world?

        The distinction between "animal" and "food" isn't necessarily faulty, but an egg considered to be for food and an egg considered to be for hatching are deemed distinct types in the real world. An egg from the grocery store is not going to hatch no matter how hard you coddle it. This is not the same type of egg that will hatch a chicken. Reusing the same model for both types of eggs does not work in the real world, and thus it stands to reason that it doesn't work in software either.

        Revisit the model in a way that actually reflects the real world and your circular dependencies are bound to disappear.

        • ninetyninenine 5 hours ago

          Who said it has to be a grocery store. I’m in a farm. Some eggs I eat, some eggs I hatch. I have a food bin and an animal pen. Two different boxes for two different things.

          Makes sense in the real world. Why doesn’t it make sense in go? You literally think there’s some elegant underlying principle here having to do with categorization do you? It’s mind boggling to me how arbitrary decisions made by the designers of go are thought of as fundamental design rules.

          When people use the word “domain model” I know the complex nomenclature has actually led them astray. These are just folders and categories. The term domain model doesn’t change a thing other than making it sound smarter.

          • 9rx 5 hours ago

            > Some eggs I eat, some eggs I hatch.

            Exactly. Like you describe, this domain model has at least two different types of eggs. This matches what was described in the original comment. But, in the original comment, the author was trying to shoehorn all functionality into a single egg type. And, unsurprisingly, it didn't work for him. It wouldn't work in the real world either.

            Revisit the model in a way that actually reflects the real world and your circular dependencies are bound to disappear.

            • ninetyninenine 5 hours ago

              Come on. Think about it. The egg hatches or is eaten.

              Now you have HatchingEgg, and EatableEgg types. Let's be real about where the shoe horning is happening.

              • 9rx 4 hours ago

                Essentially. More likely, without straying too far from the original, you would have something like `animal.Egg` and `food.Egg` (although it is likely that you would want to stray from the original in an actual setting).

                You are going to do that type of thing regardless of the language (and especially in languages with more advanced type systems!), even where circular imports and other code structure methodologies are supported. You wouldn't want your `scrambleEgg` function to accept an egg intended to be hatched. The bones are unpleasant.

                • ninetyninenine 4 hours ago

                  >You're going to do that type of thing regardless of the language

                  No I'm not. I'm going to make one Type and it's going to be called EGG. You're going to make an Egg interface and divide it into two subtypes EatableEgg and HatchableEgg.

                  I don't think you picked up on it but I have one thing, you created 3. And you created 3 just to avoid an orthogonal issue of dependencies. You aren't getting it. In my statement above I was implying that YOU are shoehorning things, NOT me.

                  Also you have to create a new package now called Types and that's where you put your Egg interface. So go forces you to create a third meta category while other languages I can choose whether I want that or not.

                  Or I just don't use folders at all in go. Then this issue doesn't even exist. It only exists because I happened to want to use folders, but I can get rid of circular dependencies simply by organizing everything by files.

                  • 9rx 4 hours ago

                    > No I'm not.

                    Enjoy your scrambled bones, then, I guess. But even if you want to model the world as having only one egg type, that type decidedly does not cleanly fit into "animal" or "food", so your taxonomy doesn't work.

                    > You're going to make an Egg interface and divide it into two subtypes EatableEgg and HatchableEgg.

                    This seems like a poor assumption. In the real world, if an "EatableEgg" is fertilized into a "HatchableEgg" there will be a type conversion. What was an "EatableEgg" is now a "HatchableEgg" and the "EatableEgg" no longer exists. What do you need the interface for?

                    > I don't think you picked up on it but I have one thing

                    Right, but we're talking about types, not things. Things can come in many different types. You even told us a story about how you consider eggs, which are the same thing, to be of different types, so this didn't go unnoticed by you earlier.

                    > Also you have to create a new package now called Types and that's where you put your Egg interface.

                    If you were to have a such a thing, wouldn't it go in something like "reproductive structure"? "types" is a strange fit alongside "animal" and "food".

                    Revisit the model in a way that actually reflects the real world and your circular dependencies are bound to disappear.

                    • ninetyninenine 3 hours ago

                      >Enjoy your scrambled bones, then, I guess. But even if you want to model the world as having only one egg type, that type decidedly does not cleanly fit into "animal" or "food", so your taxonomy doesn't work.

                      scrambled bones my ass. There's no instance where that happens. I have a scramble function that takes an egg type as a parameter I don't know where you're pulling the scrambled bones from.

                      I don't model the world that way, that's not my objective. I live on a farm, I'm just modelling things the way I modelled it on the farm. I organize my eggs into the food box and my hens into the chicken pen. I do this in the real world. I want to do the same thing in my programming and I can't. Understand?

                      >Right, but we're talking about types, not things. Things can come in many different types. You even told us a story about how you consider eggs, which are the same thing, to be of different types, so this didn't go unnoticed by you earlier.

                      If you make two different types, and you instantiate those two types you have two different things. I don't think you're getting it. Those two different things cannot represent the real world thing because the real world thing can hatch OR be eaten. The things you created can only do one or the other.

                      >If you were to have a such a thing, wouldn't it go in something like "reproductive structure"? "types" is a strange fit alongside "animal" and "food".

                      You have to do this because your Egg interface can't go into animal or food becuase it will cause a circular dependency.

                      On my farm I don't have a bin or a box (aka type) called reproductive structure or types. I don't need to do this in the real world but I need to do this in go because Go is poorly designed.

                      Look, I can make a folder called animals and food and I can put anything I want in those folders in other languages. In go, I can't. It's that simple. Go makes this arbitrary restriction out of nowhere and it makes things worse. You're shoehorning new nomenclature and reproductive concepts into your organizational scheme while I can run with what's done in REALITY with significantly less primitives.

                      >Revisit the model in a way that actually reflects the real world and your circular dependencies are bound to disappear.

                      I think you're out of touch with the real world. When you type things in the real world (aka putting things into a box or a pen) you don't care about dependencies. I don't put my eggs in the chicken pen because they came from the chickens. I put it in the food pantry. Understand? Folders are designed to do the activity I just mentioned, they are not designed to form some complicated taxonomy of unnecessary concepts.

                      • 9rx 3 hours ago

                        > I have a scramble function that takes an egg type as a parameter I don't know where you're pulling the scrambled bones from.

                        Then you're in for an educational treat. Believe it or not, inside a "hatching egg" is a little chicken! And chickens have bones. As your scramble function accepts any type of egg, it may accept an egg that contains said bones.

                        But it need not be that way. With consideration about your types, you can have the compiler ensure that you only allow "eating eggs" into your scramble function. Just like we normally take care in the real world to ensure the same kind of separation because most people would not be amused if their scrambled eggs contained bones. Apparently you roll the dice, but know that is unusual.

                        > and you instantiate those two types you have two different things.

                        I don't disagree, but I don't know of any programming language that has a concept for things. Certainly none that you would actually use for production software. Types are the pinnacle of popular computer science thus far. At some point you are going to have to accept that software and the real world aren't exactly the same.

                        But revisit the model in a way that actually reflects the real world and your circular dependencies are bound to disappear.

                        > I organize my eggs into the food box and my hens into the chicken pen.

                        And the roosters? Those eggs aren't hatching otherwise...

                        > You have to do this because your Egg interface can't go into animal or food becuase it will cause a circular dependency.

                        But, most importantly, because it would otherwise be confusing for anyone else who has to work on the code. "Why is a chick about to leave its shell considered food? I don't know anyone who considers that to be food." is the first thing they are going to say upon encountering this codebase.

                        Code is written for humans. Remember that.

                        > I put it in the food pantry.

                        You may put "eating eggs" in the pantry, but I can't imagine you put "hatching eggs" in the pantry. So why are you putting "hatching eggs" in "the food pantry" when taken to the computer? Again, this model you have doesn't even work on a human level, never mind any technical constraints when applied to software.

    • fullstackchris 5 hours ago

      I've been writing Go for about 5 years now and can confirm cicular dependency issues were typically handled buy a small refactor or considering where the borders of the domain should _really_ be

      • ablob 5 hours ago

        I think the issue the OC was hinting at is that the borders of the domain are forced to be the files. Having a folder for "food" and a folder for "animal" should not imply that they are in different packages by the OC's reasoning. Perhaps both of them should be the same package, but to achieve this, you can't organise it into folders.

        _This_ is what OC was complaining about. Considering where the borders of the domain should be is a second handed issue that is only due to the bad choice of having packages be determined by folder structure, which is the complaint of OC.

        • ninetyninenine 5 hours ago

          Right. And people conflate the issue. Especially go developers. They can’t see the difference.

          They naturally create folders based off of semantic meaning instead of packaging. Then they hit the dependency issue and they think creating the folder was a domain modeling problem when really they just created the folder based off of semantic meaning and not dependencies.

          The whole thing has confused go developers. They think putting things into the right folders actually has some deeper design meaning when really it’s just to make it easier for humans to find shit.

  • jrockway 5 hours ago

    This bothers me from time to time. When I first started using Go, I think I complained to the Go team about it. They told me to think more carefully about where package boundaries are. Having the smallest package possible doesn't necessarily mean the design is good.

    I inherited an app from a coworker, and he just put every file in the main package. This was actually fine and caused no problems.

    These days, I still have an addiction to making tiny packages, which the Go team would likely not approve. But I'm a big girl and I do what I want! The workarounds I use are:

    1) A `types` package. type Chicken struct { Children []Egg }; type Egg struct { Mom, Dad Chicken }.

    2) A `utils` package. Let me explain. I would never name a package `utils`. That's illegal and I'm told that the compiler will explode into a thousand pieces if you try. All the king's horses and all the king's men wouldn't be able to put it back together again. So don't test it. Just trust me. But, you can definitely have something like `package hatchery` with a functions related to collecting eggs from chickens. Or you can have `package eggutil` for packing eggs, frying eggs, and shipping eggs.

    I would say that I typically have a `types` package (called `types` if I'm hand-writing the code, or myapppb if it's protobufs like at Google), and then structure the rest of my app as model/view/controller. The model puts these types into the database. (Call it `eggdb`.) The controller does whatever business logic you care about (getting chickens together to fertilize eggs, call it `husbandry`). The view calls into the controller to implement your API / gRPC endpoint / beautiful Gtk+ GUI / whatever.

    And then you don't really have problems with circular dependencies.

    Having said all that if someone puts it all in main.go I'd probably approve the PR. The Go team let my coworker do it in 2014 and nobody died.

    • ninetyninenine 5 hours ago

      You’re not seeing the problem. You spend an inordinate amount of time trying to organize it and you come up with an elegant way so the catharsis of that solution makes you think you found the path to nirvana. Really you’ve been duped.

      This restriction makes sense for packages. But not for folders. The problem arises in go because folders and packages are the same thing and that’s stupid because people don’t typically use folders as if they were packages. Folders are typically used just to categorize things of similar meaning or naming or whatever you want.

      No other popular language has this issue. I can choose to organize things how I want in any other language. I have the freedom. I can make a folder A and B and C and D and just organize things in ABC order. But can’t with go. If I dont want to do this in any other language I have the option to create a package.

      Creating a package and creating a folder are completely orthogonal tools and concepts and the genius who invented go just made a horrible design decision here.

      • phyrog 5 hours ago

        > that’s stupid because people don’t typically use folders as if they were packages. Folders are typically used just to categorize things of similar meaning or naming or whatever you want

        You might do that. That does not mean everyone organizes things like this.

        What your chicken/egg example fails to realize is that things rarely just belong in one category. Chickens can be animals or food.

        To me it just sounds like you are trying to write another language while using Go. Of course you will see problems. Just as you would see problems trying to write Go code when using Java.

        In the end it comes down to this: if the language does not fit your mental model on how to do things, don't use it. But don't go around shitting on the language just because you are used to using folders differently than other people. Using a language also means learning and using the conventions of that language.

        • ninetyninenine 4 hours ago

          >You might do that. That does not mean everyone organizes things like this.

          Most people do organize things arbitrarily based off of their own categorizations and opinions. In fact such an overwhelming majority of people who use operating systems do this that it's pretty much universal.

          >What your chicken/egg example fails to realize is that things rarely just belong in one category. Chickens can be animals or food.

          So what. I choose not to eat my chickens. So I Choose to categorize it in a way that makes sense to me. Why should I conform to your point of view? Why should I conform to golangs point of view? Why can't you and I choose how to do it?

          >To me it just sounds like you are trying to write another language while using Go. Of course you will see problems. Just as you would see problems trying to write Go code when using Java.

          No. I'm complaining about go. I think it's dumb. I'm not doing OOP here, I hate OOP. Go is waaay better. This is orthogonal to that problem.

          >In the end it comes down to this: if the language does not fit your mental model on how to do things, don't use it. But don't go around shitting on the language just because you are used to using folders differently than other people. Using a language also means learning and using the conventions of that language.

          Yeah that's how all complaints and criticisms are sidelined. "You don't like it, don't use it. Use something you like." Obviously.

          I'm doing exactly that, while saying that go packages are a poor and horrible design decision.

          I think a lot of people hated java, and found go and they think because go is so much better then java then that means go can do no wrong.

          I don't think you realize there's stuff way better then go out there. But that's besides my point. What I use is separate from my point: Golang packages are poorly designed.

          • phyrog 4 hours ago

            > I don't think you realize there's stuff way better then go out there.

            Every language makes trade-offs. For you the trade-off Go makes is bad. I disagree. I like some languages better in certain parts, while I prefer Go's solution in other parts. It's all preference, there is almost never an objective "better" or "worse" like you seem to think.

            > Golang packages are poorly designed

            Agree to disagree.

            • ninetyninenine 4 hours ago

              Then you disagree with the majority of people. That's my point. This agree/disagree side lines the fact that people can be right or wrong. My claim is not only do you disagree, but you're wrong.

              • phyrog 3 hours ago

                Majority of what people? Get off your high horse, your opinion is not a fact. Give me objective metrics to measure how "good" a language or language feature is, otherwise you are just wrong.

                I'm looking forward to trying out your own perfect language that is objectively the best for any use case ever and no one can find any faults with it. Because surely such a language exists.

                • ninetyninenine 3 hours ago

                  >Majority of what people? Get off your high horse, your opinion is not a fact.

                  Just look at reality and how people organize things everywhere. It's arbitrary. Adults are placed in offices and kids are placed in schools. Then on family trees kids and Adults are organized by dependency. How people organize things in the REAL world is AN arbitrary choice. But in GO you have NO CHOICE. It has to be by family tree. That is NOT an opinion. Everything I said is FACT.

                  The metric is basically the entire world and everything in it and how we fundamentally type (aka organize) things within it. But if you want to get more specific just look at every other programming language except go and look at how the entire world uses folders in operating systems. These are tools to allow people to arbitrarily organize things.

                  >I'm looking forward to trying out your own perfect language that is objectively the best for any use case ever and no one can find any faults with it. Because surely such a language exists.

                  Don't have one. I'm just commenting on what I hate about golang.

                  • phyrog 3 hours ago

                    So a hand-wavy "look at the world bro" instead of actual metrics. Got it.

                    > I'm just commenting on what I hate about golang.

                    No you're not. You say Go (or a part of Go) is bad, which is vastly different. If you stuck to "I don't like it", you would not have gotten so much push back, but you insist in being right and everyone else is stupid and wrong.

  • roncesvalles 4 hours ago

    This is solved simply by having a chicken.go and an egg.go in the same package.

    Go appears to encourage large source files, since it allows you to define multiple "classes" (types with methods) within the same file. This is probably intentional to combat the complexity of having otherwise cohesive logic littered across several small files in OOP-first languages.

    But I like your example and get your point. What if chicken.go and egg.go diverged a few levels earlier in the dir path. All the Go repos that I've seen in production have very flat folder structures, so I guess this is just how Go is written.

    • ninetyninenine 3 hours ago

      I joined a company who wanted really clean code in their code base. That meant really good categorical organization of things into folders based off of semantic meaning about what those primitives did. You can imagine the nightmare that caused.

      People simply just didn't understand what I was saying that there was no inherent meaning in trying to make our naming conventions with folders work with go's dependency rules. They thought the work of untangling all the dependencies, folders and the naming was some quest towards a perfect design.

      You can also see in this thread, that a lot of people debating the issue with me believe the same thing. They think this arbitrary rule is speaking to a fundamental design axiom and they use big words to call it "domain modelling"

lifthrasiir 5 hours ago

Agree that the language has to be analyzed under the original design goals to be fair. Disagree that Go is definitely well-designed in that circumstance, however. A major weakness of this essay is that all perceived issues with Go have to be irrelevant to the original design goals, quite absurd if you think about that.

I would instead say that Go had big things right, while smaller things might be not as right---but not exactly "incorrect". For example explicit error handling is the big idea that Go got right, but both Rust and Zig did a lot to refine error handling experiences; comparing them with Go is just a non-starter.

  • kerkeslager 4 hours ago

    > Agree that the language has to be analyzed under the original design goals to be fair.

    I mean if you're analyzing languages in a void, I guess, but we're not. We're looking at languages as tools to solve programming programs. And almost nobody has the problems that Go was designed around, so the original design goals are kind of irrelevant.

    I mean, how many of you have worked on a million line codebase? If you haven't, why would you care that compiling millions of lines quickly was a design goal for Go? Yes, smaller codebases are also faster to compile, but partial compilation works just fine for that in other languages.

    • lifthrasiir 3 hours ago

      I should note that I do see merits of "unfair" analyses, in fact they are probably as important as fair analyses. It's just that such analyses would inevitably cover what designers originally didn't even think of, so such analyses should ideally be considered sort of bonuses instead of main criticisms.

benatkin 5 hours ago

> But don't say it wasn't (well) designed.

I’ll say that it isn’t well designed. I draw a line at treating an unused import as an error.

The creators of Go had opinions, and so do I. That’s how it works.

noelwelsh 6 hours ago

I feel this article misses the point in many cases. Take, for example, error handling. It's labourious in Go, but that's not the only issue. I'm not going to go into all the details in this comment (others have covered this at length) but:

1. Go's error handling has four possible cases (result and no error, no result and error, result and error, no result and no error) when only two make sense.

2. A consequence of the above is that it requires null values (nil in Go)

I don't see how this can be considered good design.

  • Zanfa 6 hours ago

    > 1. Go's error handling has four possible cases (result and no error, no result and error, result and error, no result and no error) when only two make sense.

    A partial success, that returns both a result and an error definitely makes sense. For example, you’re streaming data over a network, receive some bytes, but get disconnected before it’s complete. It would make sense to have both the data received so far as well as the error, in order to resume.

    I’m sure there’s an appropriate situation for the 4th case as well.

    • guappa an hour ago

      Your example doesn't make sense because that's not how the syscall works.

    • murderfs 5 hours ago

      Does it make sense for every function to have those four cases, though?

      • noelwelsh 5 hours ago

        Exactly. One can think of examples where these cases make sense, but there are many more examples where there are more cases (e.g. there many be multiple kinds of partial successes) or fewer (most functions, which either succeed or fail.)

        Also, the design requires null values. A point which the replies have ignored.

      • frou_dh 4 hours ago

        This is why it's table stakes for a decent type-system to have both product-types and sum-types. They comprise both sides of the coin, and you use the one that actually makes sense for the piece of code being written.

        A justification for Go's deficient type-system I've seen repeatedly over the years, along the lines of "Well actually the occasional function can legitimately return both a result AND an error" is nonsensical. It's basically excusing giving 98% of error-returning functions a semantically wrong return type (a tuple) because it happens to be correct in 2% of cases.

        Every function should have a semantically correct return type! If a product-type is occasionally correct for an error-returning function, use one just for those functions! And use a sum-type for the other 98% where that is correct for them!

    • WolfCop 5 hours ago

      Streaming data, there was no error but also no new data available.

      • guappa an hour ago

        That is an error :)

  • gr3ml1n 6 hours ago

    I can think of situations where all four make sense. Not sure what you mean?

    result+no error: data returned, no error happened. - Typical function call where you want some data.

    no Result+error: nothing to return, error happened. - That typical call failed.

    result+error: partial result, up to error. - Wrote x bytes to disk, then ran out of space.

    no result+no error: nothing to return, no error. - Data for a passed query. Nothing came back, but that's not an error.

    • nejsjsjsbsb 5 hours ago

      But not every situation. Make illegal state unreprestable is a good ideal.

  • 9rx 6 hours ago

    > Go's error handling has four possible cases

    If you consider (T, error) to be logically coupled, there is actually effectively an infinite number of cases. (T, U, error), (T, U, V, error), (T, U, V, W, error), etc. Although I suspect this take is suffering from trying to project idioms from another language onto Go.

    • catlifeonmars 5 hours ago

      (T, error) is just a special case of (T, E).

      • 9rx 5 hours ago

        At least we can agree it is (T, E) and not (T or E).

cdent 5 hours ago

From what I've seen, the people who complain about go's design are people who are more concerned about the ways in which it is not correct (by some definition) and less concerned about just getting stuff done.

I don't "like" go (or any other programming language), but I'm able to get more done in it in a shorter amount of time than any other language, the created thing consumes tiny amounts of resources, and most importantly when I _or anyone else_ goes back to change something weeks, months, years later it's easy to re-establish or gain contextual understanding.

  • guappa 43 minutes ago

    > I'm able to get more done in it in a shorter amount of time

    That's because you know it better than other languages. It's certainly not a universal experience.

  • alkonaut 4 hours ago

    These were the first lines of Go I ever wrote 15 minutes ago, as a response to the claim that Go had working enums. This is the kind of thing that makes me itch. https://go.dev/play/p/MMPMh7_U81-

    I think "being simple" doesn't necessarily mean "must have subtle sharp edges and papercuts everywhere". Just like javascript I think it falls into a pit of being superficially "low complexity" or "simple", but all the subtle gotchas and unhelpful tooling (The fact a compiler can't help me realize I had added an entry to the enum, but forgotten to update a name list just means the whole feature is no better than just plain ints that we had in C since before I was born).

  • taberiand 5 hours ago

    I like go, and I use it to get stuff done. I'd be able to get stuff even more done with sum types and pattern matching and better generics and iterator tools and it's a shame Go doesn't have these things.

pjdkoch 4 hours ago

ITT: apologists and haters.

Go is a bunch of preferences and aesthetics. It clearly appeals to a bunch of folks, others have a hard time putting up with it.

Everyone is free to both love and/or hate go. The language's spirit is not going to change. I'd much rather see more realism, honesty, and acceptance. Instead, this class of discussions always seems to become incessant "no u" ping-ponging.

Certainly not what I'd consider curiosity and critical thinking.

sirwhinesalot 5 hours ago

Go is not well-designed because it is simple. It is poorly designed because it fails to be so. All the weird interactions with nil for example.

pjmlp 5 hours ago

It looks like the arguments on Apple ecosystem when arguing for design faults as virtues, us are unable to understand without enlightenment, like we are holding it wrong, tablets don't need pens, don't understand keyboard design, ...

oftenwrong 5 hours ago

The error handling is explicit only in a limited way. As the author points out, you have to explicitly propagate an error condition, but you don't have to explicitly not-propagate it. Exceptions are reversed in this way: propagation is implicit, and not-propagating is explicit. I would prefer that handling is fully explicit in all cases, and not depend on supplemental linting or sheer discipline. It's a bit incongruous that golang compiler is so fussy about unused imports, but has nothing to say about silently ignored errors.

I also find that stack traces are generally more useful than plain wrapped errors. I want to know the code path in most cases where I am looking at an error.

More generally, it's disappointing that golang has so many idiosyncrasies given its philosophy of straightforwardness. I struggle to think of any useful, general purpose language that has fared better, though.

KronisLV 6 hours ago

I think Go is a pretty good choice for getting something out the door quickly, without getting in the way too much.

The other week, I was working on two APIs: one for a team that uses Java primarily and another where I was free to pick the technology as needed and was looking in the direction of making it easy to deploy, even without containers, where I picked Go.

With Go's standard library (and a few packages here or there), I had something up and running within a few days and it mostly just worked. Things that I wanted to do were just an additional method away, which, while not as sophisticated as the various convention over configuration options out there, kept everything very discoverable and observable.

With Java, I had to setup a Spring Boot project and its security configuration mechanism (SecurityFilterChain, WebSecurityFilterConfig and lots of Filter implementations) and ran into issues where calculating file checksums for uploaded files broke because Files.exists() returned that files exist for ones with UTF-8 symbols in the name, whereas internally FileUtils.checksumCRC32() used file.isFile() which returned false. Then, I also needed -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 and for another mechanism had to look into JCE briefly, as well as configuring Maven to push packages to Nexus, as well as how to disable Spring Boot trying to connect to PostgreSQL and RabbitMQ while bringing up the app context for tests, or to connect to a special instance for tests (and hopefully eventually figure out how to automate a separate schema per CI run). Oh also deciding between OkHttp and Apache HttpComponents. Oh and also adding Apache Tika to the project, yet realizing that I can't add tika-parsers-standard due to some related dependencies.

Now, this isn't a completely fair comparison because the APIs didn't need to do 1:1 the same things and I could have chosen not to go with Spring Boot, but it's so entrenched that it's most likely the majority of projects that you'll see out there (I also like Dropwizard, but I haven't seen many people use it at all). That might sour some people's perception of using that language professionally: https://earthly.dev/blog/brown-green-language/

There are things about Go that are annoying, but it not having all of those levels of abstraction that you need to think about was comparably nicer. Plus, not needing to think about JDK versions and instead just getting a single file that you can ship. Oh, and on a related note, projects like Wails are also really nice (I tried Tauri, the builds were too long and the Electron bundles are a bit too big for my needs in some other projects): https://wails.io/

I like many of the languages out there (even Java and ones like .NET) but the projects and ecosystems around them can make them harder to use sometimes.

  • oftenwrong 4 hours ago

    You were certainly bitten by the Java ecosystem, but I would agree that you hold some responsibility. Adopting Spring Boot so casually is really quite questionable. It's essentially opting to build your application within a giant, bonkers codebase. In the golang community there is a held value of using the standard library and tools, which is helped those being very good. I would suggest that Java is a better experience if you follow that same approach. If you want to serve HTTP, use the jdk.httpserver module. If you want to make HTTP requests, use java.net.http. If you want to interact with the filesystem, use java.nio.file. To build, use the included tools in the JDK. When it comes to cases where a third party dependency is warranted, such as use cases for Tika, you will have to deal with some questionable design decisions, but it's much easier when you have just a few libraries that you use by hand, and no insane framework trying to stitch everything together for you. For getting those dependencies, you are going to be acquiring them from public maven repositories, but I would suggest using Coursier or Maven Resolver instead of actually using Maven.

saghm 6 hours ago

> If you see a function call in Go you know there is one definition you need to check.

...unless it's a method on an interface type. I get the point being made about overloading, but the above statement of it is a bit too strong.

UltraSane 5 hours ago

I enjoy writing Java and C# so much more than Go.

AlgebraFox 6 hours ago

No matter how many blog posts try to justify Go, it is a lost ship. Yes it is used by many companies (I still don't understand why), but that does not make it a well designed language.

> But secondly designing errors as explicit values has been a trend-(re)setter. Go, Rust and Zig have all chosen to use this approach.

Just because Go treats errors as values, you can't compare Go's error handling with Rust's. Rust provides clean abstractions with Algebraic Data Types, ? operator over Result type to reduce polluting code with if-else nonsense in every single function. I bet atleast 30% of Go code in a project is error handling for the sake of handling.

Further Go's syntax inconsistency irritates me more. I still don't understand if I should put a comma or semicolon or nothing between members in a struct definition. Also

a := 10

...

a := 20 // error. a is defined.

a, b := 20, 30 // accepted. why ??

  • guappa 16 minutes ago

    My company has 2 teams that use go for different reasons:

    * Easy to crossbuild and you distribute a single executable (although of course someone managed to pull some dependency that was dynamically loaded and made our CLI depending on a running Gnome session)

    * C developers who can't write C code that doesn't segfault every 3 minutes (because they never understood how threads work) can still have their ego but also produce somewhat useful code.

  • theThree 5 hours ago

    > a, b := 20, 30 // accepted. why ??

    I guess it is a dept of error handling. Otherwise you have to write "foo,err1 := Foo();bar,err2 := Bar(),.."

  • fullstackchris 5 hours ago

    So are you saying you would take on the initial cognitive load of Rust vs. Go for _every_ project?

  • api 6 hours ago

    Go’s unreasonable success is due to Google but also its main feature: low up front cognitive load.

    A lot of that is from lack of async, or rather that async is hidden behind go routines.

    • catlifeonmars 5 hours ago

      > A lot of that is from lack of async, or rather that async is hidden behind go routines.

      That’s an interesting take, but one that I’ve seen in multiple places in this thread. I find that introducing goroutines into a program immediately increases the cognitive load as now you need to think about joining, data races, and need to think about error handling differently (golang.org/x/sync/errgroup is an excellent library by the way).

      Oh and did I mention deadlocks?

jillesvangurp 6 hours ago

I encounter a lot of different software stacks when helping clients as a consultant. Consultancy is something I do next to my main job which is being a CTO of a small, bootstrapped startup. That sounds like a posh job but what it really means is that I do most of the technical work together with one other person.

I've always been opinionated about tools and languages. And we probably all have our biases and preferences. What I've realized doing consultancy is that it's rarely advisable for me to tell my clients to change whatever the hell they are doing. It's actively harmful to do so. And I encounter pretty much everything you can name. Including Go.

At the same time, it's taught me to steer clear of certain tech stacks for my own things. I know I don't like them by virtue of having seen others do things with them. Go, is one of those things that's arguably alright and widely used. But I wouldn't prefer it based on what I've seen.

The reasons are mostly non technical:

1) it's a transition language for people. They might start with something like Javascript or python and they might crave something a bit more rigorous. Go is an easy choice. It's there, it's widely used. It has great libraries, and so on. But I've seen people progress to other languages as well. It's a phase people go through as they develop themselves. And there's a definite "if you have hammer, every problem looks like a nail" thing going on with it. For me Go would be a step backwards, not forwards. I have some languages I'm interested in learning. Go is not one of them.

2) it's a Google language and Google has this big "not invented here" syndrome that often translates into them reinventing wheels. In this case C++. They embarked on their journey around the same time several other languages kicked off. Rust, Swift, Kotlin, etc. all emerged roughly around the same time (15-20 years ago). Swift is Apple's language and it suffers from the same issue. So, even though Google's take on this is interesting. It's not the only one or necessarily the best one (subjective of course). Rust and Kotlin are a bit less tied to one company. And both are widely used across most/all big tech companies and less entrenched in just one of them. Full disclosure, I prefer Kotlin out of these languages and actually use it for a lot of things people might think Go is good at.

3) It's a conservative language community. Despite being relatively new, the community seems to resist change a lot. Endless debates about exception handling, package management, generics, etc. and other aspects of the language seem to progress at a snails pace with people bickering endlessly about whether that's good or bad design. That stuff just bores me. Just fix it already. I don't want to read/hear endless reasons why something cannot/must not/should not be a thing because reasons that boil down "we don't like change".

So, no Go for me. But some of my clients use it and it's of course fine for them. I consult them on what they should build, not how to build it. A lot of that stuff works just about the same regardless of the language. Use what works for you, what you are familiar with, etc. Not what others tell you to use.

oldpersonintx 6 hours ago

Go and Python are both firmly rooted in anti-cleverness

Cleverness makes for macho code golfing and aha moments in small codebases

Cleverness makes you quit your job when the codebases pass 10k lines

  • guappa 22 minutes ago

    Go is rooted in: "let's make a language with managed memory that still manages to crash with memory errors".

    They should have hired someone that knew what he was doing.

  • KronisLV 6 hours ago

    Not sure why this was flagged.

    > Cleverness makes you quit your job when the codebases pass 10k lines

    I've very much felt that in codebases that are hundreds of thousands of lines of code.

    Essentially it becomes: "Is it easier to track down this bug and fix it over a week or two (hopefully the solution isn't needed by tomorrow night), or is it easier to quit my job?"

    Sometimes the answer unironically is the second option.

  • theshrike79 2 hours ago

    I always say that you can write clever code on your free time.

    When on the clock, make stuff that's readable and easy to manage.

    • guappa 22 minutes ago

      > make stuff that's readable and easy to manage.

      So, not go.

jrimbault 6 hours ago

> Where you see a hack I see an elegant design.

https://groups.google.com/g/golang-dev/c/r4rdPdsH1Fg/m/JG3Wl...

> I can think of two examples of major aesthetic shifts in Go code that seemed ugly at the time but were adopted because they simplified software engineering and now look completely natural.

https://research.swtch.com/vgo-principles

> type Duration int64

https://github.com/golang/go/blob/519c0a2323700934cbec97b75d...

> default values

https://gotipplay.golang.org/p/jS6wwznSR7F