38 ms·
Go is still not good
- keyle 1y agoI use Go daily for work, alongside Dart, Python. I say switching to Go is like a different kind of Zen. It takes time, to settle in and get in the flow of Go... Unlike the others, the LSP is fast, the developer, not so much. Once you've lost all will to live you become quite proficient at it. /s
- written-beyond 1y agoMy developer experience was similar to rust but more frustrating because of the lax typing. ISTG if I get downvoted for sharing my opinion I will give up on life.
- aloukissas 1y agoI also sing "Fade to Black" when I have to write go :D
- tialaramex 1y agoI was like "Have I ever actually heard that?" and the answer turns out to be "No" so now I have (it's a Metallica track about suicidal ideation, whether it's good idea to listen to it while writing Go I could not say and YMMV).
- theshrike79 1y agoI've been writing small Go utilities for myself since the Go minor version number was <10 I can still check out the code to any of them, open it and it'll look the same as modern code. I can also compile all of them with the latest compiler (1.25?) and it'll just work. No need to investigate 5 years of package manager changes and new frameworks.
- figmert 1y agoHas Go become the new PHP? Every now and then I see an article complaining about Go's shortcomings.
- giancarlostoro 1y agoNo, this has been the case as long as Go has been around, then you look and its some C or C++ developer with specific needs, thats okay, its not for everyone.
- ginko 1y agoGo was announced as a replacement for C & C++ so I think it's reasonable to compare it to that.
- Matl 1y agoIt was intended as a as a replacement for C & C++ for Google's use case of network services btw.
- pjmlp 1y agoNot really, no one at other other than the original authors though of that, the authors had an issue with C++ compile times and were sponsored by their manager to work on this Go side project of theirs. Google's networking services keep being writen in Java/Kotlin, C++, and nowadays Rust.
- Matl 1y agoGo was written with the experience of a bunch of C people who weren't particularly fond of C++ while writing network services/systems at Google and have written Go as a 'C for the 21st century' with the sort of use case they used C++ for previously at Google. People like Rob Pike and Ken Thompson certainly knew that you can't put in a GC and cover all systems programming use cases, but they knew that Go could cover their use cases. Or are you suggesting that they were frustrated with C++ so they decided to write a language they couldn't use instead of C++ for their use case? > Google's networking services keep being writen in Java/Kotlin, C++, and nowadays Rust. And? Google is a massive company that uses many languages across many teams. That doesn't mean that some people at Google, incl Go's original creators, would not use Go nowdays to write what they would previously use C++ for.
- nikolayasdf123 1y agoif this is the worst, not too bad.
- giancarlostoro 1y agoAgree, most of us arent needing niche C++ / C language features, what Go has for us is sufficient.
- pif 1y agoIt doesn't need to be good because it is not meant for good developers.
- Buttons840 1y agoAnd it's perfect for most business software, because most businesses are not focused on building good software. Go has a good-enough standard library, and Go can support a "pile-of-if-statements" architecture. This is all you need. Most enterprise environments are not handled with enough care to move beyond "pile-of-if-statements". Sure, maybe when the code was new it had a decent architecture, but soon the original developers left and then the next wave came in and they had different ideas and dreamed of a "rewrite", which they sneakily started but never finished, then they left, and the 3rd wave of developers came in and by that point the code was a mess and so now they just throw if-statements onto the pile until the Jira tickets are closed, and the company chugs along with its shitty software, and if the company ever leaks the personal data of 100 million people, they aren't financially liable.
- theshrike79 1y agoGo has extremely robust linters just for the corporate use-case. And gofmt. Every piece of code looks the same and can be automatically, neutrally, analysed for issues.
- softwaredoug 1y agoI like Go, but my main annoyance is deciding when to use a pointer or not use a pointer as variable/receiver/argument. And if its an interface variable, it has a pointer to the concrete instance in the interface 'struct'. Some things are canonically passed as pointers like contexts. It just feels sloppy and I'm worried I'm going to make a mistake.
- empath75 1y agoUsing pointers as optional types is the absolute worst part of using go.
- vbezhenar 1y agoJust use pointers everywhere? Who cares.
- softwaredoug 1y agoBut just not a pointer to an interface. Its annoying to need to think about whether I’m working with an interface type of concrete type. And if use pointers everywhere, why not make it the default?
- grey-area 1y agoI just always use pointers for structs.
- nikolayasdf123 1y agoI 80% of time use structs. common misunderstanding: it does not reduce performance for pointer vs value receivers (Go compiler generates same code for both, no copy of struct receiver happens). most of structs are small anyways, safe to copy. Go also automatically translates value receivers and pointer receivers back-and-forth. and if I see pointer I see something that can be mutated (or very large). in fact, if I see a pointer, I think "here we go.. will it be mutated?". written 400,000 LOC in Go, rarely seeing this issue.
- spicyusername 1y ago
- baq 1y ago> Though Python is almost entirely refcounted, so one can pretty much rely on the __del__ finalizer being called. yeah no. you need an acyclic structure to maybe guarantee this, in CPython. other Python implementations are more normal in that you shouldn't rely on finalizers at all.
- sgarland 1y agoI love Python, but the sheer number of caveats and warnings for __del__ makes me question if this person has ever read the docs [0]. My favorite WTF: > It is possible (though not recommended!) for the __del__() method to postpone destruction of the instance by creating a new reference to it. This is called object resurrection. [0]: https://docs.python.org/3/reference/datamodel.html#object.__del__ https://docs.python.org/3/reference/datamodel.html#object.__...
- guappa 1y agoHow does this relate to the claim of the parent comment that cyclic structures are never freed in python (which is false, btw)?
- thomashabets2 1y agoAuthor here. Yes, yes. Hence the words "almost" and "pretty much". For exactly this reason.
- tex0 1y agoIf you don't like Go, then just let go. I hope nobody forces you to use it. Some critique is definitely valid, but some of it just sounds like they didn't take the time to grasp the language. It's trade offs all the way. For example there is a lot I like about Rust, but still no my favorite language.
- 7thpower 1y agoWhich begs the question: What is your favorite language?
- klabb3 1y agoDisagree. Most critiques of Go I've read have been weak. This one was decent. And I say that as a big enjoyer of Go. That said I really wish there was a revamp where they did things right in terms of nil, scoping rules etc. However, they've commited to never breaking existing programs (honorable, understandable) so the design space is extremely limited. I prefer dealing with local awkwardness and even excessive verbosity over systemic issues any day.
- ben0x539 1y agoFew things are truly forced upon me in life but walking away from everything that I don't like would be foolish. There is compromise everywhere and I don't think entering into a tradeoff means I'm not entitled to have opinions about the things I'm trading off. I don't think the article sounds like someone didn't take the time to grasp the language. It sounds like it's talking about the kind of thing that really only grates on you after you've seriously used the language for a while.
- ddlsmurf 1y agoSure but life choices are one thing, but this critique is still valuable. I learned a thing or two, and also think go can improve (I understand it's because I don't grok the language but I still prefer map to append in a loop)
- tapirl 1y agoGo indeed has some problems. But IMHO, none described in this article is valid.
- blixt 1y agoI've been using Go more or less in every full-time job I've had since pre-1.0. It's simple for people on the team to pick up the basics, it generally chugs along (I'm rarely worried about updating to latest version of Go), it has most useful things built in, it compiles fast. Concurrency is tricky but if you spend some time with it, it's nice to express data flow in Go. The type system is most of the time very convenient, if sometimes a bit verbose. Just all-around a trusty tool in the belt. But I can't help but agree with a lot of points in this article. Go was designed by some old-school folks that maybe stuck a bit too hard to their principles, losing sight of the practical conveniences. That said, it's a _feeling_ I have, and maybe Go would be much worse if it had solved all these quirks. To be fair, I see more leniency in fixing quirks in the last few years, like at some point I didn't think we'd ever see generics, or custom iterators, etc. The points about RAM and portability seem mostly like personal grievances though. If it was better, that would be nice, of course. But the GC in Go is very unlikely to cause issues in most programs even at very large scale, and it's not that hard to debug. And Go runs on most platforms anyone could ever wish to ship their software on. But yeah the whole error / nil situation still bothers me. I find myself wishing for Result[Ok, Err] and Optional[T] quite often.
- guappa 1y ago> The type system is most of the time very convenient In what universe?
- theshrike79 1y agoIn mine. It's Just Fine. Is it the best or most robust or can you do fancy shit with it? No But it works well enough to release reliable software along with the massive linter framework that's built on top of Go.
- diarrhea 1y ago> massive linter framework I wonder why that ended up being necessary... ;)
- torginus 1y agoI still don't understand why defer works on function scope, and not lexical scope, and nobody has been able to explain to me the reason for it. In fact this was so surprising to me is that I only found out about it when I wrote code that processed files in a loop, and it started crashing once the list of files got too big, because defer didnt close the handles until the function returned. When I asked some other Go programmers, they told me to wrap the loop body in an anonymus func and invoke that. Other than that (and some other niggles), I find Go a pleasant, compact language, with an efficient syntax, that kind of doesn't really encourage people trying to be cute. I started my Go journey rewriting a fairly substantial C# project, and was surprised to learn that despite it having like 10% of the features of C#, the code ended up being smaller. It also encourages performant defaults, like not forcing GC allocation at every turn, very good and built-in support for codegen for stuff like serialization, and no insistence to 'eat the world' like C# does with stuff like ORMs that showcase you can write C# instead of SQL for RDBMS and doing GRPC by annotating C# objects. In Go, you do SQL by writing SQL, and you od GRPC by writing protobuf specs.
- grey-area 1y agoThere’s probably no deep reason, does it matter much?
- torginus 1y agoYes it does, function-scope defer needs a dynamic data structure to keep track of pending defers, so its not zero cost. It can be also a source of bugs where you hang onto something for longer than intended - considering there's no indication of something that might block in Go, you can acquire a mutex, defer the release, and be surprised when some function call ends up blocking, and your whole program hangs for a second.
- nasretdinov 1y agoI think it's only a real issue when you're coming from a language that has different rules. Block-scoping (and thus not being able to e.g. conditionally remove a temp file at the end of a function) would be equally surprising for someone coming from Go. But I do definitely agree that the dynamic nature of defer and it not being block-scoped is probably not the best
- porridgeraisin 1y ago> Previous posts Why Go is not my favourite language and Go programs are not portable have me critiquing Go for over a decade. I chuckled
- colesantiago 1y agoSame here, I don't know if this makes him Go's biggest fan or this is actually genuinely sad. Never had any problems with Go as it makes me millions each year.
- neuroelectron 1y agoNever had a problem with Enron because I sold it when it was high.
- jact 1y agoI worked briefly on extending an Go static site generator someone wrote for a client. The code was very clear and easy to read, but difficult to extend due to the many rough edges with the language. Simple changes required altering a lot of code in ways that were not immediately obvious. The ability to encapsulate and abstract is hindered in the name of “simplicity.” Abstraction is the primary way we achieve simple and easy to extend code. John Ousterhoust defined a complex program as one that is difficult to extend rather than necessarily being large or difficult to understand at scale. The average Go program seems to violate this principle a lot. Programs appear “simple” but extension proves difficult and fraught. Go is a case of the emperor having no clothes. Telling people that they just don’t get it or that it’s a different way of doing things just doesn’t convince me. The only thing it has going for it is a simple dev experience.
- morsecodist 1y agoI find the way people talk about Go super weird. If people have criticisms people almost always respond that the language is just "fine" and people kind of shame you for wanting it. People say Go is simpler but having to write a for loop to get the list of keys of a map is not simpler.
- TheDong 1y agoI agree with your point, but you'll have to update your example of something go can't do > having to write a for loop to get the list of keys of a map We now have the stdlib "maps" package, you can do: keys := slices.Collect(maps.Keys(someMap)) With the wonder of generics, it's finally possible to implement that. Now if only Go was consistent about methods vs functions, maybe then we could have "keys := someMap.Keys()" instead of it being a weird mix like `http.Request.Headers.Set("key", "value")` but `map["key"] = "value"` Or 'close(chan x)' but 'file.Close()', etc etc.
- morsecodist 1y agoFair I stopped using Go pre-generics so I am pretty out of date. I just remember having this conversation about generics and at the time there was a large anti-generics group. Is it a lot better with generics? I was worried that a lot of the library code was already written pre-generics.
- akoboldfrying 1y agodefer is no worse than Java's try-with-resources. Neither is true RAII, because in both cases you, the caller, need to remember to write the wordy form ("try (...) {" or "defer ...") instead of the plain form ("..."), which will still compile but silently do the wrong thing.
- xyzzyz 1y agoSure, true RAII would be improvement over both, but the author's point is that Java is an improvement over Go, because the resource acquisition is lexical scoped, not function-scoped. Imagine if Java's `try (...) { }` didn't clear the resource when the try block ends, but rather when the wrapping method returns. That's how Go's defer works.
- akoboldfrying 1y agoCan't you create a new block scope in Go? If not, I agree. If so, just do that if you want lexical scoping?
- gf000 1y agoYou have to create an anonymous function.
- Jtsummers 1y agodefer is not block scoped in Go, it's function scoped. So if you want to defer a mutex unlock it will only be executed at the end of the function even if placed in a block. This means you can't do this (sketch): func (f *Foo) foo() { // critical section { f.mutex.Lock() defer f.mutex.Unlock() // something with the shared resource } // The lock is still held here, but you probably didn't want that } You can call Unlock directly, but then if there's a panic it won't be unlocked like it would be in the above. That can be an issue if something higher in the call stack prevents the panic from crashing the entire program, it would leave your system in a bad state. This is the key problem with defer. It operates a lot like a finally block, but only on function exit which means it's not actually suited to the task. And as the sibling pointed out, you could use an anonymous function that's immediately called, but that's just awkward, even if it has become idiomatic.
- _cenw 1y ago[flagged]
- saltserv 1y ago[dead]
- the_duke 1y agoI personally don't like Go, and it has many shortcomings, but there is a reason it is popular regardless: Go is a reasonably performant language that makes it pretty straightforward to write reliable, highly concurrent services that don't rely on heavy multithreading - all thanks to the goroutine model. There really was no other reasonably popular, static, compiled language around when Google came out. And there still barely is - the only real competitor that sits in a similar space is Java with the new virtual threads. Languages with async/await promise something similar, but in practice are burdened with a lot of complexity (avoiding blocking in async tasks, function colouring, ...) I'm not counting Erlang here, because it is a very different type of language... So I'd say Go is popular despite the myriad of shortcomings, thanks to goroutines and the Google project street cred.
- zwnow 1y agoWhat modern language is a better fit for new projects in your opinion?
- aloukissas 1y agoElixir, with types
- sarchertech 1y agoThat doesn’t exist yet. Also Elixir is in no way a replacement for Go. It can’t match it for performance. There’s no mutable array, almost everything is a linked list, and message passing is the only way to share data. I primarily use Elixir in my day job, but I just had to write high performance tool for data migration and I used Go for that.
- zwnow 1y agoThis one i can get behind.
- pmarreck 1y agoI love Elixir but you cannot compile it into a single binary, it is massively concurrent but single-threaded slow, and deployment is still nontrivial. And lists are slower than arrays, even if they provide functional guarantees (everything is a tradeoff…) That said, pretty much everything else about it is amazing though IMHO and it has unique features you won’t find almost anywhere else
- jonathan920 1y agoOh no , Rust is too tough, go is no good, am i going back to java?
- maxloh 1y agoMaybe the new in-development Carbon language? It sounds promising, but it is nowhere near its 1.0 release.
- masklinn 1y agoCarbon exists only for interoperating with and transitioning off of C++. Creating a new code base in carbon doesn’t really make sense, and the project’s readme literally tells you not to do that.
- maxloh 1y ago> ... and the project’s readme literally tells you not to do that. Could you quote which paragraph you're talking about? AFAIK, interoperability with C++ code is just one of their explicit goals; they only place that as the last item in the "Language Goals" section.
- masklinn 1y ago> Existing modern languages already provide an excellent developer experience: Go, Swift, Kotlin, Rust, and many more. Developers that can use one of these existing languages should.
- pjmlp 1y agoSo many options in-between.
- antiquark 1y agoI'm still appalled that there's no "do while" loop in go.
- pansa2 1y agoPython doesn’t have one either
- zwnow 1y ago> Wait, what? Why is err reused for foo2()? Is there’s something subtle I’m not seeing? Even if we change that to :=, we’re left to wonder why err is in scope for (potentially) the rest of the function. Why? Is it read later? First time its assigned nil, second time its overwritten in case there's an error in the 2nd function. I dont see the authors issue? Its very explicit.
- thomashabets2 1y agoAuthor here: I'm not talking about the value. I'm talking about the lifetime of the variable. After checking for nil, there's no reason `err` should still be in scope. That's why it's recommended to write `if err := foo(); err != nil`, because after that, one cannot even accidentally refer to `err`. I'm giving examples where Go syntactically does not allow you to limit the lifetime of the variable. The variable, not its value. You are describing what happens. I have no problem with what happens, but with the language.
- zwnow 1y agoWhy does the lifetime even matter?
- thomashabets2 1y agoI gave an example in the post, but to spell it out: Because a typo variable is not caught, e.g. as an unused variable. The example from the blog post would fail, because `return err` referred to an `err` that was no longer in scope. It would syntactically prevent accidentally writing `foo99()` instead of `err := foo99()`.
- lowmagnet 1y agoI'll have to read the rest later but this was an unforced error on the author's part. There is nothing unclear about that block of code. If err isn't but, it was set, and we're no longer in the function. If it's not, why waste an interface handle?
- crinkly 1y agoI dislike Go but I haven’t found anything else I dislike less.
- slackfan 1y agoStill better (compiler speed) than Rust.
- gf000 1y agoStill not playing remotely in the same league. Only one of them is a "systems language", reusing Go's inappropriate marketing term.
- gwd 1y agoAnyone want to try to explain what he's on about with the first example? bar, err := foo() if err != nil { return err } if err := foo2(); err != nil { return err } The above (which declares a new value of err scoped to the second if statement) should compile right? What is it that he's complaining about? EDIT: OK, I think I understand; there's no easy way to have `bar` be function-scoped and `err` be if-scoped. I mean, I'm with him on the interfaces. But the "append" thing just seems like ranting to me. In his example, `a` is a local variable; why would assigning a local variable be expected to change the value in the caller? Would you expect the following to work? int func(a *MyStruct) { a = &MyStruct{...} } If not why would you expect `a = apppend(a, ...)` to work?
- terminalbraid 1y agoYou didn't copy the code correctly from the first example.
- gwd 1y agoWell no, the second "if" statement is a red herring. Both of the following work: bar, err := foo() if err != nil { return err } if err = foo2(); err != nil { return err } and bar, err := foo() if err != nil { return err } if err := foo2(); err != nil { return err } He even says as much: > Even if we change that to :=, we’re left to wonder why err is in scope for (potentially) the rest of the function. Why? Is it read later? My initial reaction was: "The first `err` is function-scope because the programmer made it function-scope; he clearly knows you can make them local to the if, so what's he on about?` It was only when I tried to rewrite the code to make the first `err` if-scope that I realized the problem I guess he has: OK, how do you make both `err` variable if-scope while making `bar` function-scope? You'd have to do something like this: var bar MyType if lbar, err := foo(); err != nil { return err } else { bar = lbar } Which is a lot of cruft to add just to restrict the scope of `err`.
- sublimefire 1y agoedit: the main rant about err was that it is left in scope but I believe the author does not like that
- sublimefire 1y agoThis post is just an attention grabbing rage bate. Listed issues are superficial unless the person is a bit far into the spectrum. There is no good datapoint which would weigh the issues against real world problems, i.e. how much does it cost. Even the point about ram is weak without the data.
- fschuett 1y agoTechnically, the term "billion dollar mistake", coined in 1965, would now be a "10 billion dollar mistake" in 2025. Or, if the cost is measured in terms of housing, it would be a "21 billion dollar mistake". :^/
- masklinn 1y agoThe billion dollar mistake was made in 1965 but the term was coined in 2009, defined as the following: > I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
- YetAnotherNick 1y agoWhat does this mean? Do they just use recover and keep bad data? > The standard library does that. fmt.Print when calling .String(), and the standard library HTTP server does that, for exceptions in the HTTP handlers. Apart from this most doesn't seem that big of a deal, except for `append` which is truly a bad syntax. If you doing it inplace append don't return the value.
- thomashabets2 1y agoThe standard library recovers from the panic, and program continues. This means that if you do: func (f Foo) String() string { some.Lock() t := get_something() some.Unlock() t = transform_it(t) return t } And `get_something()` panics, then the program continues with a locked mutex. There are more dangerous things than a deadlocked program, of course. It's non-optional to use defer, and thus write exception safe code. Even if you never use exceptions.
- andy_ppp 1y agoThey are forcing people to write Typescript code like it’s Golang where I am right now (amongst other extremely stupid decisions - only unit test service boundaries, do not pull out logic into pure functions, do not write UI tests, etc.). I really must remember to ask organisations to show me their code before joining them. (I realise this isn’t who is hiring, but email in bio)
- theshrike79 1y agoHave you seen Java people write Python? Same vibe :)
- toastercat 1y agoReminded me of this classic talk https://www.youtube.com/watch?v=o9pEzgHorH0 https://www.youtube.com/watch?v=o9pEzgHorH0
- Mawr 1y agoSure have: https://youtu.be/wf-BqAjZb8M?t=831 https://youtu.be/wf-BqAjZb8M?t=831
- sebstefan 1y agoAh yes. I love working at places that hire experts just to tell them how they should do the work they're an expert at.
- candiddevmike 1y agoI do this and think it works really well... myfunc(arg: string): Value | Err I really try not to throw anymore with typescript, I do error checking like in Go. When used with a Go backend, it makes context switching really easy...
- andy_ppp 1y agoThey still throw and just have millions of try catch blocks repeated everywhere around almost every function :-/
- commandersaki 1y ago“What color is your nil?” — The two billion dollar mistake. Talk about hyperbole.
- lvl155 1y agoI think a lot of people got on the Go train because of Google and not necessarily because it was good. There was a big adoption in Chinese tech scene for example. I personally think Rust/Go/Zig and other modern languages suffer a bit from trying too hard not to be C/C++/Java.
- wewxjfq 1y agoGo was a breath of fresh air and pretty usable right from the start. It felt like a neat little language with - finally - a modern standard library. Fifteen years ago, that was a welcome change. I think it's no surprise that Go and Node.js both got started and took off around the same time. People were looking something modern, lightweight, and simple and both projects delivered that.
- deleted 1y ago[deleted]
- 813ac4312b25c 1y ago> Probably [hello NIGHTMARE !]. Who wants that? Nobody wants that. I don't really care if you want that. Everyone should know that that's just the way slices work. Nothing more nothing less. I really don't give a damn about that, i just know how slices behave, because I learned the language. That's what you should do when you are programming with it (professionally)
- terminalbraid 1y agoIf you're fine with that then you should be upset by the subsequent example, because by your own definition "that's just not the way slices work".
- 813ac4312b25c 1y agoI am fine with the subsequent example, too. If you read up about slices, then that's how they are defined and how they work. I am not judging, I am just using the language as it is presented to me. For anyone interested, this article explains the fundamentals very well, imo: https://go.dev/blog/slices-intro https://go.dev/blog/slices-intro
- terminalbraid 1y agoThen you seem to be fine with inconsistent ownership and a behavioral dependence on the underlying data rather than the structure. You really don't see why people would point a definition that changes underneath you out as a bad definition? They're not arguing the documentation is wrong.
- assbuttbuttass 1y agoThe definition is perfectly consistent. append is in-place if there's enough capacity (and the programmer can check this directly with cap() if they want), and otherwise it allocates a new backing array.
- 1y ago
- pjmlp 1y agoAs usual, lets revisit something that Pascal could do in 1976, type StatusCodes = (Success, Ongoing, Done) Go in 2025, type StatusCodes int const ( Success StatusCodes = iota Ongoing Done )
- thiht 1y agoWhere's Pascal today?
- terminalbraid 1y agoJust below Go with Perl in between. All above Fortran, all below Visual Basic. https://www.tiobe.com/tiobe-index/ https://www.tiobe.com/tiobe-index/
- ofrzeta 1y agoIt's alive and kicking, right? :) https://www.freepascal.org https://www.freepascal.org They even have a game engine that can compile to a WASM target: https://castle-engine.io/web https://castle-engine.io/web
- dardeaup 1y agoOuch!! Pascal's lack of popularity certainly isn't due to the fact that it supports such nice enumerated types (or sets for that matter). I think he was just pointing out that such nice things have existed (and been known to exist) for a long time and that it's odd that a new language couldn't have borrowed the feature.
- pjmlp 1y agoBeing used by these folks, https://www.embarcadero.com/ https://www.embarcadero.com/ If you prefer, I can provide the same example in C, C++, D, Java, C#, Scala, Kotlin, Swift, Rust, Nim, Zig, Odin.
- fuzztester 1y agoPlease do. Would be interesting to read.
- ochronus 1y agos/good/perfect
- porridgeraisin 1y agoI wrote a small explainer on the typed-vs-untyped nil issue. It is one of the things that can actually bite you in production. Easy to miss it in code review. Here's the accompanying playground: https://go.dev/play/p/Kt93xQGAiHK https://go.dev/play/p/Kt93xQGAiHK If you run the code, you will see that calling read() on ControlMessage causes a panic even though there is a nil check. However, it doesn't happen for Message. See the read() implementation for Message: we need to have a nil check inside the pointer-receiver struct methods. This is the simplest solution. We have a linter for this. The ecosystem also helps, e.g protobuf generated code also has nil checks inside pointer receivers.
- xlii 1y agoAfter spending some time in lower level languages Go IMO makes much more sense. Your example: First one - you have an address to a struct, you pass it, all good. Second case: you set address of struct to "nil". What is nil? It's an address like anything else. Maybe it's 0x000000 or something else. At this point from memory perspective it exists, but OS will prevent you from touching anything that NULL pointer allows you to touch. Because you don't touch ANYTHING nothing fails. It's like a deadly poison in a box you don't open. Third example id the same as second one. You have a IMessage but it points to NULL (instead NULL pointing to deadly poison). And in fourth, you finally open the box. Is it magic knowledge? I don't think so, but I'm also not surprised about how you can modify data through slice passing. IMO the biggest Go shortcoming is selling itself as a high level language, while it touches more bare metal that people are used to touch.
- beanjuiceII 1y agogreat example, that is indeed tricky
- gnfargbl 1y agoAs a long-time Go programmer I didn't understand the comment about two types of nil because I have never experienced that issue, so I dug into it. It turns out to be nothing but a misunderstanding of what the fmt.Println() statement is actually doing. If we use a more advanced print statement then everything becomes extremely clear: package main import ( "fmt" "github.com/k0kubun/pp/v3" ) type I interface{} type S struct{} func main() { var i I var s *S pp.Println(s, i) // (*main.S)(nil) nil fmt.Println(s == nil, i == nil, s == i) // true true false i = s pp.Println(s, i) // (*main.S)(nil) (*main.S)(nil) fmt.Println(s == nil, i == nil, s == i) // true false true } The author of this post has noted a convenience feature, namely that fmt.Println() tells you the state of the thing in the interface and not the state of the interface, mistaken it as a fundamental design issue and written a screed about a language issue that literally doesn't exist. Being charitable, I guess the author could actually be complaining that putting a nil pointer inside a nil interface is confusing. It is indeed confusing, but it doesn't mean there are "two types" of nil. Nil just means empty.
- thomashabets2 1y agoAuthor here. No, I didn't misunderstand it. Interface variables have two types of nil. Untyped, which does compare to nil, and typed, which does not. What are you trying to clarify by printing the types? I know what the types are, and that's why I could provide the succinct weird example. I know what the result of the comparisons are, and why. And the "why" is "because there are two types of nil, because it's a bad language choice". I've seen this in real code. Someone compares a variable to nil, it's not, and then they call a method (receiver), and it crashes with nil dereference. Edit, according to this comment this two-types-of-null bites other people in production: https://news.ycombinator.com/item?id=44983576 https://news.ycombinator.com/item?id=44983576
- gnfargbl 1y ago> Author here. No, I didn't misunderstand it. Interface variables have two types of nil. Untyped, which does compare to nil, and typed, which does not. There aren't two types of nil. Would you call an empty bucket and an empty cup "two types of empty"? There is one nil, which means different things in different contexts. You're muddying the waters and making something which is actually quite straightforward (an interface can contain other things, including things that are themselves empty) seem complicated. > I've seen this in real code. Someone compares a variable to nil, it's not, and then they call a method (receiver), and it crashes with nil dereference. Sure, I've seen pointer-to-pointer dereferences fail for the same reason in C. It's not particularly different.
- bfrog 1y agoGo nearly gave me carpal tunnel with the vast quantities and almost the same but not quite the same repetitive code patterns it brings along with it. I’d never use it again.
- hu3 1y agoYou still type most of your code? AI solved my issues with carpal tunnel. And when I'm feeling fancy, I don't even type, just command AI by voice. "handle error case".
- bfrog 1y agoI've yet to see AI produce anything that wasn't hot garbage. I'm perfectly fine using Rust or C without carpal tunnel issues depending on the context.
- hu3 1y agoI could reply with skill issue. But a more constructive comment would be to use AI to auto-complete, in the same line, what you would have to type anyway. Not as a way to produce large swaths of code.
- bfrog 1y agoLet me know when the legal quagmire around AI generated junk is sorted and it can run reasonably on a local desktop not on a cloud. I might give it a go again. So far it’s been trash that requires cloud access with questionable legality around who owns what.
- spicyusername 1y agoA popular language is always going to attract some hate. Also, these kinds of discussions can be useful for helping the language evolve. But everyone knows in their heart of hearts that a few small language warts definitely don't outweigh Go's simplicity and convenience. Do I wish it had algebraic data types, sure, sure. Is that a deal-breaker, nah. It's the perfect example of something that's popular for a reason. It is easily one of the most productive languages. No fuss, no muss, just getting stuff done.
- rand0m4r 1y agoThis was an interesting read and very educational in my case, but each time I read an article criticizing a programming language it's written by someone who hasn't done anything better. It's a shame because it is just as effective as pissing in the wind.
- terminalbraid 1y agoIf you're saying someone can't credibly criticize a language without having designed a language themselves, I'll ask that you present your body of work of programming language criticisms so I know if you have "produced something better" in the programming language criticism space. Of course, by your reasoning this also means you yourself have designed a language. I'll leave out repeating your colorful language if you haven't done any of these things.
- olddustytrail 1y ago> If you're saying someone can't credibly criticize a language without having designed a language themselves Actually I think that's a reasonable argument. I've not designed a language myself (other than toy experiments) so I'm hesitant to denigrate other people's design choices because even with my limited experience I'm aware that there are always compromises. Similarly, I'm not impressed by literary critics whose own writing is unimpressive.
- kstrauser 1y agoWho would be qualified to judge their those critics’ writing as good or bad? Critics already qualified as good writers? Who vetted them, then? It’d have to be a stream of certified good authors all the way back. No, I stick by my position. I may not be able to do any better, but I can tell when something’s not good. (I have no opinion on Go. I’ve barely used it. This is only on the general principle of being able to judge something you couldn’t do yourself. I mean, the Olympics have gymnastic judges who are not gold medalists.)
- kstrauser 1y agoI’ve never been a rock star, but I think Creed sucks. I really don’t like your logic. I’m not a Michelin chef, but I’m qualified to say that a restaurant ruined my dessert. While I probably couldn’t make a crème brûlée any better than theirs, I can still tell that they screwed it up compared to their competitor next door. For example, I love Python, but it’s going to be inherently slow in places because `sum(list)` has to check the type of every single item to see what __add__ function to call. Doesn’t matter if they’re all integers; there’s no way to prove to the interpreter that a string couldn’t have sneaked in there, so the interpreter has to check each and every time. See? I’ve never written a language, let alone one as popular as Python, but I’m still qualified to point out its shortcomings compared to other languages.
- voodooEntity 1y agoAs someone who for >10 years writes golang and has written some bigger codebases using it, this are my takes on this articles claims: :Error variable Scope -> Yes can be confusing at the beginning, but if you have some experience it doesnt really matter. Would it be cool to scope it down?`Sure, but it feels like here is something blown up to an "issue" where i would see other things to alot more important for the go team to revisit. Regarding the error handling in go, some hate it , some love it : i personally like it (yes i really do) so i think its more a preference than a "bad" thing. :Two types of nil -> Funny, i never encountered this in > 10 years of go with ALOT of work in pointer juggling, so i wonder in which reality this hits your where it cant be avoided. Tho confusing i admit :It’s not portable -> I have no opinion here since i work on unix systems only and i have my compiled binaries specific shrug dont see any issue here either. :append with no defined ownership -> I mean... seriously? Your test case, while the results may be unexpected, is a super wierd one. Why you you append a mid field, if you think about what these functions do under the hood your attemp actualyl feels like you WANT to procude strange behaviour and things like that can be done in any language. :defer is dumb -> Here i 100% agree - from my pov it leads to massive resource wasting and in certain situations it can also create strange errors, but im not motivated to explain this - ill just say defer, while it seems usefull, from my pov is a bad thing and should not be used. :The standard library swallows exceptions, so all hope is lost -> "So all hope is lost" i mean you already left the realm of objectiveness long before tbut this really tops it. I wrote some quite big go applications and i never had a situation where i could not handle an exception simply by adjusting my code in a way that i prevent it from even happening. Again - i feel like someone is just in search of things to complain that could simply be avoided. (also in case someone comes up with a super specific probably once in a million case, well alrways keep in mind that language design doesnt orient on the least occuring thing). :Sometimes things aren’t UTF-8 -> I wont bother to read another whole article, if its important include an example. I have dealth with different encodings (web crawler) and i could handle all of them. :Memory use -> What you describe is one of the design decisions im not absolutly happy with, the memory handling. But than, one of my golang projects is an in memory graph storage/database - which in one of my cases run for ~2years without restart and had about 18GB of dataset stored in it. It has a lot of mutex handling (regarding your earlier complain with exxceptions, never had one) and it btw run as backend of a internet facing service so it wasnt just fed internal data. -------------------- Finally i wanne say : often things come down to personal preference. I could spend days raging about javascript, java, c++ or some other languages, but whatfor? Pick the language that fits your use case and your liking, dont pick one that doesnt and complain about it. Also , just to show im not just a big "golang is the best" fanboy, because it isnt - there are things to critizize like the previously mentioned memory handling. While i still think you just created memory leaks in your app, golang had this idea of "arenas" which would enable the code to manage memory partly himself and therefor developt much more memory efficient applications. This has stalled lately and i REALLY hope the go team will pick it up again and make this a stable thing to use. I probably would update all of my bigger codebases using it. Also - and thats something thats annoying me ALOT beacuse it made me spend alot of hours - the golang plugin system. I wrote an architecture to orchestrate processing and for certain reasons i wanted to implement the orchestrated "things" as plugins. But the plugin system as it is rn can only be described as the torments of hell. I messed with it for like 3 years till i recently dropped the plugin functionality and added the stuff directly. Plugins are a very powerfull thing and a good plugin system could be a great thing, but in its current state i would recommend noone to touch it. These are just two points, i could list some more but the point i want to get to is : there are real things you can critizize instead of things that you create yourself or that are language design decision that you just dont like. Im not sure if such articles are the rage of someone who just is bored or its ragebait to make people read it. Either way its not helping anyone.
- elktown 1y agoIs there anything that soothes devs more than developing a superiority complex of their particular tooling? And then the unquenchable thirst to bash "downwards"? I find it so utterly pathetic.
- 0x000xca0xfe 1y ago> If you stuff random binary data into a string, Go just steams along, as described in this post. > Over the decades I have lost data to tools skipping non-UTF-8 filenames. I should not be blamed for having files that were named before UTF-8 existed. Umm.. why blame Go for that?
- thomashabets2 1y agoAuthor here. What I intended to say with this is that ignoring the problem if invalid UTF-8 (could be valid iso8859-1) with no error handling, or other way around, has lost me data in the past. Compare this to Rust, where a path name is of a different type than a mere string. And if you need to treat it like a string and you don't care if it's "a bit wrong" (because it's for being shown to the user), then you can call `.to_string_lossy()`. But it's be more hard to accidentally not handle that case when exact name match does matter. When exactness matters, `.to_str()` returns `Option<&str>`, so the caller is forced to deal with the situation that the file name may not be UTF-8. Being sloppy with file name encodings is how data is lost. Go is sloppy with strings of all kinds, file names included.
- 0x000xca0xfe 1y agoThanks for your reply. I understand that encoding the character set in the type system is more explicit and can help find bugs. But forcing all strings to be UTF-8 does not magically help with the issue you described. In practice I've often seen the opposite: Now you have to write two code paths, one for UTF-8 and one for everything else. And the second one is ignored in practice because it is annoying to write. For example, I built the web server project in your other submission (very cool!) and gave it a tar file that has a non-UTF-8 name. There is no special handling happening, I simply get "error: invalid UTF-8 was detected in one or more arguments" and the application exits. It just refuses to work with non-UTF-8 files at all -- is this less sloppy? Forcing UTF-8 does not "fix" compatibility in strange edge cases, it just breaks them all. The best approach is to treat data as opaque bytes unless there is a good reason not to. Which is what Go does, so I think it is unfair to blame Go for this particular reason instead of the backup applications.
- quectophoton 1y agoIn practice, none of these thing mentioned in the article have been an issue for me, at all. (Upvoted anyway) What has been an issue for me, though, is working with private repositories outside GitHub (and I have to clarify that, because working with private repositories on GitHub is different, because Go has hardcoded settings specifically to make GitHub work). I had hopes for the GOAUTH environment variable, but either (1) I'm more dumb and blind than I thought I already was, or (2) there's still no way to force Go to fetch a module using SSH without trying an HTTPS request first. And no, `GOPRIVATE="mymodule"` and `GOPROXY="direct"` don't do the trick, not even combined with Git's `insteadOf`.
- Tohsig 1y agoDefinitely not just you. At my previous job we had a need to fetch private Go modules from Gitlab and, later, a self-hosted instance of Forgejo. CTO and I spent a full day or so doing trial and error to get a clean solution. If I recall correctly, we ultimately resorted to each developer adding `GOPRIVATE={module_namespace}` to their environment and adding the following to their `.netrc`: ``` machine {server} # e.g. gitlab.com login {username} password {read_only_api_key} # Must be actual key and not an ENV var ``` Worked consistently, but not a solution we were thrilled with.
- Tohsig 1y agoFixing my formatting in case this proves useful to anyone: machine {server} # e.g. gitlab.com login {username} password {read_only_api_key} # Must be actual key and not an ENV var
- naikrovek 1y agoShow me a programming language that does not have annoying flaws and I'll show you a programming language that does not yet exist, and probably won't ever exist. I really like Go. It scratches every itch that I have. Is it the language for your problems? I don't know, but very possibly that answer is "no". Go is easy to learn, very simple (this is a strong feature, for me) and if you want something more, you can code that up pretty quickly. The blog article author lost me completely when they said this: > Why do I care about memory use? RAM is cheap. That is something that only the inexperienced say. At scale, nothing is cheap; there is no cheap resource if you are writing software for scale or for customers. Often, single bytes count. RAM usage counts. CPU cycles count. Allocations count. People want to pretend that they don't matter because it makes their job easier, but if you want to write performant software, you better have that those cpu cache lines in mind, and if you have those in mind, you have memory usage of your types in mind.
- Capricorn2481 1y ago> At scale, nothing is cheap; there is no cheap resource if you are writing software for scale or for customers. Often, single bytes count. RAM usage counts. CPU cycles count. Allocations count Well if maximalist performance tuning is your stated goal, to the point that single bytes count, I would imagine Go is a pretty terrible choice? There are definitely languages with a more tunable GC and more cache line friendly tools than Go. But honestly, your comment reads more like gatekeeping, saying someone is inexperienced because they aren't working with software at the same scale as you. You sound equally inexperienced (and uninterested) with their problem domain.
- naikrovek 1y ago“RAM is cheap” and “CPU is cheap” are the calling cards of the inexperienced programmer who is trying to justify the poor quality of their code. End of statement.
- xlii 1y agoI don't agree with most of the article but I believe I know where it comes from. Golang's biggest shortcoming is the fact that it touches bare metal isn't visible clearly enough. It provides many high level features which makes this ambience of "we got you" but fails on delivering proper education to its users that they are going to have a dirt on their hands. Take a slice for example: even in naming it means "part of" but in reality it's closer to "box full of pointers" what happens when you modify pointer+1? Or "two types of nil"; there is a difference between having two bytes (simplification), one of struct type and the other of address to that struct and having just a NULL - same as knowing that house doesn't exist and being confident that house exists and saying it's in the middle of the volcano beneath the ocean. The Foo99 critique is another example. If you'd want to have not 99 loop but 10 billion loops each with mere 10 bytes you'd need 100GiB of memory just to exit it. If you'd reuse the address block you'd only use... 10 bytes. I also recommend trying to implement lexical scope defer in C and putting them in threads. That's a big bottle of fun. I think that it ultimately boils down to what kind of engineer one wants to be. I don't like hand holding and rather be left on my own with a rain of unit tests following my code so Go, Zig, C (from low level Languages) just works for me. Some prefer Rust or high level abstractions. That's also fine. But IMO poking at Go that it doesn't hide abstractions is like making fun of football of being child's play because not only it doesn't have horses but also has players using legs instead of mallets.
- thomashabets2 1y ago> I believe I know where it comes from […] poking at Go that it doesn't hide abstractions Author here. No, this is not where it comes from. I've been coding C form more than 30 years, Go for maybe 12-15, and currently prefer Rust. I enjoy C++ (yes, really) and getting all those handle-less knives to fit together. No, my critique of Go is that it did not take the lessons learned from decades of theory, what worked and didn't work. I don't fault Go for its leaky abstractions in slices, for example. I do fault it for creating bad abstraction APIs in the first place, handing out footguns when they are avoidable. I know to avoid the footgun of appending to slices while other slices of the same array may still be accessible elsewhere. But I think it's indefensible to have created that footgun in the year Go was created. Live long enough, and anybody will make a silly mistake. "Just don't make a mistake" is not an option. That's why programming language APIs and syntax matters. As for bare metal; Go manages to neither get the benefits possible of being high level, and at the same time not being suitable for bare metal. It's a missed opportunity. Because yes, in 2007 it's not like I could have pointed to something that was strictly better for some target use cases.
- abtinf 1y agoGo has problems, sure. But I’ve yet to see a hit piece on Go that actually holds up to real scrutiny. Usually, as here, objections to go take the form a technically-correct-but-ultimately-pedantic arguments. The positives of go are so overwhelmingly high magnitude that all those small things basically don’t matter enough to abandon the language. Go is good enough to justify using it now while waiting for the slow-but-steady stream of improvements from version to version to make life better.
- Mawr 1y agoYep, most of what the author complains about are trivial issues you could find in any language. For contrast, some real, deep-rooted language design problems with Go are: - Zero values, lack of support for constructors - Poor handling of null - Mutability by default - A static type system not designed with generics in mind - `int` is not arbitrary precision [1] - The built-in array type (slices) has poorly considered ownership semantics [2] Notable mentions: - No sum types - No string interpolation [1]: https://github.com/golang/go/issues/19623 https://github.com/golang/go/issues/19623 [2]: https://news.ycombinator.com/item?id=39477821 https://news.ycombinator.com/item?id=39477821
- baranul 1y agoThere are other choices of languages, that are close to and influenced by Golang. Languages such as Vlang[1] (which addresses several issues mentioned) and maybe Odin[2]. Even more, they are at the stage where advance programmers can contribute or influence them in the ways that they might find satisfactory. Golang is too far down the road and cemented in its ways, to expect such significant changes in direction. At this stage, people need to accept it for what it is or look elsewhere. [1]: https://vlang.io/ https://vlang.io/ [2]: https://odin-lang.org/ https://odin-lang.org/
- singularity2001 1y agoThat's why there is the Goo language: Go with syntactic sugar and batteries included https://github.com/pannous/goo/ https://github.com/pannous/goo/ • errors handled by truthy if or try syntax • all 0s and nils are falsey • #if PORTABLE put(";}") #end • modifying! methods like "hi".reverse!() • GC can be paused/disabled • many more ease of use QoL enhancements
- benjiro 1y ago[flagged]
- J_Shelby_J 1y agoNo one cares more about rust than Gophers.
- arccy 1y agoUsually it's the other way around...
- tschellenbach 1y agoI both agree with these points, and also think it absolutely doesn't matter. Go is the best language if you need to ship quickly and have solid performance. Also Go + AI works amazingly well. So in some ways you can actually move faster compared to languages like Node and Python these days.
- tonyhart7 1y agoYeah the language doesn't feel next gen I can see why people pick it but its major step up in convenience rather than major step up in evolution programming language itself
- cyberpunk 1y agoI've written a fair chunk of go in $dayjob and I have t say it's just... Boring. I know that sounds like a weird thing to complain about, but I just can't get enthused for anything I write in go. It's just.. Meh. Not sure why that is, guess it doesn't really click for me like other languages have in the past. It's a good language for teams, for sure, though.
- nasretdinov 1y agoNo, it's absolutely meant to be boring by design. It's also a downside, obviously, but it's easily compensated by working on something that's already challenging. The language standing out of your way is quite useful in such cases
- emseetech 1y agoGo being boring is exactly why I use it.
- Mawr 1y ago> its major step up in convenience rather than major step up in evolution programming language itself The distinction you're making here does not exist IMO. Convenience is the entire point of design.
- defraudbah 1y agolol, first I thought - "cmon, errors are bad, stop beating the dead horse", but then the fan started, good article, had a lot of fun reading it
- z0r 1y agoSomeone send this man a peer bonus
- t43562 1y agoCross compiling go is easy. Static binaries work everywhere. The cryptographic library is the foundation of various CAs like letsencrypt and is excellent. The green threads are very interesting since you can create 1000s of them at a low cost and that makes different designs possible. I think this complaining about defer is a bit trivial. The actual major problem for me is the way imports work. The fact that it knows about github and the way that it's difficult to replace a dependency there with some other one including a local one. The forced layout of files, cmd directories etc etc. I can live with it all but modules are the things which I have wasted the most time and struggled the most.
- hellcow 1y ago> The fact that it knows about github and the way that it's difficult to replace a dependency there with some other one including a local one. Use `replace` in `go.mod`, or `go.work` if you're hacking on it locally?
- mdaniel 1y agoor go ahead and commit it, if you're galaxy brain and want to throw off would-be attackers trying to understand your codebase https://github.com/pulumi/pulumi/blob/v3.191.0/pkg/go.mod#L5 https://github.com/pulumi/pulumi/blob/v3.191.0/pkg/go.mod#L5 or https://github.com/opentofu/terraform-provider-aws/blob/main/go.mod#L387 https://github.com/opentofu/terraform-provider-aws/blob/main...
- JetSetIlly 1y ago> The forced layout of files, cmd directories etc etc. You don't need to have a cmd directory. I see it a lot in Go projects but I'm not sure why.
- openasocket 1y agoI've worked almost exclusively on a large Golang project for over 5 years now and this definitely resonates with me. One component of that project is required to use as little memory as possible, and so much of my life has been spent hitting rough edges with Go on that front. We've hit so many issues where the garbage collector just doesn't clean things up quickly enough, or we get issues with heap fragmentation (because Go, in its infinite wisdom, decided not to have a compacting garbage collector) that we've had to try and avoid allocations entirely. Oh, and when we do have those issues, it's extremely difficult to debug. You can take heap profiles, but those only tell you about the live objects in the heap. They don't tell you about all of the garbage and all of the fragmentation. So diagnosing the issue becomes a matter of reading the tea leaves. For example, the heap profile says function X only allocated 1KB of memory, but it's called in a hot loop, so there's probably 20MB of garbage that this thing has generated that's invisible on the profile. We pre-allocate a bunch of static buffers and re-use them. But that leads to a ton of ownership issues, like the append footgun mentioned in the article. We've even had to re-implement portions of the standard library because they allocate. And I get that we have a non-standard use case, and most programmers don't need to be this anal about memory usage. But we do, and it would be really nice to not feel like we're fighting the language.
- nasretdinov 1y agoI've found that when you need this it's easier to move stuff offheap, although obviously that's not entirely trivial in a GC language, and it certainly creates a lot of rough edges. If you find yourself writing what's essentially, e.g. C++ or Rust in Go, then you probably should just rewrite that part in the respective language when you can :)
- arccy 1y agoI guess you'd be interested in the arena experiment, though it seems to be currently on pause
- theobeers 1y agoPerhaps the new "Green Tea" GC will help? It's described as "a parallel marking algorithm that, if not memory-centric, is at least memory-aware, in that it endeavors to process objects close to one another together." https://github.com/golang/go/issues/73581 https://github.com/golang/go/issues/73581
- m0llusk 1y agoNone of these objections seem at all serious to me, then the piece wraps up with "Why do I care about memory use? RAM is cheap." Excuse me? Memory bloat effects performance and user experience with every operation. Careful attention to software engineering should avoid or minimize these problems and emphasize the value of being tidy with memory use.
- SkiFire13 1y ago> Two types of nil What in the javascript is this.
- mdaniel 1y agoI get bitten by the "nil interface" problem if I'm not paying a lot of attention since golang makes a distinction between the "enclosing type" and the "receiver type" package main import "fmt" type Foo struct{ Name string } func (f *Foo) Kaboom() { fmt.Printf("hello from Kaboom, f=%s\n", f.Name) } func NewKaboom() interface{ Kaboom() } { var p *Foo = nil return p } func main() { obj := NewKaboom() fmt.Printf("obj == nil? %v\n", obj == nil) // The next line will panic (because method receives nil *Foo) obj.Kaboom() } go run fred.go obj == nil? false panic: runtime error: invalid memory address or nil pointer dereference
- Mawr 1y agoCongratulations, you have found a few pain points in a language. Now as a scientific exercise apply the same reasoning to a few others. Will the number of issues you find multiplied by their importance be greater or lower than the score for Go? There you go, that's the entire problem - Go is bad, but there is no viable alternative in general.
- ergonaught 1y agoFor the most part I've loved Go since just before 1.0 through today. Nits can surely be picked, but "it's still not good" is a strange take. I think there is little to no chance it can hold on to its central vision as the creators "age out" of the project, which will make the language worse (and render the tradeoffs pointless). I think allowing it to become pigeon holed as "a language for writing servers" has cost and will continue to cost important mindshare that instead jumps to Rust or remains in Python or etc. Maybe it's just fun, like harping on about how bad Visual Basic was, which was true but irrelevant, as the people who needed to do the things it did well got on with doing so.
- baby 1y agoSum types is the one big thing missing IMO, the language got a LOT of things right otherwise
- SkepticalWhale 1y agoGo has its fair share of flaws but I still think it hits a sweet spot that no other server side language provides. It’s faster than Node or Python, with a better type system than either. It’s got a much easier learning curve than Rust. It has a good stdlib and tooling. Simple syntax with usually only one way to do things. Error handling has its problems but I still prefer it over Node, where a catch clause might receive just about anything as an “error”. Am I missing a language that does this too or more? I’m not a Go fanatic at all, mostly written Node for backends in my career, but I’ve been exploring Go lately.
- rsyring 1y agoMaybe Nim. But it's not really caught on and the ecosystem is therefore relatively immature.
- ecshafer 1y ago> It’s faster than Node or Python, with a better type system than either. It’s got a much easier learning curve than Rust. It has a good stdlib and tooling. Simple syntax with usually only one way to do things. Error handling has its problems but I still prefer it over Node, where a catch clause might receive just about anything as an “error”. I feel like I could write this same paragraph about Java or C#.
- acedTrex 1y agoJava and C# are both languages with A LOT more features and things to learn. Go someone can pick 80% of the language up in a single day.
- gadflyinyoureye 1y agoAnd they will trip over the remains 20% percent for the rest of their days.
- bob1029 1y agoJust because you can learn about something doesn't mean you need to. C# now offers top-level programs that are indistinguishable from python scripts at a quick glance. No namespaces, classes or main methods are required. Just the code you want to execute and one simple file. https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/top-level-statements https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals...
- ironmagma 1y agoAnother annoying thing Go proponents say is that it is simple. It is not. And even if it was, the code you write with a simple language is not automatically simple. Take the k8s control plane for example; some of the most convoluted and bulky code that exists, and it’s all in Go.
- divan 1y agoIn 2015 I wrote an article "How to complain about Go" to mock this type of articles that completely miss the big picture and the real world impact of "imperfect" language. Glad it's still relevant :)
- aurumque 1y agoThis has always been my takeaway with Go. An imperfect language for imperfect developers, chosen for organizations (not people) to ensure a baseline usefulness of their engineers from junior to senior. Do I like it? No. Would I ever choose it willingly? No. But when the options at the time were Javascript or untyped Python, it may have seemed like a more attractive option. Python was also dealing with a nasty 2-to-3 upgrade at the time that looks foolish in comparison to Golang's automatic formatting and upgrade mechanisms.
- divan 1y ago> An imperfect language for imperfect developers There is a crack in everything, that's how the light gets in.
- tapirl 1y agoGo indeed has its problems. But the ones described in this article just prove the author is a Go newbie.
- laserlight 1y agoIn a comment in this thread, the author states that they have 12 - 15 years of experience in Go [0]. [0] https://news.ycombinator.com/item?id=44985378 https://news.ycombinator.com/item?id=44985378
- tapirl 1y agobut still a Go newbie? The article's points feel overly simplistic/shallow and lack the depth you'd expect from an experienced Go programmer.
- thomashabets2 1y agoI won't be dumping credentials, but perhaps the newbie is you? I also review a lot of Go code. I see these problems all the time from other people.
- rustandmore 1y agoMost of non-opinion parts of this article are "Go sucks because <insert newbie mistake>" or otherwise factually incorrect points. At that point, all languages suck because there's newbie mistakes and I don't understand them. Maybe it is time to dump credentials rather than being obscure. I don't believe it for a moment that you have decade+ experience in Go.
- runjake 1y agoEvery language has its flaws. I respect Go for staying relatively simple. And it has decent concurrency (for my needs). These days, it seems like languages keep chasing paradigms and over adapt to moving targets. Look at what Rust and Swift have become. C# has stayed relatively sane somehow, but it's not what I'd call indepedent.
- Insanity 1y agoI wrote a book on Go, so I'm biased. But when I started using Go more than a decado ago, it really felt like a breath of fresh air. It made coding _fun_ again, less boilerplate heavy than Java, simple enough to pick up, and performance was generally good. There's no single 'best language', and it depends on what your use-cases are. But I'd say that for many typical backend tasks, Go is a choice you won't really regret, even if you have some gripes with the language.
- munificent 1y agoOften, when I have some home DIY or woodworking problem, I reach for my trusty Dremel: * The Dremel is approachable: I don't have to worry about cutting off my hand with the jigsaw or set up a jig with the circular saw. I don't have to haul my workpiece out to the garage. * The Dremel is simple: One slider for speed. Apply spinny bit to workpiece. * The Dremel is fun: It fits comfortably in my hand. It's not super loud. I don't worry about hurting myself with it. It very satisfyingly shaves bits of stuff off things. In so many respects, the Dremel is a great tool. But 90% of the time when I use it, it ends up taking my five times as long (but an enjoyable 5x!) and the end result is a wobbly scratchy mess. I curse myself for not spending the upfront willpower to use the right tool for the job. I find myself doing this with all sorts of real and software tools: Over-optimizing for fun and ease-of-entry and forgetting the value of the end result and using the proper tool for the job. I think of this as the "Dremel effect" and I try to be mindful of it when selecting tools.
- Insanity 1y agoThat's a fun analogy. Most of my coding these days is definitely in the 'for fun' bucket given my current role. So I'd rather take 5x and have fun. That said, I don't think Go is only fun, I think it's also a viable option for many backend projects where you'd traditionally have reached for Java / C#. And IMO, it sure beats the recent tendency of having JS/Python powering backend microservices.
- munificent 1y agoAgreed that if Go is a Dremel (I'm not sure whether or not it is) then JS is, like, a rusty non-locking pocket knife.
- 827a 1y agoRecently I was in a meeting where we were considering adopting Go more widely for our backend services, but a couple of the architect level guys brought up the two-types-of-nil issue and ultimately shot it down. I feel like they were being a little dramatic about it, but it is startling to me that its 2025 and the team still has not fixed it. If the only thing you value in language design is never breaking existing code, even if by any definition that existing code is already broken, eventually the only thing using your language will be existing code.
- dilap 1y agoThis has already been explained many times, but it's so much fun I'll do it again. :-) So: The way Go presents it is confusing, but this behavior makes sense, is correct, will never be changed, and is undoubtedly depended on by correct programs. The confusing thing for people use to C++ or C# or Java or Python or most other languages is that in Go nil is a perfectly valid pointer receiver for a method to have. The method resolution lookup happens statically at compile time, and as long as the method doesn't try to deref the pointer, all good. It still works if you assign to an interface. package main import "fmt" type Dog struct {} type Cat struct {} type Animal interface { MakeNoise() } func (*Dog) MakeNoise() { fmt.Println("bark") } func (*Cat) MakeNoise() { fmt.Println("meow") } func main() { var d *Dog = nil var c *Cat = nil var i Animal = d var j Animal = c d.MakeNoise() c.MakeNoise() i.MakeNoise() j.MakeNoise() } This will print bark meow bark meow But the interface method lookup can't happen at compile time. So the interface value is actually a pair -- the pointer to the type, and the instance value. The type is not nil, hence the interface value is something like (&Cat,nil) and (&Dog,nil) in each case, which is not the interface zero value, which is (nil, nil). But it's super confusing because Go type cooerces a nil struct value to a non-nil (&type, nil) interface value. There's probably some naming or syntax way to make this clearer. But the behavior is completely reasonable.
- dilap 1y ago(Side note, Go did fix scoping of captured variables in for,range loops, which was a back-incompat change, but they justified it by emperically showing it fixed more bugs than it caused (very reasonable). C# made the same change w/ the same justification earlier, which was inspiration for Go.)
- arewethereyeta 1y agothere are plenty of other languages. I dont get this love-hate type of speech like golang itself owes you an apology.
- 4ndrewl 1y agoOk, it's not a good fit for you. Don't use it I guess and ignore all the X is not good posts for language X you do decide to use?
- hoppp 1y agoGo is the best language for me because I develop fast with it, don't have that many bugs, it builds fast and I'm usually just fine having a garbage collector The dependency management is great too Go is a super productive powerhouse for me.
- golly_ned 1y agoOf all the languages one could accuse of being hermetically designed in an ivory tower, Go would be the second-least likely.
- reactordev 1y agoThere were points in this article that made me feel like Rob Schneider in Demolition Man saying "He doesn't know about the three sea shells!" but there were a couple points made that were valid. the nil issue. An interface, when assigned a struct, is no longer nil even if that struct is nil - probably a mistake. Valid point. append in a func. Definitely one of the biggest issues is that slices are by ref. They did this to save memory and speed but the append issue becomes a monster unless abstracted. Valid point. err in scope for the whole func. You defined it, of course it is. Better to reuse a generic var than constantly instantiate another. The lack of try catch forces you to think. Not a valid point. defer. What is the difference between a scope block and a function block? I'll wait.
- moogly 1y agoWould have been more interested in a rebuttal of the claims you consider invalid instead of an overly verbose +1 that added nothing.
- zac23or 1y agoGreat article! I like Go and Rust, but sometimes I feel like they lack tools that other languages have just because they WANT to be different, without any real benefit. Whenever I read Go code, I see a lot more error handling code than usual because the language doesn't have exceptions... And sometimes Go/Rust code is more complex because it also lacks some OOP tools, and there are no tools to replace them. So, Go/Rust has a lot more boilerplate code than I would expect from modern languages. For example, in Delphi, an interface can be implemented by a property: type TMyClass = class(TInterfacedObject, IMyInterface) private FMyInterfaceImpl: TMyInterfaceImplementation; // A field containing the actual implementation public constructor Create; destructor Destroy; override; property MyInterface: IMyInterface read FMyInterfaceImpl implements IMyInterface; end; This isn't possible in Go/Rust. And the Go documentation I read strongly recommended using Composition, without good tools for that. This "new way is the best way, period ignore good things of the past" is common. When MySQL didn't have transactions, the documentation said "perform operations atomically" without saying exactly how. MongoDB didn't have transactions until version 4.0. They said it wasn't important. When Go didn't have generics, there were a bunch of "patterns" to replace generics... which in practice did not replace. The lack of inheritance in Go/Rust leaves me with the same impression. The new patterns do not replace the inheritance or other tools. "We don't have this tool in the language because people used it wrong in the old languages." Don't worry, people will use the new tools wrong too!
- eptcyka 1y agoGo allows deferring an implementation of an interface to a member of a type. It is somewhat unintuitive, and I think the field has to be an unnamed one. Similarly, if a field implements a trait in Rust, you can expose it via `AsRef` and `AsMutRef`, just return a reference to it. These are not ideal tools, and I find the Go solution rather unintuitive, but they solve the problems that I would've solved with inheritance in other languages. I rarely use them.
- zac23or 1y agoThanks. I had been searching for this for a project in the past and couldn't find it in Go or Rust. Before posting, I asked chatgpt, and he said it wasn't possible...
- sethammons 1y agoI agree with just about everything in the post. I've been bit a time or two by the "two flavors of null." That said, my most pleasant and most productive code bases I've worked in have all been Go. Some learnings. Don't pass sections of your slices to things that mutate them. Anonymous functions need recovers. Know how all goroutines return.
- kstenerud 1y agoI used go for years, and while it's able to get small things up and running quickly, bigger projects soon become death-by-a-thousand-cuts. Debugging is a nightmare because it refuses to even compile if you have unused X (which you always will have when you're debugging and testing "What happens if I comment out this bit?"). The bureaucracy is annoying. The magic filenames are annoying. The magic field names are annoying. The secret hidden panics in the standard library are annoying. The secret behind-your-back heap copies are annoying (and SLOW). All the magic in go eventually becomes annoying, because usually it's a naively repurposed thing (where they depend on something that was designed for a different purpose under different assumptions, but naively decided to depend on its side effects for their own ever-so-slightly-incompatible machinery - like special file names, and capitalization even though not all characters have such a thing .. was it REALLY such a chore to type "pub" for things you wanted exposed?). Now that AI has gotten good, I'm rather enjoying Rust because I can just quickly ask the AI why my types don't match or a gnarly mutable borrow is happening - rather than spending hours poring over documentation and SO questions.
- chippiewill 1y agoI haven't done serious Rust development since AI got good, but I did have a brief play last December and it's shocking how good they are at Rust. It feels like the verbose syntax and having tons of explicit information everywhere just makes it breeze through problems that would trip up a human for ages.
- AtNightWeCode 1y agoI once described this "debugging" problem to one of the creators and he did not even understand the problem. It is so amateurish you wonder if they ever dipped a toe outside the academic world. Btw, AI sucks on GO. One would have guessed that such a simple lang would suit ChatGPT. Turns out ChatGPT is much better at Java, C#, Pyhton and many other langs than GO.
- hnlmorg 1y agoI’ve had no worse success with AI and Go I have for any other language (JavaScript, Python, Terraform, Swift). I’d say Terraform was the worst. But that shouldn’t be a surprise given it’s niche
- Night_Thastus 1y agoFascinating. Coming from C++ I can't imagine not having RAII. That seems so wordy and painful. And that nil comparison is...gross. I don't get how you can assign an interface to be a pointer to a structure. How does that work? That seems like a compile error. I don't know much about Go interfaces.
- zzzeek 1y agoGo is the kind of language you use at your job, and you necessarily need to have dozens of linters and automated code quality checks set up to catch all the gotchas, like the stuff with "err" here, and nobody is ever going to get any joy from any of it. the entire exercise of "you must return and consume an error code from all functions" has been ridiculous from go's inception, it looked ridiculous to me back when I saw it in 2009, and now that I have to use it for k8s stuff at work, it's exactly as ridiculous as it seemed back then. With all of that, Go becomes the perfect language for the age of LLMs writing all the code. Let the LLMs deal with all the boilerplate and misery of Go, while at the same time its total lack of elegance is also well suited to LLMs which similarly have the most dim notions of code elegance.
- Shawnecy 1y agoFor too many aspects for my liking, Go moves complexity out of the language and into your code where you get to unavoidably deal with the cognitive load. It's fine if you can keep things small and simple, but beyond a certain complexity, it's a hard pass for me.
- 0xbadcafebee 1y agoWhat's popular and what's good are rarely (if ever) the same thing. Python sucks balls but it has more fanboys than a K-pop idol. Enforced whitespace? No strong typing? A global interpreter lock? Garbage error messages? A package repository you can't search (only partially because it's full of trash packages and bad forks) with random names and no naming convention? A complete lack of standardization in installing or setting up applications/environments? 100 lines to run a command and read the stout and stderr in real time? The real reason everyone uses Python and Go is because Google used them. Otherwise Python looks like BASIC with objects and Go is an overhyped niche language.
- api 1y agoGo passes on a lot of ideas that are popular in academic language theory and design, with mixed but I think mostly positive results for its typical use cases. Its main virtues are low cognitive load and encouraging simple straightforward ways of doing things, with the latter feeding into the former. Languages with sophisticated powerful type systems and other features are superior in a lot of ways, but in the hands of most developers they are excuses to massively over-complicate everything. Sophomore developers (not junior but not yet senior) love complexity and will use any chance to add as much of it as they can, either to show off how smart they are, to explore, or to try to implement things they think they need but actually don't. Go somewhat discourages this, though devs will still find a way of course. Experienced developers know that complexity is evil and simplicity is actually the sign of intelligence and skill. A language with advanced features is there to make it easier and simpler to express difficult concepts, not to make it more difficult and complex to express simple concepts. Every language feature should not always be used.
- danenania 1y agoOh yeah. Said another way, it discourages nerd-sniping, which in practice is a huge problem with functional programming and highly expressive type systems. You end up creating these elegant abstractions that are very seductive from a programmer-as-artist perspective, but usually a distraction from just getting the work done in a good enough way. You can tell that the creators of Go are very familiar with engineer psychology and what gets them off track. Go takes away all shiny toys.
- ttz 1y agoevery language has its problems; Go I think is pretty good despite them. not saying points raised in the article are invalid, you def have to be careful, and I hate the "nil interface is not necessarily nil" issue as much as anyone. It's hard to find a language that will satisfy everyone's needs. Go I find better for smaller, focused applications/utilities... can definitely see how it would cause problems at an "enterprise" level codebase.
- fullstackchris 1y agoall these lame folks complaining about "what go could have been"... is this not HACKER news? cant you go and build your own, "better" language? but you won't, you'll just complain
- skywhopper 1y agoGod, this sort of article is so boring. Go is a great language, as evidenced by the tremendous amount of excellent software that’s been written in it. Are there some rough points? Sure. But that’s all this is, is a list of annoyances the author experiences when using Go. Great, write an article about the problems with Go, but don’t say “therefore it’s a bad language”. While I agree with many of the points brought up, none of them seems like such a huge issue that it’s even worth discussing, honestly. So you have different taste the the original designers. Who cares? What language do you say is better? I can find just as many problems with that language. Also, defer is great.
- moomoo11 1y agoThis reads like your generic why js sucks why c sucks why py sucks etc. Use the language where it makes sense. You do know what if you have an issue that the language fails at, you can solve that particular problem in another language and.. call that code? We used to have a node ts service. We had some computationally heavy stuff, we moved that one part to Go because it was good for that ONE thing. I think later someone ported that one thing to Rust and it became a standalone project. Idk. It’s just code. Nobody really cares, we use these tools to solve problems.
- umutdev 1y agogo error handling bla bla shitpost
- kragen 1y agoThere are a couple of minor errors in this post, but mostly it consists of someone getting extremely overexcited about minor problems in Golang they have correctly identified.
- thomashabets2 1y agoAuthor here. I may not be able to deny the second part, but I would love to hear anything you think is factually incorrect or that I may have been unclear about. Always happy to be corrected. (something that's not a minor error, that someone else pointed out, is that Python isn't strictly refcounted. Yeah, that's why emphasized "almost" and "pretty much". I can't do anything about that kind of critique)
- kragen 1y agoOh, I meant that you were mistaken about handling nom-UTF-8 filenames (see https://news.ycombinator.com/item?id=44986040 https://news.ycombinator.com/item?id=44986040) and in 90% of cases a deferred mutex unlock makes things worse instead of better. The kind of general reason you need a mutex is that you are mutating some data structure from one valid state to another, but in between, it's in an inconsistent state. If other code sees that inconsistent state, it might crash or otherwise misbehave. So you acquire the mutex beforehand and release it once it's in the new valid state. But what happens if you panic during the modification? The data structure might still be in an inconsistent state! But now it's unlocked! So other threads that use the inconsistent data will misbehave, and now you have a very tricky bug to fix. This doesn't always apply. Maybe the mutex is guarding a more systemic consistency condition, like "the number in this variable is the number of messages we have received", and nothing will ever crash or otherwise malfunction if some counts are lost. Maybe it's really just providing a memory fence guarding against a torn read. Maybe the mutex is just guarding a compare-and-swap operation written out as a compare followed by a swap. But in cases like these I question whether you can really panic with the mutex held! This is why Java deprecated Thread.stop. (But Java does implicitly unlock mutexes when unwinding during exception handling, and that does cause bugs.) This is only vaguely relevant to your topic of whether Golang is good or not. Explicit error handling arguably improves your chances of noticing the possibility of an error arising with the mutex held, and therefore handling it correctly, but you correctly pointed out that because Go does have exceptions, you still have to worry about it. And cases like these tend to be fiendishly hard to test—maybe it's literally impossible for a test to make the code you're calling panic.
- sunnyps 1y agoWould the interface nil example be clearer if checking for `nil` didn't use the `==` operator? For example, with a hypothetical `is` operator: package main import "fmt" type I interface{} type S struct{} func main() { var i I var s *S fmt.Println(s, i) // nil nil fmt.Println(s is nil, i is nil, s == i) // t,t,f: Not confusing anymore? i = s fmt.Println(s, i) // nil nil fmt.Println(s is nil, i is nil, s == i) // t,f,t: Still not confusing? } Of course, this means you have to precisely define the semantics of `is` and `==`: - `is` for interfaces checks both value and interface type. - `==` for interfaces uses only the value and not the interface type. - For structs/value, `is` and `==` are obvious since there's only a value to check.
- bitdeep 1y ago- I’ve seen a lot of debate here comparing Go’s issues (like nil handling or error scoping) to Rust’s strengths. - As someone who’s worked with C/C++ and Fortran, I think all these languages have their own challenges—Go’s simplicity trades off against Rust’s safety guarantees, for example. - Could someone share a real-world example where Go’s design caused a production issue that Rust or another language would’ve avoided? - I’m curious how these trade-offs play out in practice. Sorry, I don't do Go/Rust coding, still on C/C++/Fotran.
- metaltyphoon 1y ago> Go’s design caused a production issue A simple one, if you create two separate library in Go and try to link with an application, you will have a terrible time. I've ran into this same issue: https://github.com/golang/go/issues/65050 https://github.com/golang/go/issues/65050 https://www.youtube.com/watch?v=xuv9A7CJF54&t=440s https://www.youtube.com/watch?v=xuv9A7CJF54&t=440s
- pensatoio 1y ago> caused a production issue that's not a production issue, and it's a very niche use case
- metaltyphoon 1y agoIt's a niche use case to have software that load plugins and it just so happens those plugins are written in Go? No it's not a niche case. If all programing you do in Go is web servers than sure you won't see this.
- samdoesnothing 1y agoAgreed. It's great.
- zazazx 1y agoBeen using Go for two years now, coming from C. Totally fair points. Go’s quirks can feel more like landmines than design decisions, especially when coming from languages that handle things like RAII, error scope, or nil with more grace. But part of Go’s charm (and curse) is its unapologetic minimalism. It’s not trying to be elegant, just predictable and maintainable at scale. Saying “no sane person” would choose X might feel cathartic, but it shuts down understanding of why rational teams do choose Go and often thrive with it. Go’s not for everyone, but it does what it does on purpose.
- nirui 1y agoThe chosen example: bar, err := foo() if err != nil { return err } if err = foo2(); err != nil { return err } Sounded more like a nitpicking. If you really care about scope while being able to use `bar` later down, the code should be written as: bar, err := foo() if err != nil { return err } err = foo2() // Just reuse `err` plainly if err != nil { return err } which actually overwrites `err`, opposite to "shadowing" it. The confusing here, is that the difference between `if err != nil` and `if err = call(); err != nil` is not just style, the later one also introduces a scope that captures whatever variables got created before `;`. If you really REALLY want to use the same `if` style, try: if bar, err := foo(); err != nil { return err } else if bar2, err := foo2(); err != nil { return err } else { [Use `bar` and `bar2` here] return ... }
- leecommamichael 1y agoThe Odin Programming Language has meaningful responses to all concerns. The design of the language is impeccable.
- tom_m 1y agoIt's incredibly good.
- tom_m 1y agoNot only is it really good, it's even better for AI.
- enigma101 1y agoThen don't use it yiks. Everyone got an opinion these days.
- novoreorx 1y agoI would love to see a Go fork that addresses some of the problems mentioned in this article. I will contribute a name for this project: "mygo"
- deleted 1y ago[deleted]
- arwhatever 1y agowat
- neop1x 1y agoThis is OP's second article of hating Go. In the meantime he also wrote about Rust: "I’ve not found anything frustrating or stupid in Rust yet." So he can just use Rust and stop using Go completely. Problem solved. No need to write those hates. Go lang is cleary not good for him and it's ok.