I've written a bit of code in Go, and the problem I have with it is primarily it feels really outdated for a "modern" language - every time I use it I feel like I'm dealing with something written by someone who really hated Java in 2005. There are features that could be added to the language that would make it more readable and less error-prone without compromising the simplicity of the core language. Generics are the famous example, but the one that really gets me is the the lack of nullable type signatures. This is a great way to avoid an entire class of bugs that nearly every modern language I've used has evolved a solution for except Go.
Another issue I have is the reliance on reflection. In general, I think if you have to rely on reflection to do something, that usually means you're working around some inherent limitation in the normal language - and the resulting code is often far less readable than the code would be in a more expressive language. Lots of Go libraries and frameworks are forced to use it in a lot of cases because there's just no other way to express some really basic things without it.
I really want to like Go. There's a lot I like - the "only one way to do something" approach means that code always feels consistent. Errors as values is a far superior approach to exceptions. I had to write some for a job interview project a while back and it felt really refreshing, but every time I try to use it for a personal project, I don't feel like I'm getting anything out of it that I couldn't get out of say, Rust, or modern, typed Python.
If you’re the type of engineer who prides themselves on the raw amount of code you write, then Go is for you. If you’d rather focus on solving problems creatively and expressively, Go is not your tool.
I don't mean this as slight against those people that really enjoy writing lots of (Go) code. It’s just my observation after being in a few different contexts where Go was the language of choice. Personally Go is too verbose for me and this is especially painful/apparent when you get to writing the multitude of tests required to ensure your code works since Go’s type system/compiler doesn't lend you much in the way of helping ensure code correctness.
>If you’d rather focus on solving problems creatively and expressively, Go is not your tool.
>It’s just my observation
Well, my observation after dealing with js and ruby projects (ruby was my main language a few years ago) - those creating and expressive languages lead to fewer lines, sure, but in the end you are dealing with a pile of crap nobody want's to touch because some of the developers were so creative and wanted to express themselves more than they wanted to solve the problem.
Go is a tool if you actually want to solve the problem and make sure people after can quickly understand how and why you solved it. Rather than stand in awe of your creation.
This feels largely like a mischaracterization, and does not align with my experience. I'd change it to say, if you are focused on solving the problems creatively rather than using the language creatively, Go is a viable option. But if you're interested in creative usage of the language itself, Go is not right for you.
Please omit the last line. Go is actually far more verbose than modern java when you are comparing anything bigger than a single snippet of code. Modern functional Java is fairly concise. Go is not - though maybe this will change with generics and functional packages after Go 2.0.
>> the problem I have with it is primarily it feels really outdated for a "modern" language
It's mostly not about the language. An exception is of course when moving from a dynamically typed interpreted language to a statically typed compiled language.
The success of projects depends much more on other things than the programming language. It's about
- Processes and standards, like following a well defined structure, testing, documentation, ...
- Maintainability of code. It must be easy to read, to understand syntactically and to build a mental model of the code
- Long term reliability and stability of the eco system
- Easy and reliable tooling
- Developer efficiency, e.g. compile times
Go shines in many of the aspects. Especially in maturity, stability, amazing standard lib and tooling. As you mention Rust: This is exactly where Rust falls short. Rust is a great language with amazing people behind it. But there are reasons why its adoption in the broad real world is very, very small. The reasons are not the language. So I always feel it's a bit invasive when Rust promoters enter Go or Java threads by telling how much better Rust as a language is.
In this example Go has additional benefits being statically typed and compiled, very fast and with build in concurrency support.
Go isn't for programmers, it's for managers. Because of the simplicity of the language, it's easy to get people onboarded with, and it's fairly difficult for a single team member to go astray and move the codebase in a direction which will be unmaintainable. It's a kind of lowest-common-denomenator language which is easy to read, debug, and maintain, even for a mediocre programmer.
That said, I think you are right about explicit nullability. A language like Go with this feature, as well as named arguments and limited ADT's could be very compelling for Go's use case.
And yet it's a top-5 most-loved language among programmers and there's no real evidence that it's popular among managers (discussed in detail here: https://news.ycombinator.com/item?id=27175297). Moreover, lots of open-source software exists in Go--if only managers like the language, what's the theory for why all of this software (including major software, like Kubernetes) exists? Is it managers building it in their spare time? Even then, those managers are acting as programmers (not managers) in that capacity.
With respect to the implication that only managers care about the pragmatic aspects of software development (collaboration, onboarding, readability, debugability, maintainability, etc), where did this meme come from? I get the meme that there are many programmers who value abstraction above all else, but I'm not familiar with the meme that all programmers are abstraction maximalists.
I completely disagree with this statement. Some of the most high-performance modern platform-level code written is currently written in Go.
It was a Google presentation that examined their efforts to convert their download site to Go (from C/C++) that got my attention. It's easier to read, has a simpler mental model, and faster than its C/C++ cousin.
Doesn't Java fit the same bill? (although it was kinda modern at the time)
> I think you are right about explicit nullability
Sadly it's really hard to shoe-horn this onto Go, now that the std lib is widely used. Same for proper sum types (which can then be used for multiple return values).
But the continuation of the nullability mistake puzzles me the most... And that wile Go's be designed by big name lang gurus: what were they thinking?
> Errors as values is a far superior approach to exceptions.
Why is that? I have never seen a cogent explanation for why this is the case.
I can tell you why exceptions (as implemented in Java) are cool: You can write code as if every function call is successful, as opposed to adding a line (or more) of error handling code after every function call, which makes it harder to follow the logic.
I write Java, but I prefer errors as values exactly because you can't only consider the happy path. It really makes you think about the appropriate response to this specific failure. You can do that with exceptions, but in practice it's exactly like you say: all error handling is delegated to some generic catch-all, which in a web app usually just gives a generic 500 Internal Server Error.
If I encode my errors as values (usually with Either), I have to decide how to gracefully fall back if a failure occurs before I'm even allowed to use the successful result. Maybe I just hide the part of the view that needed that data. Maybe I show an appropriate error message. Maybe I email operations to let them know about the problem. Whatever I do, I have to actually think about it and not just assume that the error will be caught somewhere by someone. The result is usually a dramatically improved user experience when failures inevitably occur.
Exceptions tend to pass the buck so far down the line that there's no context to make an appropriate decision. Values tend to force a decision early, when you still have enough context. (Obviously both can be used against the grain, but the question is which pattern is easier.)
Look at any piece of Java code and try to guess the cyclomatic complexity of it. It's not simple. Because every line, and every function call can fail. And that failure is one more place where the execution tree of your code branches. You can't see it unless you check every method call in your code.
In Go -- every error is obvious, and you get a sense of a cyclomatic complexity of any piece of code by just going through it quickly.
So, that's it for me. The complexity of the code is visible.
I think it's a little bit complicated to explain but mostly it boils down to this: errors are real. Java methods kind of let you ignore them through declaring exceptions, with this idea that, well, somebody else will deal with it. Golang functions make errors feel more present. They force you to think about how you're going to handle the errors up front, and to question whether or not you even should error in a particular instance. It's actually helped change the way I think about functions. There are many behaviors that in Java I would not have even considered making them idempotent but in golang making them idempotent is both easier and, as it turns out, more robust.
The patterns for error handling the golang have introduced are admittedly verbose, but they do lend a certain element of confidence that once the code is written, the errors should be handled. Of course a programmer can ignore the errors explicitly but doing so is different than forgetting to catch a thrown exception, because the programmer must go out of their way to write code ignoring the error. It feels like there's more agency around the decision.
the fundamental tenet in Go is that every error should be handled. Here is an excerpt from Dave Cheney that clarifies this:
"For the truly exceptional cases, the ones that represent either unrecoverable programming mistakes, like index out of bounds, or unrecoverable environmental problem, like running out of stack, we have panic.
For all of the remaining cases, any error conditions that you will encounter in a Go program, are by definition not exceptional — you expect them because regardless of returning a boolean, an error, or panicing, it is the result of a test in your code"
I'm working with some Python that follows this error as value convention and it's awful. The code likes to catch exceptions as soon as possible and replace them with strings like "not found". Exceptions are useful things, they tell me what broke and where. Instead I get a "not found" error value, and find out that happened because the endpoint called a service that returned a "item not found" error value, and then a few layers of obscure string error values later I find out some logic did division by zero or something.
In other words, I also don't understand the hype around error values.
So, back to Python, what error value is suitable for replacing an exception? I mean, Python is flexible enough to do Go-style error values, but what does a good error value look like? Maybe I just haven't seen good error values?
If an error happens way down in the weeds and I initially return a very specific error value, do I just pass it up the stack forever? If so, how is that different than traditional exceptions? Do I replace the specific error value with more general error values as I move up the stack to higher-level code? If so, then I am throwing valuable information away and am likely to end up with something like "not found" as my error value at a the top -- not very useful.
> I've written a bit of code in Go, and the problem I have with it is primarily it feels really outdated for a "modern" language
Go is a language to get the job done. It's for the masses. It's this type of language you write a blog post on how you have done this and that and why instead of a scientific paper. It's really the boring mortar.
Exactly this. Go is the language you use when you need to get shit done, reasonably performant and easily distributed to different operating systems.
The language actively hinders any attempts to be fancy or "expressive". You write (or copy/paste) the same pattern(s) over and over again.
On the other hand, you can pick up pretty much anyone's Go codebase and quickly see how it works. No need to figure out what flavour of metaprogramming or other fancy crap the original author was a fan of at the time of writing.
It's boring, it pays the bills. You can be expressive with other languages on your own time.
> Errors as values is a far superior approach to exceptions
No, it's not. Unhandled errors that aren't noticed just lead to undefined/nondeterministic behavior and some really nasty bugs. And moreover, I literally cannot understand how it is possible for any programmer to not realize this (compare: elixir's approach, which instead embraces errors and restarts processes instantly).
It feels like Option types could be done quite easily - basically slices with a hardcoded capacity of 1. Could compile down to a pointer type. But it would make linting way easier. Of course, the real power of Option comes from Map.
Give Go a map and option and I'll be a happy gopher.
At that point why not use something F# or Rust? Both can provide comparable or better runtimes, very decent ecosystems, and they are already much better languages.
Yeah, I have mixed feelings about it as well. There are some good things, but there are also ugly warts.
For example if you try to use some numeric types that are not int, int32, float64 (for example float32 or uint16) you'll be in a lot of pain due to constant casting back and forth. Yes generics could solve this one too.
I also couldn't make myself enjoy programming in it. I think it's because it tried to be simple, so anyone can learn it. Because of it is kind of rare to say that you created with a creative way of solving specific problem.
That's very different to for example C, to which Go (at least early on) was compared to.
One of the reasons I love Go is that it's just so easy to read Go code written by other people on my team or third party libraries. There have been tons of cases where I'm working with another library and I just step into their code in my editor to understand how something works. And the code is always so easy to follow.
It's very rare that I come across weird patterns or someone trying to be very clever. Its always straightforward code.
This is one of its biggest advantages to me as well. I have had to support many production systems, written in varieties of languages, over my tenure and in my experiences the systems that live the longest are those that are the most readable by new team members; people transition in and out and its important for them to learn quickly. And while a person's language experience matters to some degree, it is more the underlying domain that takes the most time. Languages that simplify on the number of solution patterns and focus on key ones being "idiomatic" helps to lower the mental load someone has to overcome to learning that domain. If you know C, Java, JS, etc. you can pretty quickly figure out how to read Go, and that matters.
Is Go perfect at this? No. I too would love to see some higher level functions exist for to help reduce boilerplate. For example, this proposal: https://github.com/golang/go/issues/45955 to add Filter, Map, etc. to slices. That seems like a practical set of functions to add to minimize boilerplate while at the same time not breaking away from simple idioms.
This has been my experience. I love the fact that the standard library itself is written in Go, so it's easy to see what exactly is going on and even learn some idioms that way. It's refreshing for me personally as I had a JavaScript background before learning Go
Haha this must be ironic or Stockholm syndrome. With duck typing and channels, missing basic modern language constructions... it is definitely not known for readability.
It would be kind of a fun experiment to require a rough estimate of LoC written in a given language be included in feedback about that language. These comments are kind of a cesspool of poorly written feedback that wraps ambiguous complaints about "readability", "maintenance" and even the silly take of "Go is a language for engineering managers", whatever that means. I suspect most of the negative comments on any language come from people that have spent practically no time writing it, and thus are inherently uninformed.
Of course, if you hate a language, you're not going to write lots of code in it (unless you have to, and then I would expect your feedback to be pretty negative but at least informed). Feedback from beginners/programmers external to a language is super important for the success of that language of course, but lots of debates about things like programming language are cluttered with feedback that isn't really made in good faith. Someone that doesn't like static typing is going to leave feedback about Go that may not directly say "I just don't like static typing", but ultimately is as simple to reconcile (and thus, should mostly be discarded).
I have written hundreds of thousands of lines of plain old C, as well hundreds of thousands of lines of Go over a career spanning three decades. I've used Go in earnest since 2013.
I would pick Go for any large project in a heartbeat.
I have to concur that the haters who post to HN probably have never used this language in earnest.
Here's what you get out of the box:
* Extensive standard library
* Cheap concurrency
* Fast compilation
* Built-in test/benchmark framework, including coverage and race detection
* Built-in heap and CPU profiling
Of all the complaints the one I understand the least is the reservations about performance. It is within 10-20% of C, and callouts to C and assembly are possible. (My 8-way multiplexed MD5 routine was written in AVX assembler and lives on in minio.) Extracting that last 10-20% is two or three sigmas away from the norm of how programming languages are used these days.
The objection to generics is similar. The lack of generics shows up -- once in a long while. It doesn't prevent me from being immensely productive with this language.
Looking back at the last few years I've wondered if I could have accomplished what I did in Go by using a different language (C in particular) and the answer, for me, is "not a chance".
I recently spearheaded the creation of a new Go service at my company. If it was Node or Python I could easily imagine a team getting sucked into weeks of bikeshedding the API framework, the test framework, linting tools, formatting tools, the TLS stack, research spikes on packaging and distribution, etc.
With Go, all of these problems were basically solved by the strength of the standard library. We could not be happier.
I used to use Java because it was fast enough, have good libraries and was productive enough. Go is almost as fast, the standard library has most of what you need and its ability to compile to a single binary makes deployments simple.
I also look back and think, could I have done all that in Java? Probably... but I also feel it would have required more effort.
The problem with not having generics is the systems you build instead. They either throw away type safety and use reflection, which causes hard to debug issues, or they lean on code generation, which is slow and hard to debug.
I agree that the lack of generics only shows up once in a while and doesn't hamper productivity as much as might be expected, but I'm still very excited to see them added to the language.
Sincere question out of curiosity, since I have not been exposed to Go at all.
How do container types, like arrays or hashmaps work without generics? Do you basically have to declare and implement types/methods for each different contained type?
I've also been mostly using Go over the last 6-8 years. Before that it's mostly been C++, pretty "modern" towards the end.
Go would definitely be my preferred pick for distributed/server applications even though the previous large (mixed C and C++) project I was on was a large distributed application and we did just fine. In fact I'd say our productivity and quality in C++ in that other team was just as good as any I've seen (which is not a random/double blind sort of thing, who knows if that project started in Go from day 1).
I would say that in a large project the language choice has some impact but there are many other variables. A well managed C++ project with competent developers can work well. A less than well managed with so-so developers in Go can be disaster zone. There are lots of other moving pieces, testing, build automation, CI/CD etc.
There are certain areas where I would prefer not to use Go. Basically areas where you spend a lot of time up or down the abstractions. I would prefer not to implement a video codec or a signal processing system in Go. Firmware, motion control etc. would also probably not be a good fit. Going the other direction there are things I can do really quickly in Python (lessay grab some data from some services, do some manipulation, draw some graphs), generally smaller utility like pieces where I want to use data with various random types, use third party libraries. I wouldn't want to write a large project in Python because it's slow and is dynamically typed.
I would also beg to differ wrt/ 10%-20% performance difference. You tend to rely a lot more on GC and produce more "garbage" in Go and the optimizer isn't as good vs. the best C/C++ compilers, I'd say it's more like 50%-150%. But for a lot of stuff it doesn't matter. If you're just making database queries then most of your CPU is sitting there anyways and the delta on the caller side isn't a factor.
Go is pretty nice in it's zone. It's got rough edges but all in all it's fun to use. There are occasional annoyances (like those times where you implement another cache for the 10th time because you can't build a good generic cache or those times where you need a priority queue and interact with those clunky library implementations, so yeah, I'm in the generics fan club). But that hasn't stopped me from using and loving Go. I don't miss those C++ core dumps ;) or dealing with memory management (even with smart pointers and containers it can be a pain). Go's more dynamic aspects (reflection etc.) also make some things easier vs. C++. People can learn Go really quickly which is another bonus, learning C or C++ can take years.
should we add the amazing declarative marshalling and unmarshalling to/from json and xml and possibly others? or is that part of the interaction with the outside world, ffi and marshalling in one bag?
It’s not that Go is not a great language; The problem is that it’s a very conservative language that is not picking up low-hanging fruit. Their maintainers also have a nasty habit of being condescending to other people’s use cases; E.g., f-strings, unix shebang as comment, ... .
I find your characterization of critica of Go, highly uncharitable, but unfortunately all too common with Go apologetics.
If we look outside of Go, I see a lot of criticism of C++, Java, PHP and Node.js, but I rarely see the critics being attacked with "You just didn't write enough C++ code" to qualify for a critic license. I find this approach hypocritical.
For what it's worth, I wrote several software systems in Go, and deployed them in production. At least one of them is serving millions of daily active users.
Some things I'm happy about (and these were my reasons to use Go in the first place), like:
1. No JIT = no warmup
2. Portable static binaries
3. Solid tooling
4. (Mostly) high quality static library
What I really find frustrating in Go is not the verbosity (Rust is probably equally verbose, and I get over that), but the utter lack of type safety.
You see, most critics of Go (definitely the people complaining about lack of genrics), DO NOT complain about static typing. In fact, I don't remember ever seeing anyone complain about that. What people often complain about is _not enough static typing_.
Due to lack of genrics and other limitations of the language, Go essentially forces you to use empty intrrfaces, stringly-typed data, weak enums and runtime type checks all over the place. This leads to fragile code, and makes it very hard to have the same safety guarantees you could have in Java - let alone in a more FP-oriented language like Rust or Haskell.
> I find your characterization of critica of Go, highly uncharitable, but unfortunately all too common with Go apologetics.
I might suggest that if you're the type to use the phrase "Go apologetics", you misinterpreted my post and also emit uncharitable feedback quite often :)
> If we look outside of Go, I see a lot of criticism of C++, Java, PHP and Node.js, but I rarely see the critics being attacked with "You just didn't write enough C++ code" to qualify for a critic license. I find this approach hypocritical.
If you scroll up, you'll find my request was simply to include the level of involvement a programmer has with a given language as part of their critique. Critique away! But as with all things, when novices critique things they don't understand, they're mostly just being negative for negativity's sake.
Go into a C++ space and include "I've never written C++", followed by any criticism and let me know how serious your criticisms are taken. I can wait :)
> Due to lack of genrics and other limitations of the language, Go essentially forces you to use empty intrrfaces, stringly-typed data, weak enums and runtime type checks all over the place. This leads to fragile code, and makes it very hard to have the same safety guarantees you could have in Java - let alone in a more FP-oriented language like Rust or Haskell.
We can disagree - the purpose of my post was not to prove Go was a "good" language or not. These bits of feedback sound like you're following antipatterns, but you're welcome to program in Go however you'd like.
My experience in Go in production is limited to ~150 lines of code I wrote to contribute a small feature for one Terraform provider.
The provider in question is a very thin wrapper over an API library. The feature I added contained almost no business logic of its own and would probably take 15-20 lines in a more expressive language.
Based on that experience, I would not choose Go for a project of my own (unless it could directly benefit from Go's strengths, i.e. unless it required fast startup, portability and concurrency).
This is a pretty good example of feedback provided by language novices. You have almost no experience with the language, complained about an aspect of it (line count), and then said you wouldn't use it again, unless you would (as you defined in your closing parens).
I'm not trying to diminish your feedback at all. You like what you like, don't what you don't like, etc. Nobody is forcing otherwise. It's just an interesting pattern that you can see with parallels in almost any language. Much like when novice Clojurists say "I wouldn't use Clojure because of all the parens" - there are things that, for most novice+ users, just kind of end up not mattering a ton. The things inexperienced users think are a big deal (in your example, line count) usually end up not being a big deal.
I’ve not used Go, so I have no opinion. I know some folks that really like it, though, and I respect their opinion.
Same with Rust.
I programmed PHP for twenty years (not full-time). I never learned to like the language, but got fairly good at it.
I now program pretty much exclusively in Swift. It is full-time. I’ve been writing it since it was announced.
I like Swift quite a bit.
One of the things that I’ve learned, is that there aren’t actually that many shops that have done industrial-scale Swift projects. Lots of people have done fairly small ones, but very few really big ones (like you will find with ObjC).
Writing a half million lines in any language gives you some authority.
> Writing a half million lines in any language gives you some authority.
Totally.
In this blog post, Khan Academy says "It is performant enough for us and our team likes it". In this comment section, there are people insisting it's a bad language while having never used it. Not just "Haven't used it at that scale". Legitimately have never written a line of Go. This is part of why I made my post, to try to highlight the silliness of a lot of the "feedback" being slung around.
I wrote 5000 lines of java for a simple multi-user chat room server. I had a separate Java client. I replaced the server with half a page (~200 lines of Erlang) as my first attempt at using Erlang with the exact same functionality.
I still write copious amounts of Java for work and Java is still horrid.
Using an existing websocket library, that chat client, with multithreading, should be a handful of lines in Java. Even writing the raw socket handling yourself, with multithreading, should only be a few classes and maybe 500 lines...
I have spent 3 years being employed to write Go, and i think the language is abhorrent. Once I moved to a job where I didn't need to write Go, I quit smoking.
I appreciate you adding context to your negative comment. I wouldn't really call it feedback, though, as you kinda just said "I hate it" a couple different ways.
What you're trying to say is that Go's domain is mostly serverside. That's true. But it sounds too useful and ambitious, so you've modified it to "CRUD microservices". Which is silly and false, but works better as a slur on the language.
What's dispiriting here is that there are cogent arguments against Go --- decisions Go could have made differently that probably wouldn't have cost the project much in terms of its other goals of compilation speed and simplicity, but make it more annoying to work in. I switch between Rust and Go every day and miss pattern matching almost every time, for instance.
Instead, though, we get the same dumb innuendo. "It's a language for managers". "It's a language for CRUD apps". "It's a language for junior programmers". Never mind that the kernel of truth in those statements is shared by languages like Python; you can't really get away with trashing Python like that, or you'll be haunted by the ghost of one of Peter Norvig's terrifying shirts. Which is a way you know not much is really being said here, except that there's some weird turf war between Go and whatever language its haters are advocating for.
Linear search through a slice is almost always going to be faster than a map operation, because N is almost always small and slices have great cache locality.
If your slice has a million entries, it's definitely a different story.
Using a map might not be faster as others have pointed out, but it's often more readable. The core library does this in several places. A map[someType]bool makes for nice, readable code:
I use Go at work and Rust at home. They're very different languages but I enjoy them both. I do find that Go is more of a "get shit done" language and Rust is more on the creative side.
I just wish that Go would have optionals. Pointers cause too many bugs. Is this parameter a pointer because it'll be mutated? Or because it's allowed to be null? Only way to find out is to run it or to inspect the functions code. You shouldn't need to view implementation details to find out
I'd also rather have optionals/non-nullable types in the core syntax than generics. Although, I must say, I haven't seen a nil pointer crash for a long time.
You must be good at avoiding it or your programs are simple. Go is not much better than C in this particular case.
I forgot specific detail how it was possible (I noticed it 1-2 years ago) but in Go you could have nil value when using references. It was somewhere around implementing an object.
> Our former .NET engineer now says, “Being able to call a function that doesn’t return an error and know for sure that it must succeed is really nice.”
Except when it panics. The article lacks substance honestly.
That being said, it further cements the notion that golang has been a replacement for Python and Ruby, not Java/C#/C++ as originally planned.
Go can easily replace Python or Ruby (because it is faster and libraries are available to do what you need). Go can replace Java and C# especially because concurrency is easier and performance is on par, if you can get along with the inferior libraries Go provides. Go cannot replace C++ in any function, because its compiler is just too primitive, low-level things are impossible and libraries will never be on par with what C and C++ have.
But generally, Go is nice because there is sufficient tooling to do most things nicely and the language is primitive enough to make the tooling work in almost all situations. Which is different from C++, because tooling there is always too stupid for templates, macros, inheritance and pointers. And it is different from Java where tooling is unusably complex, slow and enterpricey. Also, Go didn't accumulate too many "surprise" misfeatures yet, so code can be more reliably written and reviewed than in any of the other languages the parent named. Panic can be seen as the rare instance of such a misfeature, but usually it is only used to signal typically unrecoverable problems like "out of memory". So it is far from the footgun that exceptions are.
Rust has great tooling compared to C++, at least on mainstream platforms. It's a no-brainer choice for green-field projects where low-level development is required.
And the misfeatures of C++ are the stuff of legend, so Rust does way better from that POV as well.
> And it is different from Java where tooling is unusably complex, slow and enterpricey.
That's very subjective. In my opinion, nothing comes close to the excelent tooling that you can find in the JVM ecosystem. From IntelliJ to SonarQube, from VisualVM to Java Flight Recorder, etc. I'm not even talking about frameworks and the support that IDEs like IntelliJ offer for them, which is high quality.
If code can panic it should be documented. Code you write shouldn't ever panic without good reason during the regular runtime of your application. A panic means that the current goroutine has encountered an unrecoverable error, which is usually only the case for bugs such as out of range indexes or nil pointers.
Not handling panics is OK if you own your own code. But if you're sell an application that panics you can guarantee that's going to cause pain while supporting clients. An error log with details can be as useful but won't make the client think you're incompetent. I say this having given Go applications that panic-ed to clients before and they freaked out about these errors that weren't actually a big deal (a minor case in a controller of a web server).
Except that every nil pointer dereference panics. Every out of bounds access panics.
Is that also prevented by culture?
Let's not forget that, for better or worse, exceptions in Java are checked. The types of exceptions that are errors in Go, must be explicitly handled or forwarded by the caller in Java - there is no way around it. The the type of exceptions that are panics in Go are just unchecked exceptions in Java: you don't have to explicitly handle them, but you can use the same mechanism for doing so, and handling them is more graceful, easier and culturally enforced than panic/recover. I have many criticisms of Java, but I'd argue Java culture is much more graceful in error handling there.
that person is talking about application level error handling. In most cases if a functions panics generally the caller isn't expected to do anything about it other than perhaps have a panic handler at the top level of the app that logs things and lets it crash. So in general you don't need to think about catching or handling panics in most of your application code. It is the equivalent of a method that you are calling from your java code returning an undocumented NPE.
Speaking based on direct experience, I doubt that is happening in Financial industry. I wrote a utility in Go that is heavily used in our build workflow. I could have written it in Python too but my objective was to have a dependency-free fast binary in a statically typed language with a decent standard library and Go checked all the boxes. The language is decent and I don't have any huge complaints.
Would I now choose Go to replace our core applications that are written in Java? Nope. Why? My personal main reason is that in pursuit of keeping things simple and homogenous, even everyday things take too much code. I can get the same functionality in Java in basically half loc while keeping the code totally boring and devoid of any interface-madness-programmer-creativity-expressivity black-holes. When you want to type less and deliver quickly, that is important.
Would I use Java to write a utility with objectives similar to what I mentioned in my first paragraph? Nope. Go is totally fine for it.
Panic is like sigsegv in C. Email the stack trace to the team that owns that server and if the numbers are high no more features till the stability is fixed.
Why would anyone want to use a programming language that uses product types instead of sum types for the return types of functions which can return errors? Or why would anyone want to use a language that doesn't even let you write a type safe hash table? Also, the fact that Go suffers from the billion-dollar mistake is downright inexcusable. Go is way too error prone for me to sleep comfortably at night, because the lack of a reasonable type system is a constant liability.
Yes, I've worked on several Go systems. Nothing I mentioned about the language is an assumption; these are well-documented properties of Go. I've also have experience in a handful of languages which have more disciplined type systems (Rust, Haskell, Ocaml, TypeScript, , Kotlin, ...), and it's a night-and-day difference in terms of safety and reliability. Being able to confidently know that you've handled all error cases, that null pointers aren't hiding in your data, etc. is useful for safety.
Another issue I have is the reliance on reflection. In general, I think if you have to rely on reflection to do something, that usually means you're working around some inherent limitation in the normal language - and the resulting code is often far less readable than the code would be in a more expressive language. Lots of Go libraries and frameworks are forced to use it in a lot of cases because there's just no other way to express some really basic things without it.
I really want to like Go. There's a lot I like - the "only one way to do something" approach means that code always feels consistent. Errors as values is a far superior approach to exceptions. I had to write some for a job interview project a while back and it felt really refreshing, but every time I try to use it for a personal project, I don't feel like I'm getting anything out of it that I couldn't get out of say, Rust, or modern, typed Python.
I don't mean this as slight against those people that really enjoy writing lots of (Go) code. It’s just my observation after being in a few different contexts where Go was the language of choice. Personally Go is too verbose for me and this is especially painful/apparent when you get to writing the multitude of tests required to ensure your code works since Go’s type system/compiler doesn't lend you much in the way of helping ensure code correctness.
Go is essentially the new Java.
>It’s just my observation
Well, my observation after dealing with js and ruby projects (ruby was my main language a few years ago) - those creating and expressive languages lead to fewer lines, sure, but in the end you are dealing with a pile of crap nobody want's to touch because some of the developers were so creative and wanted to express themselves more than they wanted to solve the problem.
Go is a tool if you actually want to solve the problem and make sure people after can quickly understand how and why you solved it. Rather than stand in awe of your creation.
I can simply do dict.contains("foo") in Java vs having to go through a verbose hoop:
Common operations like filtering/transforming a collections are very concise in Java - I'm certain doing so in Go will take atleast 5x as many lines: Don't get me started on having to go through iota hoops to declare enums.If the formater allowed to write in one line if err != nil { return err }, it would drop the number of code lines by the third.
Do you have any examples? Not a golang programmer myself.
It's mostly not about the language. An exception is of course when moving from a dynamically typed interpreted language to a statically typed compiled language.
The success of projects depends much more on other things than the programming language. It's about
- Processes and standards, like following a well defined structure, testing, documentation, ...
- Maintainability of code. It must be easy to read, to understand syntactically and to build a mental model of the code
- Long term reliability and stability of the eco system
- Easy and reliable tooling
- Developer efficiency, e.g. compile times
Go shines in many of the aspects. Especially in maturity, stability, amazing standard lib and tooling. As you mention Rust: This is exactly where Rust falls short. Rust is a great language with amazing people behind it. But there are reasons why its adoption in the broad real world is very, very small. The reasons are not the language. So I always feel it's a bit invasive when Rust promoters enter Go or Java threads by telling how much better Rust as a language is.
In this example Go has additional benefits being statically typed and compiled, very fast and with build in concurrency support.
Old code always compiles.
The stdlib of Rust, I find it superior, very much so, to the one of go. Even though this is mostly due to the different type system.
The tooling around rust, it is just great, what tool you feel missing in Rust?
Few years ago, rust was lacking libraries, but they are really catching up quickly.
They are different languages that shine in different scenarios, but maturity, stability and stdlib is not where Rust is lacking behind.
That said, I think you are right about explicit nullability. A language like Go with this feature, as well as named arguments and limited ADT's could be very compelling for Go's use case.
And yet it's a top-5 most-loved language among programmers and there's no real evidence that it's popular among managers (discussed in detail here: https://news.ycombinator.com/item?id=27175297). Moreover, lots of open-source software exists in Go--if only managers like the language, what's the theory for why all of this software (including major software, like Kubernetes) exists? Is it managers building it in their spare time? Even then, those managers are acting as programmers (not managers) in that capacity.
With respect to the implication that only managers care about the pragmatic aspects of software development (collaboration, onboarding, readability, debugability, maintainability, etc), where did this meme come from? I get the meme that there are many programmers who value abstraction above all else, but I'm not familiar with the meme that all programmers are abstraction maximalists.
I completely disagree with this statement. Some of the most high-performance modern platform-level code written is currently written in Go.
It was a Google presentation that examined their efforts to convert their download site to Go (from C/C++) that got my attention. It's easier to read, has a simpler mental model, and faster than its C/C++ cousin.
Doesn't Java fit the same bill? (although it was kinda modern at the time)
> I think you are right about explicit nullability
Sadly it's really hard to shoe-horn this onto Go, now that the std lib is widely used. Same for proper sum types (which can then be used for multiple return values).
But the continuation of the nullability mistake puzzles me the most... And that wile Go's be designed by big name lang gurus: what were they thinking?
Why is that? I have never seen a cogent explanation for why this is the case.
I can tell you why exceptions (as implemented in Java) are cool: You can write code as if every function call is successful, as opposed to adding a line (or more) of error handling code after every function call, which makes it harder to follow the logic.
If I encode my errors as values (usually with Either), I have to decide how to gracefully fall back if a failure occurs before I'm even allowed to use the successful result. Maybe I just hide the part of the view that needed that data. Maybe I show an appropriate error message. Maybe I email operations to let them know about the problem. Whatever I do, I have to actually think about it and not just assume that the error will be caught somewhere by someone. The result is usually a dramatically improved user experience when failures inevitably occur.
Exceptions tend to pass the buck so far down the line that there's no context to make an appropriate decision. Values tend to force a decision early, when you still have enough context. (Obviously both can be used against the grain, but the question is which pattern is easier.)
In Go -- every error is obvious, and you get a sense of a cyclomatic complexity of any piece of code by just going through it quickly.
So, that's it for me. The complexity of the code is visible.
The patterns for error handling the golang have introduced are admittedly verbose, but they do lend a certain element of confidence that once the code is written, the errors should be handled. Of course a programmer can ignore the errors explicitly but doing so is different than forgetting to catch a thrown exception, because the programmer must go out of their way to write code ignoring the error. It feels like there's more agency around the decision.
"For the truly exceptional cases, the ones that represent either unrecoverable programming mistakes, like index out of bounds, or unrecoverable environmental problem, like running out of stack, we have panic.
For all of the remaining cases, any error conditions that you will encounter in a Go program, are by definition not exceptional — you expect them because regardless of returning a boolean, an error, or panicing, it is the result of a test in your code"
https://dave.cheney.net/2014/11/04/error-handling-vs-excepti...
https://dave.cheney.net/2015/01/26/errors-and-exceptions-red...
In other words, I also don't understand the hype around error values.
So, back to Python, what error value is suitable for replacing an exception? I mean, Python is flexible enough to do Go-style error values, but what does a good error value look like? Maybe I just haven't seen good error values?
If an error happens way down in the weeds and I initially return a very specific error value, do I just pass it up the stack forever? If so, how is that different than traditional exceptions? Do I replace the specific error value with more general error values as I move up the stack to higher-level code? If so, then I am throwing valuable information away and am likely to end up with something like "not found" as my error value at a the top -- not very useful.
Go is a language to get the job done. It's for the masses. It's this type of language you write a blog post on how you have done this and that and why instead of a scientific paper. It's really the boring mortar.
OTOH it will pay your rent.
The language actively hinders any attempts to be fancy or "expressive". You write (or copy/paste) the same pattern(s) over and over again.
On the other hand, you can pick up pretty much anyone's Go codebase and quickly see how it works. No need to figure out what flavour of metaprogramming or other fancy crap the original author was a fan of at the time of writing.
It's boring, it pays the bills. You can be expressive with other languages on your own time.
No, it's not. Unhandled errors that aren't noticed just lead to undefined/nondeterministic behavior and some really nasty bugs. And moreover, I literally cannot understand how it is possible for any programmer to not realize this (compare: elixir's approach, which instead embraces errors and restarts processes instantly).
Give Go a map and option and I'll be a happy gopher.
For example if you try to use some numeric types that are not int, int32, float64 (for example float32 or uint16) you'll be in a lot of pain due to constant casting back and forth. Yes generics could solve this one too.
I also couldn't make myself enjoy programming in it. I think it's because it tried to be simple, so anyone can learn it. Because of it is kind of rare to say that you created with a creative way of solving specific problem.
That's very different to for example C, to which Go (at least early on) was compared to.
Then there's Rust designed around low-level code and memory safety, and a much more complex syntax.
I think it just goes to show that any language (modern or otherwise) is a consequence of its design decisions (or lack of).
Can you elaborate on that? Usually the argument on which one is better is that it depends on use case.
It's very rare that I come across weird patterns or someone trying to be very clever. Its always straightforward code.
Is Go perfect at this? No. I too would love to see some higher level functions exist for to help reduce boilerplate. For example, this proposal: https://github.com/golang/go/issues/45955 to add Filter, Map, etc. to slices. That seems like a practical set of functions to add to minimize boilerplate while at the same time not breaking away from simple idioms.
Deleted Comment
We use Ruby/Rails and Go pretty heavily at work it's much easier to jump in and grok Go code than rails (written by the same engineers mind you).
Go removes abstractions (to a fault sometimes) so, anecdotally, it's pretty damn easy to just read and figure out what's happening.
Of course, if you hate a language, you're not going to write lots of code in it (unless you have to, and then I would expect your feedback to be pretty negative but at least informed). Feedback from beginners/programmers external to a language is super important for the success of that language of course, but lots of debates about things like programming language are cluttered with feedback that isn't really made in good faith. Someone that doesn't like static typing is going to leave feedback about Go that may not directly say "I just don't like static typing", but ultimately is as simple to reconcile (and thus, should mostly be discarded).
I would pick Go for any large project in a heartbeat.
I have to concur that the haters who post to HN probably have never used this language in earnest.
Here's what you get out of the box:
* Extensive standard library * Cheap concurrency * Fast compilation * Built-in test/benchmark framework, including coverage and race detection * Built-in heap and CPU profiling
Of all the complaints the one I understand the least is the reservations about performance. It is within 10-20% of C, and callouts to C and assembly are possible. (My 8-way multiplexed MD5 routine was written in AVX assembler and lives on in minio.) Extracting that last 10-20% is two or three sigmas away from the norm of how programming languages are used these days.
The objection to generics is similar. The lack of generics shows up -- once in a long while. It doesn't prevent me from being immensely productive with this language.
Looking back at the last few years I've wondered if I could have accomplished what I did in Go by using a different language (C in particular) and the answer, for me, is "not a chance".
With Go, all of these problems were basically solved by the strength of the standard library. We could not be happier.
I used to use Java because it was fast enough, have good libraries and was productive enough. Go is almost as fast, the standard library has most of what you need and its ability to compile to a single binary makes deployments simple.
I also look back and think, could I have done all that in Java? Probably... but I also feel it would have required more effort.
I agree that the lack of generics only shows up once in a while and doesn't hamper productivity as much as might be expected, but I'm still very excited to see them added to the language.
How do container types, like arrays or hashmaps work without generics? Do you basically have to declare and implement types/methods for each different contained type?
Go would definitely be my preferred pick for distributed/server applications even though the previous large (mixed C and C++) project I was on was a large distributed application and we did just fine. In fact I'd say our productivity and quality in C++ in that other team was just as good as any I've seen (which is not a random/double blind sort of thing, who knows if that project started in Go from day 1).
I would say that in a large project the language choice has some impact but there are many other variables. A well managed C++ project with competent developers can work well. A less than well managed with so-so developers in Go can be disaster zone. There are lots of other moving pieces, testing, build automation, CI/CD etc.
There are certain areas where I would prefer not to use Go. Basically areas where you spend a lot of time up or down the abstractions. I would prefer not to implement a video codec or a signal processing system in Go. Firmware, motion control etc. would also probably not be a good fit. Going the other direction there are things I can do really quickly in Python (lessay grab some data from some services, do some manipulation, draw some graphs), generally smaller utility like pieces where I want to use data with various random types, use third party libraries. I wouldn't want to write a large project in Python because it's slow and is dynamically typed.
I would also beg to differ wrt/ 10%-20% performance difference. You tend to rely a lot more on GC and produce more "garbage" in Go and the optimizer isn't as good vs. the best C/C++ compilers, I'd say it's more like 50%-150%. But for a lot of stuff it doesn't matter. If you're just making database queries then most of your CPU is sitting there anyways and the delta on the caller side isn't a factor.
Go is pretty nice in it's zone. It's got rough edges but all in all it's fun to use. There are occasional annoyances (like those times where you implement another cache for the 10th time because you can't build a good generic cache or those times where you need a priority queue and interact with those clunky library implementations, so yeah, I'm in the generics fan club). But that hasn't stopped me from using and loving Go. I don't miss those C++ core dumps ;) or dealing with memory management (even with smart pointers and containers it can be a pain). Go's more dynamic aspects (reflection etc.) also make some things easier vs. C++. People can learn Go really quickly which is another bonus, learning C or C++ can take years.
If we look outside of Go, I see a lot of criticism of C++, Java, PHP and Node.js, but I rarely see the critics being attacked with "You just didn't write enough C++ code" to qualify for a critic license. I find this approach hypocritical.
For what it's worth, I wrote several software systems in Go, and deployed them in production. At least one of them is serving millions of daily active users.
Some things I'm happy about (and these were my reasons to use Go in the first place), like:
What I really find frustrating in Go is not the verbosity (Rust is probably equally verbose, and I get over that), but the utter lack of type safety.You see, most critics of Go (definitely the people complaining about lack of genrics), DO NOT complain about static typing. In fact, I don't remember ever seeing anyone complain about that. What people often complain about is _not enough static typing_.
Due to lack of genrics and other limitations of the language, Go essentially forces you to use empty intrrfaces, stringly-typed data, weak enums and runtime type checks all over the place. This leads to fragile code, and makes it very hard to have the same safety guarantees you could have in Java - let alone in a more FP-oriented language like Rust or Haskell.
I might suggest that if you're the type to use the phrase "Go apologetics", you misinterpreted my post and also emit uncharitable feedback quite often :)
> If we look outside of Go, I see a lot of criticism of C++, Java, PHP and Node.js, but I rarely see the critics being attacked with "You just didn't write enough C++ code" to qualify for a critic license. I find this approach hypocritical.
If you scroll up, you'll find my request was simply to include the level of involvement a programmer has with a given language as part of their critique. Critique away! But as with all things, when novices critique things they don't understand, they're mostly just being negative for negativity's sake.
Go into a C++ space and include "I've never written C++", followed by any criticism and let me know how serious your criticisms are taken. I can wait :)
> Due to lack of genrics and other limitations of the language, Go essentially forces you to use empty intrrfaces, stringly-typed data, weak enums and runtime type checks all over the place. This leads to fragile code, and makes it very hard to have the same safety guarantees you could have in Java - let alone in a more FP-oriented language like Rust or Haskell.
We can disagree - the purpose of my post was not to prove Go was a "good" language or not. These bits of feedback sound like you're following antipatterns, but you're welcome to program in Go however you'd like.
The provider in question is a very thin wrapper over an API library. The feature I added contained almost no business logic of its own and would probably take 15-20 lines in a more expressive language.
Based on that experience, I would not choose Go for a project of my own (unless it could directly benefit from Go's strengths, i.e. unless it required fast startup, portability and concurrency).
This is a pretty good example of feedback provided by language novices. You have almost no experience with the language, complained about an aspect of it (line count), and then said you wouldn't use it again, unless you would (as you defined in your closing parens).
I'm not trying to diminish your feedback at all. You like what you like, don't what you don't like, etc. Nobody is forcing otherwise. It's just an interesting pattern that you can see with parallels in almost any language. Much like when novice Clojurists say "I wouldn't use Clojure because of all the parens" - there are things that, for most novice+ users, just kind of end up not mattering a ton. The things inexperienced users think are a big deal (in your example, line count) usually end up not being a big deal.
Same with Rust.
I programmed PHP for twenty years (not full-time). I never learned to like the language, but got fairly good at it.
I now program pretty much exclusively in Swift. It is full-time. I’ve been writing it since it was announced.
I like Swift quite a bit.
One of the things that I’ve learned, is that there aren’t actually that many shops that have done industrial-scale Swift projects. Lots of people have done fairly small ones, but very few really big ones (like you will find with ObjC).
Writing a half million lines in any language gives you some authority.
Totally.
In this blog post, Khan Academy says "It is performant enough for us and our team likes it". In this comment section, there are people insisting it's a bad language while having never used it. Not just "Haven't used it at that scale". Legitimately have never written a line of Go. This is part of why I made my post, to try to highlight the silliness of a lot of the "feedback" being slung around.
I still write copious amounts of Java for work and Java is still horrid.
Using an existing websocket library, that chat client, with multithreading, should be a handful of lines in Java. Even writing the raw socket handling yourself, with multithreading, should only be a few classes and maybe 500 lines...
What didn't you like about it?
What's dispiriting here is that there are cogent arguments against Go --- decisions Go could have made differently that probably wouldn't have cost the project much in terms of its other goals of compilation speed and simplicity, but make it more annoying to work in. I switch between Rust and Go every day and miss pattern matching almost every time, for instance.
Instead, though, we get the same dumb innuendo. "It's a language for managers". "It's a language for CRUD apps". "It's a language for junior programmers". Never mind that the kernel of truth in those statements is shared by languages like Python; you can't really get away with trashing Python like that, or you'll be haunted by the ghost of one of Peter Norvig's terrifying shirts. Which is a way you know not much is really being said here, except that there's some weird turf war between Go and whatever language its haters are advocating for.
If your slice has a million entries, it's definitely a different story.
I just wish that Go would have optionals. Pointers cause too many bugs. Is this parameter a pointer because it'll be mutated? Or because it's allowed to be null? Only way to find out is to run it or to inspect the functions code. You shouldn't need to view implementation details to find out
I forgot specific detail how it was possible (I noticed it 1-2 years ago) but in Go you could have nil value when using references. It was somewhere around implementing an object.
Except when it panics. The article lacks substance honestly.
That being said, it further cements the notion that golang has been a replacement for Python and Ruby, not Java/C#/C++ as originally planned.
But generally, Go is nice because there is sufficient tooling to do most things nicely and the language is primitive enough to make the tooling work in almost all situations. Which is different from C++, because tooling there is always too stupid for templates, macros, inheritance and pointers. And it is different from Java where tooling is unusably complex, slow and enterpricey. Also, Go didn't accumulate too many "surprise" misfeatures yet, so code can be more reliably written and reviewed than in any of the other languages the parent named. Panic can be seen as the rare instance of such a misfeature, but usually it is only used to signal typically unrecoverable problems like "out of memory". So it is far from the footgun that exceptions are.
And the misfeatures of C++ are the stuff of legend, so Rust does way better from that POV as well.
That's very subjective. In my opinion, nothing comes close to the excelent tooling that you can find in the JVM ecosystem. From IntelliJ to SonarQube, from VisualVM to Java Flight Recorder, etc. I'm not even talking about frameworks and the support that IDEs like IntelliJ offer for them, which is high quality.
Let's not forget that, for better or worse, exceptions in Java are checked. The types of exceptions that are errors in Go, must be explicitly handled or forwarded by the caller in Java - there is no way around it. The the type of exceptions that are panics in Go are just unchecked exceptions in Java: you don't have to explicitly handle them, but you can use the same mechanism for doing so, and handling them is more graceful, easier and culturally enforced than panic/recover. I have many criticisms of Java, but I'd argue Java culture is much more graceful in error handling there.
that person is talking about application level error handling. In most cases if a functions panics generally the caller isn't expected to do anything about it other than perhaps have a panic handler at the top level of the app that logs things and lets it crash. So in general you don't need to think about catching or handling panics in most of your application code. It is the equivalent of a method that you are calling from your java code returning an undocumented NPE.
Would I now choose Go to replace our core applications that are written in Java? Nope. Why? My personal main reason is that in pursuit of keeping things simple and homogenous, even everyday things take too much code. I can get the same functionality in Java in basically half loc while keeping the code totally boring and devoid of any interface-madness-programmer-creativity-expressivity black-holes. When you want to type less and deliver quickly, that is important.
Would I use Java to write a utility with objectives similar to what I mentioned in my first paragraph? Nope. Go is totally fine for it.
It’s a language for getting things done. It isn’t for language buffs. And that is a huge plus for me.
Python has been around since 1994 (2 years before Java: 1996). It's heavily used in Data Science and Science in general.
The C API allows you to write code in most compiled language (C, C++, Rust, ...) and Python serves as a glue between business logics.
Go simply does not serves the same purpose. There is no winner/loser here, just different use cases...