216 comments

[ 5.2 ms ] story [ 261 ms ] thread
I want to love Go, due to my background in C and UNIX sysadmin, but can't. Rust does it for me though.
This is an article about Go, not an article comparing Go to Rust. I'd say the same thing if the roles were reversed: don't start language debates. They spread like kudzu and choke everything else out of the threads.
I wish people would learn that "don't leave a comment" is a completely viable and reasonable option.
If HN adhered to this policy, it would be the most boring site on the web. Tangential, complementary and/or controversial conversation in posts is where some of the best content is.

Comment police/gatekeeping discussion (outside of dang actually enforcing the rules) is probably the most annoying behavior on this site.

Comment "Rust is better because I use it" does not make great discussion tho. It's just useless noise
A number of applications I really enjoy are written in Go, so it's been on mind to learn enough to be dangerous.

Has anyone had good success with any particular resources?

What are some examples? I've always thought of Go as a server-side language.
fzf, hub, syncthing. Possibly also scc if for some reason you're not running tokei or loc.

Maybe also frp and hugo.

Docker and corresponding tools.

https://github.com/abiosoft/colima

And many others, really.

> Docker and corresponding tools. https://github.com/abiosoft/colima

Neither sound like "applications I really enjoy". More like applications you suffer.

> And many others, really.

How about examples?

Well, how about Kubernetes?
To update an old saw, Kubernetes is proof you can write Java in any language.
>More like applications you suffer. >How about examples?

Sorry, I'm not here to tease or please you.

I'm pretty sure you can use google or github search to look for popular Go based projects and try them out.

> Sorry, I'm not here to tease or please you.

So you replied to a comment asking for examples, without examples, because... your life passion is to be an ass?

No, I replied with examples that you personally find invalid for your personal reason.

And I'm not eager to bruteforce the "right answer".

Gitea and Hugo come to immediate mind. I thought there was a game framework that used Go as well (which I can't find at the moment).

As a .NET guy, I've actually started looking for tools built in Go since I know they'll be easy to run across the platforms I use, without extra dependencies.

Game framework you may be thinking of is called ebitengine.
.NET applications can be built as a single static binary, although IME it's usually much heavier than a Go alternative.

https://learn.microsoft.com/en-us/dotnet/core/deploying/sing...

I also exclusively host dotnet applications on Linux (and also develop them there), and it works just fine.

Oh I know, and I'll develop in .NET if I want to build something for Windows/Mac/Linux. (Similarly I've been hosting personal dotnet applications in Linux, although I've been building them on Windows/Mac. As someone who started back in .NET 2.0, it's amazing.)

But if I'm looking for a tool someone else built, .NET options are usually pretty slim.

Caddy, Gotify, Photoprism
A good place to start is their official "tour".

I found it a little bit redundant coming from already knowing common programming constructs and C, but it was still a good introduction to the syntax and especially for the more unique features like Goroutines.

https://go.dev/tour/list

If you're really new to Go, but familiar with other programming languages, I would recommend starting with the tour (https://go.dev/tour/welcome/1) - I think it explains some of the differences between Go and other languages pretty well and gets you up to speed on the basics fast, without having to install anything. Once you have done that, you can continue with the "official" documentation listed on https://go.dev/doc/ which includes "Effective Go" (some useful recommendations) and the FAQ.
If you want a challenge, try "The Go Programming Language" by Alan A.A. Donovan and Brian W. Kernighan.

It's somewhat akin to what "The C Programming Language" (K&R) is to C. It's a fairly practical and on-point, and has a lot of (somewhat tricky) exercises, while still providing a complete overview of the language.

The Donovan book is fine. It may be slightly outdated in places but nothing that would trip up a developer with prior experience in some other language. One of Go's virtues is it's simple enough you can be comfortable and productive with just a weekend of learning.
If you can't learn Go, you're going to have a bad time with Rust.
Don’t be so sure. I found Go to be frustrating and difficult, but Rust has been a pleasure.
I do c# in my day job, and so far Rust has been much easier to pick up than Go. Maybe it just happens to be more similar to c#, but given it can be used as a system level lang like c++ I've been very surprised how quick it can be picked up (at least for basic tasks).
> Lack of docs, broken and confusing functionality, limited language features, bizarre operations.

You are painting with a pretty broad brush, can you provide a concrete example of these?

I don't know how applicable this is, but even the basic if-else syntax can hit you with hours of frustrating struggles. The parantheses have to be arranged in a specific way that was never mentioned in the online language specs over at go.dev, otherwise the code will fail:

    if (cond) {
         return x
    } else { 
         return y
    }
works, but

    if (cond) { return x }
    else { return y }
doesn't.
> never mentioned in the online language specs over at go.dev

hmm, what's this then?

https://go.dev/doc/faq#Does_Go_have_a_ternary_form

That's the FAQ. The official Language Specification says nothing about this little gimmick, at least in the if-else section: https://go.dev/ref/spec#If_statements

You'd think one would want to look for it in that instead of an FAQ page.

If you looked at the spec which provides simple, valid examples that work, then why would you spend hours writing different code wondering why it doesn’t work?

The section you linked does explain it, btw.

  IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .
Note there is no newline or semicolon between Block and the optional “else” clause, and a Block is thusly defined:

  Block = "{" StatementList "}" .
So that being the case I’m not sure why you’d assume it’s valid to put a newline after the trailing “}” of the Block, before the “else”.

The other factor at play here is the treatment of semicolons: https://go.dev/ref/spec#Semicolons

All of this is quite natural once you use the language. in fact, it was designed this way in response to the way it was being used.

It is not appropriate for the spec to constantly reiterate concepts that have already been introduced, but rather it should precisely define each concept once. If you want to learn a language from its spec then it behooves you to consider the document as a whole. That requires a lot of mental overhead and I think most people are better off learning from examples and tutorials rather than reading a language spec.

To someone coming from C, C++, or Java, the idea that a newline would change the meaning is completely novel. And Go has the same "flavor" as those languages, so most people would expect Go to behave the same unless the difference were called out very explicitly.
I think the absence of semicolons is a pretty big call-out.
The reason it doesn't work is because of how semicolons work: like in C every statement needs to end in a semicolon, but the compiler will automatically insert them if the last word in a line is a keyword (continue etc.), an identifier, literal value (int, string, etc.), or ++, --, ), ], }. [1]

So your example gets transformed to:

  if (cond) { return x };
  else { return y };
So it is mentioned in the language specification, just as a non-obvious consequence of this.

It's not my most favourite part of the language either, but at least the compiler message on it is better now (it used to complain about semicolons, which was confusing because they're not there in your source code; now it just says "unexpected else, expecting }", which is still a bit confusing but better).

I'm fairly sure The Go Programming Language book mentions this somewhere near the beginning, but some of the online tutorial/tour perhaps could be improved. There's always a bit of a balance in how much to mention.

[1]: https://go.dev/ref/spec#Semicolons

I think gofmt will fix this for you?
I have a completely diferente experience coming to go. It is simple and straight forward language to learn. Goroutine alone is a great feature.

> limited language features

That is by design and I like that. I was a C# developer and seem how C# has evolved, I'm glad that go keeps it simple.

> Lack of docs, broken and confusing functionality

Docs for me are just fine, never had a problem. Could you give an exemple of "broken and confusing functionality"? I really like the cli ux and the its std lib, it is really complete from my point of view, so I'm curious to hear from someone with another point of view.

Go is the best documented ecosystem I've seen. Since godoc is so widespread, people write doc comments and examples all the time. Every single public project has a doc page at pkg.go.dev too.

The syntax rules and the reference spec of the language is very concise too.

Then you clearly have been living with some crap ecosystems. Go is very poorly documented. Basic functionality that is hidden behind environment variables isn't documented at all sometimes. Just figuring out how to compile an app statically took searching through four docs and guessing. The build and install options don't even result in a binary half the time and its not clear why.
The number of flags required to statically compile stuff when using cgo is indeed a bit complex (if you're not using cgo it will always statically compile), partly because interfacing with C is a bit complex.

There is an issue to make it easier[1], and doesn't seem too hard to find more information about it[2].

More general: I think I've seen every language described as "badly documented" at this point. Someone new comes to the language, knows what they want to do, gets stuck/frustrated because it's a little bit outside the "mainstream path", and calls it "badly documented".

[1]: https://github.com/golang/go/issues/26492

[2]: https://duckduckgo.com/?q=go+compile+static

I honestly have no idea why so many hate the language but I guess the Bjarne Stroustrup rule applies.

For me it's a language I always wanted to have: essentially C with a GC, small enough you can carry it in your head with most of the footguns removed.

I honestly also have no idea why Rust always makes an appearance in a Go thread, I can't think of two languages with such a diametrically opposite learning complexity.

Simply because it's limiting. Which is good if you are inexperienced, but not so great once you grow and try to become more productive. A more expressive language is a must then or you'll be stuck. For some people it's this feeling of being stuck that they don't like - others can live with it more easily.

> I honestly also have no idea why Rust always makes an appearance in a Go thread

Both Rust and Go people are very vocal about their languages.

I have about 30 years of coding experience, try again. C++ to Python, C to Go. Often I want to get shit done and not marvel at how smart I am.
Seconded. I have been bitten far more often by too fancy abstractions then by not having more fancy abstractions in my tooling. And Go has quite a few fundamental ones, like first class functions and closures.
What's with the notion of "getting shit down" vs "being smart". I want to get shit done as well. And for me, an expressive proramming language helps me with that. Otherwise I would be using assembly. It really doesn't have to do anything with "being smart".
The language is limited, but I do not find it limiting.
Agreed, if anything I think the limitations actually help me get stuff done. No need to think how best to resolve something, just do it the obvious way and move along.
That's okay! You should use what you like. People are different, otherwise we would all use the same few languages.
> Which is good if you are inexperienced, but not so great once you grow and try to become more productive. A more expressive language is a must then or you'll be stuck.

Ok, I get it that there are developers that can't live with languages that don't have tons of shiny features to play with, but please don't disparage the others as "inexperienced"! If you have ever gone back to some "expressive" code you wrote months/years ago while feeling very productive and were left wondering "WTF was I thinking when I wrote that? Can someone explain it to me?", then you start to understand the reasoning behind Go's simplicity.

It's not "shiny features". It's things that can make you more productive. Or would you say golang's channls are also a shiny feature?

Also, I'm not "disparaging" anyone here. Please read it again and notice that I was simply saying that golang is good if you are inexperienced. That does not mean that every golang dev is inexperienced. If you interpreted my post in that way, I think you should probably ask yourself why that is.

But you said:

> Which is good if you are inexperienced, but not so great once you grow and try to become more productive. A more expressive language is a must then or you'll be stuck.

which implies that people who use Go have not grown and become as productive as people that use more expressive languages (because such languages are a "must" to grow in this way). At the very least, you are obviously disparaging Go programmers' productivity.

> which implies that people who use Go have not grown and become as productive

That's not what it says, or at least not what I meant.

People are different and can accept a different amounts of repetitive work. Heck, there are even people who really like it and want to think as little as possible. So all I'm saying is that there exist people in Go that grow and then want to improve their productivity but are now restricted by the language.

And I think I have a point in the sense that Go is already adapting to those people by e.g. adding generics. So the number of those people can't be that small, otherwise I don't think they would follow their requests.

> because such languages are a "must" to grow in this way

And yeah, there are of course different ways to become more productive, e.g. through other kind of tooling, better organization etc. But the language is definitely one of our most important tools so if the language limits you then switching it is the only way to improve in this regards.

In fact, English is not my native language and I often feel limited in both my mother tongue and English. Sometimes I'd love to just mix the two but obviously I can't. It's the same thing, but natural languages are much closer to each in terms of expressivenes than programming languages are.

> At the very least, you are obviously disparaging Go programmers' productivity.

I'm saying that a developer who feels limited by golang can and will become more productive with a less restrictive language, all other things being equal(!).

So there are two categories of Go programmers in your opinion: (1) the beginners, who will eventually feel its limitations and will advance to a better language; and (2), those who don't mind repetitive work and want to think as little as possible, those will happily use Go until retirement. Still not really flattering, is it?
Common! My example of people who don't want to think at all was clearly not in the context of developers. I'm talking about factory workers or sometimes even office workers.

For golang there's certainly devs how just don't mind a certain amount of repetitive work and are happy to use Go until retirement. And I don't think that's a problem at all. People are different and I think we should embrace that. If you think being called a "factory worker" is not flattering, then that's more your personal judgement than me being objectively disregarding factory workers.

Again: Try to read my response in the right context. I'm explaining why some people dislike (or even hate) the language. It was a response. Please try to keep that in mind when you reply.

> Simply because it's limiting. Which is good if you are inexperienced, but not so great once you grow and try to become more productive. A more expressive language is a must then or you'll be stuck.

I've never been "stuck" with Go in the sense that I wasn't able to do something I wanted.

I've programmed in plenty of more expressive languages over the years, and I don't think there's much difference in terms of productivity. Sure, some more expressive languages allow you to do something very quickly and concisely, but it also increases cognitive load when writing and reading and in the end it seems to balances out. Sometimes I do find myself thinking "gosh, I wish I had $feature_x from $language_y" at times, but I have that with every language.

It's a "dumb" languages in that sense, I guess. Which is also why it's nice: you don't have to focus on the mechanics of what you're implementing, you just implement what you want, as per the "Zen of Python"; "There should be one – and preferably only one – obvious way to do it."

(comment deleted)
I've been coding professionally in Go and have recently switched largely to Rust. I feel Go is designed for junior developers: it's easy to get started, like with Python, but comes at the cost of verbosity and lack of expressiveness. E.g., you'll have to live with "if err != nil" all over the place. It's good for orgs though because code written by different people looks the same and readable. Most importantly, it's garbage collected, which can be good or bad depending on where you're using it (good because you can be super cavalier with memory, but bad because of stop-the-world pauses).

Rust IMO is much more advanced and expressive. It's a joy to write, makes you think and feel like a developer rather than an assembler that you can feel with Go. IMO it handles most of the common problems with languages well and has probably the best support for Web Assembly if that's your goal. It's not garbage collected, which is critical for systems programming. It is more complex than Go though and takes longer to master. Also, while it's growing rapidly, Rust also has fewer job openings compared to Go, if you're doing this for getting a job.

I think there's two ways people view programming: it's either a means to an end, or it's a fun pizzle trap. Personally I only care about getting the job done in the cheapest and most reliable way possible. There's lots of interesting problems when you think about the dynamics of distributed systems, improving developer velocity, etc. Trying to code golf a single method just isn't appealing to me, so I don't mind slapping together some Go and a good set of tests and calling it a day.
What is it with people who love Rust? If a language is not an absolute behemoth, it's a kid's language. Do you feel the same way about Lua? Like, this notion is wild to me.

> Rust IMO is much more advanced and expressive. It's a joy to write

It can be argued that in a professional setting, this isn't what a business cares about. Businesses care about getting stuff done, not whether you like to write your code and your language is "expressive". They want boring. Boring is good.

(comment deleted)
> Rust IMO is much more advanced and expressive. It's a joy to write [...]

Maybe, but is it also a joy to read?

Seriously. I think Rust has a lot going for it, but I will say this: the type inference in Rust combined with its expressive type system can make sophisticated programs very hard to follow. My experience was with extensively reading the cargo codebase; trying to figure out what the resulting type of an expression that maps/filters/collects over other complex expressions is a chore.

For example, I find it very hard to figure out what the type of public_dependencies is in this declaration: https://github.com/rust-lang/cargo/blob/8fbc8459d59f3acecdb6...

This is the same kind of thing that makes JavaScript challenging to read. It requires a lot of discipline on the author’s part to make it clear what is going on, and even the best of us lack discipline at scale.

Simply looking at the Resolve struct definition tells you.

    /// A map from packages to a set of their public dependencies
    public_dependencies: HashMap<PackageId, HashSet<PackageId>>,
I do agree with you that Rust is easier to read in an IDE though, since then those types gets autoresolved and hinted inline for you.
> bad because of stop-the-world pauses

Have you actually experienced this to be a problem in the last half decade with Go? GC pauses in modern Go programs are short enough to be negligible in almost any practical program.

> Lack of docs

Are… you sure? Go has some of the best documentation. You can learn Go in one sitting with its tutorial. Not exactly sure I agree with you there.

> limited language features

This is where people lose me.

Go is an awesome language and I'm very happy I chose it as the main language for my personal projects. However, I have implemented some quirky Lisp in Go to have some more dynamic experience, too, so now my applications are becoming hybrid.
> Workspaces make it easy to work on multiple modules simultaneously, which is most helpful when you are maintaining a set of related modules with module dependencies between them.

It's a somewhat uneasy feeling to see how the "solution/projects" (in Visual Studio terms) hierarchy keeps getting reinvented in every. single. bloody. ecosystem, even in non-PL ones. Surely there must be a better way to organize things?

Go workspaces are amazing. You create a go.work file, add a line with each module you're working on, and then the Go language server handles the rest. Solutions and projects seem way more complicated, but also not entirely congruent. A Go workspace is used when you have two modules that depend on each other and you want to work on both concurrently. The two modules are still independent and could be worked on individually by pulling in the latest release of the other module.

The modules in a workspace may have entirely different ownership. For instance, let's say you're using a WebRTC library to run a selective forwarding unit as part of your video conferencing app. Maybe you notice a bug in the WebRTC library. You can check out the code for the WebRTC library and add it to your workspace so you can test your application against your local WebRTC library changes.

By amazing do you mean specifically for Go? Because I don’t see how it differs from working on two Maven projects simultaneously in Java where one depends on the other.
In maven, don't you need to update the pom file of the projects to point to the local copy? What's nice about the Go workspace system is that the workspace lives above the individual modules, meaning that the individual modules don't need any modification to pull in local dependencies.

E.g. if your workspace looks like this:

    workspace/
        mod1/
        mod2/
Then both mod1 and mod2 will pull their dependencies remotely.

But if you add a file to workspace/go.work, then local versions of mod1 and mod2 can be used without changing either mod1 or mod2.

This is how a VS Solution is. You can have it anywhere in the file system and points to different projects. How those projects are on your file system its up to you (aka git clone, git submodule)
You can read about the VS solution format here: https://learn.microsoft.com/en-us/visualstudio/extensibility...

As you can see it's quite complicated. The contents of go.work is literally

    mod1/
    mod2/
Also, the go.work file is meant to be your local workspace. If mod1 and mod2 have dependencies between each other, they should declare them explicitly and pull them in from the remote in most cases. The go.work file is only when you want to work on both modules locally. Meaning you don't check in go.work to your github repository in most cases.
Yeah the VS sln file is outdated and meant only to be used by the IDE. There are talks about revamping it, like the C# csproj had.

I wasn’t aware about “not checking in go.work”. What if you want separate modules to be aware of each other but keep them in the same repo?

> In maven, don't you need to update the pom file of the projects to point to the local copy?

Yes and no. It depends. You can use modules. With modules, each sub project lives in a directory under the root project. Maven reactor figures out the build order and all that crazy stuff. It’s the same with sbt and gradle.

I really want to love this language, a fast and simple garbage collected lang, but feel like they missed the spot a little. I just wish they did something different with error handling / nil, doesn't feel right for the language. Also whats up with stuff like unused imports being such a big deal?
I keep count in my own code of the number of bugs Go's "damnable use requirement" has caught for me, and I'm now up to 5. It's not a big number, but they were real bugs that would have annoyed the shit out of me if they'd made it into shipping code. I think this is one of the polarizing decisions Go made that is going to turn out to be universal orthodoxy 10 years from now.

I think error handling is the thing about Go that people get most wrong. I believe that what's happened is that Go expected its users to be people fleeing C++ (they definitely got that one wrong), and instead they got a flood of Python and Java developers. Systems programming is error programming. Hiding and abstracting errors in systems code isn't a win; it's a handicap. Fiddly decisions about errors is the whole ballgame. But that's not the case in application code (EAFP!) and Go has been beset by that countervailing sentiment ever since.

That's not to say Go's got error handling perfect; it would be better with matching. But no mainstream language gets errors perfect. What unifies the strong systems languages is that they enable the overt programming of errors.

The biggest problem with the error handling is that it's not composable. It's better than unchecked exceptions, but that's not saying much.

The second problem is nil, use of which is common and doesn't enforce error handling.

I find it inconstant to be so pedantic about unused vars/imports to avoid bugs, but then leave error handling to what it is.
I think solid error handling is the difference between production and prototype code, even in applications. They may not have to be handled in the same way as in systems programming, but they still have to be handled in a careful, consistent, disciplined way.
> I keep count in my own code of the number of bugs Go's "damnable use requirement" has caught for me, and I'm now up to 5.

Are you saying that you’ve had 5 bugs caused by unnecessary imports?

I'm saying I've had that stupid import error pop up in my face 5 times and changed my code as a result, rather than the imports, because it surfaced an error.
Shouldn't it be a warning, though? You'll still notice it, but you aren't forced to deal with the problem immediately.
I'm not saying it caught syntax errors. I'm saying it caught actual bugs in my code. I don't want to be allowed to blow those off.
>I'm saying it caught actual bugs in my code.

It did so 5 times, versus how many times it gave a false positive where you just had to delete the import or underscore the variable?

Warnings are intended precisely for this type of "hey, you probably made a mistake here". Errors should imo be reserved for cases where the compiler is more or less sure you are wrong.

Rust has probably the biggest possible focus on "making sure only correct programs compile", and rustc does not error on an unused variable or import; it gives a warning. Precisely because it does not know if it is a bug, or just, I don't know, "commenting out the code that uses it to try something".

Also, in the case of an unused variable, you CAN blow it off, by using _. In fact, this makes it far easier to forget about it later compared to a warning that can be silenced in the same way, because if it's a warning you'll only do it if you're actually sure, while with an error you might do it even if you're less certain, because it's absolutely necessary.

Together with that any reasonable codebase denies any warnings from the compiler, Clippy or changes from running Rustfmt when running CI. Which is pretty much equal to Go best practice, using something like Golangci-lint instead.

Although, I guess this is the difference between being 13 years old and 7. For Rust easily accessible CI has always existed, for Go at the time of launch it took effort setting it up. Therefore best practices were pushed into the compiler to the detriment of the users, giving the same benefit trading effort writing the code.

What’s about unused imports? Imports usually added/removed automatically by editor.
My biggest complaint in go is "Defaults are useful" logic. I have seen numerous serious production incidents due to simple uninitialized variables. The only workaround is to use a function for all initialization but because that is more verbose it is often not done, and you lose the "keyword arguments" of struct literals.

There are other footguns like the unpredictable by-reference behaviour of slices and the printf functions corrupting your output if you make a type error but they are much less severe than the misguided idea that implicit default values are useful.

I have just embedded entire SPA dist in Go binary, much easier to deploy and reduce read from disk latency.
I've enjoyed writing Go for years. However, it's not been the language that does it for me. It's been the combination of features and ecosystem that makes it a default.

It's hard to explain, but the language is one you can throw into a team of random developers and come out with benchmarks, tests, CI/CD pipelines, unified code formatting, and good parallel work models (via goroutines) almost every time. Builds are instant. Built artifacts are a single binary - not 1,200 files that must be moved. Every single package that has any traction is both easy to read and has documentation inline or via published go.dev docs.

It's easy enough the frontend JS guy can write it. It's complex enough you can use goa.design or go-kit to auto generate openapi specs and complex microservice designs that support multiple transports (like gRPC). Errors always get handled because the linter points them out and the devs don't cheat by setting up multi-function try/catch blocks.

I've had less issues with production Go applications than any other language. I don't want it to win over Rust though, Rust is awesome. I want Go to replace Java.

Wholely agree that Go tooling is fantastic, especially since most of it is bundled and directly supported by the language. Honestly my biggest gripe is nillability, but it really doesn't bite me nearly as much as it did in Java or C#.

There's a reason it's sliding into the "boring and effective" (that's a good thing) camp of languages, but it's still got some cool factor.

Strong agree. Go isn't my favorite language, but it is my favorite programming tool.
Yeah I was at a startup where maybe 10% of engineering had used go before and 20% had used Java before (it was a PHP monolith so most people were PHP developers). The Java code that was written was pretty awful (I was hired probably due to my java experience, so I had a decent idea of good java). However, the Go services were actually really good. There is something about having a simple language that works really well for backend CRUD services.
I think the main difference is there's just an order of magnitude more awful Java devs than Go developers due to Java being the most popular backend language for N years.

If the timeline was flipped and Java was the new language on the block while Go had been around for 25 years, I bet you'd see the quality of Java code was vastly superior simply due to Java attracting passionate developers interested in learning new technology.

I still see Java devs with 15-20 years "experience" coding like 1st year CS students. It really wouldn't matter what language they were coding in.

What sticks out most for the Java code being pretty awful? Was the PHP code just as awful too in similar aspects?
Go is not really an easier language than Java, though.
Back in 2012 I was considerably better-versed in Java (it was the main language for my CS education and a bunch of my hobby stuff). Still, I ended up picking Go because it was so much easier--no need to deal with Maven or Gradle or the alternatives, no need to pick a testing framework or a test runner, no need to configure CI jobs to publish documentation or source code packages, no need to run some external web server process, no need to worry about runtime dependencies (including the JVM itself), etc. No need to navigate an ecosystem (including stdlib) riddled with inheritance and other antipatterns, etc. No need to get a master's degree in tuning the JVM. Text editor plugins just worked. `gofmt` existed and was ubiquitous. The language itself was far smaller and it had native, idiomatic value types from inception!. Everything about Go was just so much easier than Java.
Agree on all points.

I'd like to add that it just tends to not punch you in the balls. I originally picked it up trying to solve a simple web hook integration problem. I had a Python Flask app running which received a webhook, did some data transformation and did a POST to an API. After spending a whole morning pissing around trying to get uWSGI, systemd via ansible etc working I went for lunch. Came back, learned enough Go and had it working in a single binary with systemd unit before the end of the day without any 3rd party packages, server mechanisms, dependency management, futzing. It just worked. The same code has been moved into docker, then into kubernetes over the last few years with no change, no bugs and no headaches.

Similar experiences with Go vs Java and Python. One thing that has stuck with me to this day was how quickly I was able to write a proxy to distribute keys consistently across a set of Redis servers to break out of a bottleneck in an internal processing pipeline.
> I had a Python ....

IMHO, the biggest win of Go over anything Python is the single distributable.

You write once. Compile (or cross-compile) once. Distribute the binary. Job done.

Python you've got the hell of dependencies. Write your script once, but then users have to endure pip hell for the hundreds of libraries you've depended on. And then the potential different dependencies between different Python stuff.

Go is awesome for that and the Go stdlib has 80+% of what most people need.

Also, you can run that Go binary on any Linux system whereas with Python you'll be lucky if you can run on any non-Ubuntu/RHEL Linux system. Moreover, that Go binary will be like 10% of the size of a Python artifact (which amounts to significantly faster deployments and cold start times for things like Lambdas) and it will perform about 2-3 orders of magnitude faster than Python and there will be headroom to optimize (whereas with Python you're usually not going to eek much performance out beyond the naive implementation without substantially overhauling the architecture).
I work on a Python/Django project, and frankly, that hasn't been my experience at all. Poetry makes per-project dependency management using venvs a breeze. There are a few deps on native libraries, such as libjpeg, so you do need to make sure those are installed, but the needed libraries are quite easy to find on most systems, and our Dockerfile only lists a dozen or so apt packages. There are also a lot of Mac users on our team, and they don't have any issues either.

Sure, it's not as nice or compact as a single binary, but "you'll be lucky if you can run on any non-Ubuntu/RHEL system" is massively overblown.

Are there reasonable Go alternatives to pandas, numpy, and sklearn?
> Are there reasonable Go alternatives to pandas, numpy, and sklearn?

You got me there. That is one of the things I keep asking Santa to bring me for Christmas. ;-)

I wish someone would come along with a competitive Go ML lib. That would be awesome with a capital A !

There are one or two niche libraries around for specific tasks (e.g. anomaly detection) but there's no broad-scope general ML library.

Yes, I would also like better C interop speed and better GUI toolkits for desktop apps/games. But hey, that is why we have Rust.
Go has been an acquired taste for me. I still don't love its syntax but the tooling and interesting combination of properties (GC, compiles to a binary, strongly typed) are what ultimately won me over.

It's perfect for middleware, APIs and CLI tools.

I currently write a SaaS website in Go after doing quite some Rust. I do prefer Rust as a language - it's more expresive and I greatly prefer Result<> over error, but it has two major downsides for me compared to Go. First compilation is much slower and Go feels like a non-compiled language because of the compilation speed (my last startup I've used Scala with horrendous compilation speeds, the main reason not to use it) The second is the borrow checker. It's excellent mostly (love explicit mut) but fails for me for structs with sub-structs coming from different owners. Very fast you have Arc<> noise everywhere. The Go GC just works. A simple GC as a -gc=on with auto-arc for variables without lifetime annotations for people that don't need Rust for embedded applications would be a dream come true.

I do love 'import "embed"' in Go to embed web files (templates) inside a binary and then deploy/manage that one binary with systemd with zero downtime updates with systemd port activation. Slick.

Not sure if it’s an exact parallel, but you can also embed files into rust binaries with “include_bytes!” and “include_str!”.
It's a bit more advanced than what Rust macros provided last time I checked. You can shove whole directories in there and then traverse them or serve as static resources with pretty much one line of code.

Random blog post with a few examples:

https://blog.carlmjohnson.net/post/2021/how-to-use-go-embed/

Very cool, I guess one the the advantages of rust macros is that you could write your own to do something more advanced in theory
I've gone as far as embedding a ReactJS app inside of Go's embedFS. It's pretty damn powerful.
You can avoid Arc in rust the same way you avoid mutexes in go - use channels instead. I find programming rust with channels between tasks feels quite ergonomic. Also, at least to me, it seems like it's a no brainier to pick a language with a GC if your target runtime environment allows for it.
Mine does. Still picked Rust, and so happy I did. The type system alone does it for me.
Slight counterpoint - I have written Go in the past and now am working with Kotlin and to be honest, I prefer it. I wouldn't mind Kotlin being the new Java rather than Go.
Java is the new Java :) With features like records, virtual threads, pattern matching, sealed types, string templates, and more to come, it's shaping up very nicely.
The compile times and the fact it takes 10x the memory for a lot of services is certainly something I would like to see addressed. Not to mention that Java for things like serverless has painfully slow startup times.

Java runs nice and quick when it's ready, but it's slow to get there and quite a memory hog.

Compile times? Besides gradle’s startup time, java compiles just as fast as go imo. Go barely does any optimization, but javac does even less - so I don’t really see it a problem on actually similarly sized projects.

Memory usage is a tradeoff — a good GC works best with a bit more overhead.

I want the compiler doing as much work as it possibly can. If they can somehow make the compiler faster, I guess that’s nice, but I don’t want to be saddled with a language that’s been compromised to move work from the computer back into 0.0001 MHz human brains.
I tried the latest version of Java. Unfortunately, it still feels very clunky. They need to add the ability specify functions directly instead of as classes. ex: BiConsumer<T,U>
I prefer Kotlin as well. Its much more readable for expressing business problems.
"Errors always get handled because the linter points them out and the devs don't cheat by setting up multi-function try/catch blocks"

My experience with Go has been that errors get "handled" by propagating them up the stack manually and then logging them, without keeping proper track of how the error got there. So there is error handling but only in theory, in practice what you get is a sort of hand-written stack unwinding logic with far worse functionality and consistency than exceptions, at 100x the verbosity.

In go, you either handle the error where it happens or close to it, or log it. No need to pass things up just to log. I like the simplicity, and the zero cost at runtime. If you could handle the error in another lang you could handle it in go, or youd just supress or log it anyway.
In a microservice, almost all errors are handled by doing nothing and returning the error to the caller. Retrying may be an option, but you have to be careful not to make your system into a cascading failure amplifier.
Error handling is strictly worse than pretty much any other language out there — you are only making you believe that you are handling your error cases. A pragmatic “handle it here or bubble it up” is a much better approach, as more often than not you can’t actually handle the error at call site.

Goroutines in themselves won’t give you actually correct concurrency.

I really hope that Go doesn’t replace Java.

> A pragmatic “handle it here or bubble it up” is a much better approach

This is exactly the approach used in Go code, so I'm not sure what you're referring to. If you're talking about the fact that you can technically ignore an error result like

    res, _ := doWork()
then that's a fair point. But we still have https://staticcheck.io/docs/checks#SA4006 for that.
I don’t see any automatic bubble up with Go.
I don’t think the original commenter said anything about “automatic”. That’s more of a subjective decision—whether or not you manually bubble up an error, or it just floats up because of the exception handler.

Empirically, java code has had terrible error handling, because people just stick one catch statement at the root of their program. Go atleast confronts you with the error at every step.

And that is exactly the wrong approach. You really can’t do anything with the error case at most places. If I write a web server, which parts could knowingly handle a db connection error? Either the db driver internally, or going 10 deep higher and the request handler returning an 500 error.

In go you would just verbosely (and error pronely!!) have to manually bubble it up, while this is automatically happening in Java with an informative stack trace, with no option to forget about it. Java is the one that handles error cases properly (and that’s why you might see exceptions more there — because they weren’t silenced somewhere wrongly!) here.

I agree that a stack trace is useful, but I disagree that automatic bubbling is universally good. If you look at well-written Go, context is added to every site where you bubble up an error.
>Error handling is strictly worse than pretty much any other language out there — you are only making you believe that you are handling your error cases. A pragmatic “handle it here or bubble it up” is a much better approach, as more often than not you can’t actually handle the error at call site.

It could've definitely be better (just Rust-like Result<T> type would make it so much clearer to handle) but Go's insistence of dealing with it here and now means you have to think about what is exactly happening when it errors out, and are encouraged to add context to the error message instead of "here is stack trace, fuck you user, you're not worth knowing what is wrong".

But it's verboseness in common cases is inexcusable, if I have 3 steps to establish a connection, having 3 lines per error to handle is inexcusably bad. And it will be fucking 3 lines even if you write it in one line, because go fmt will expand it back to 3 lines

> Goroutines in themselves won’t give you actually correct concurrency.

Main advantage is they are cheap enough that doing goroutine per request won't bite you in most cases so you can have near-perfect parallelization of common apps by just writing essentially serial code.

The concurrency primitives are built into language and stdlibs which means when you do need synchronization, everyone does it same way.

> The concurrency primitives are built into language and stdlibs which means when you do need synchronization, everyone does it same way.

As long as you after refactor N still are staying in the happy path avoiding the numerous footguns and implicit behavior Go supplies you with.

I would say if you stray outside a single thread or shared nothing fire up a Goroutine per response architecture deeply reconsider the choice of using Go.

https://eng.uber.com/data-race-patterns-in-go/

> are encouraged to add context to the error message instead of "here is stack trace, fuck you user, you're not worth knowing what is wrong"

haha, so true. I almost always wrap errors at every level with addition context. If it was worth doing, then it's worth explaining.

People that prefer try/catching large sections of functionality or want to skip verbose handling of errors often have not thought through the user or tester experience. Heck, even trying to recreate the issue/state from production.

if err != nil { return err }

vs

if err != nil { return fmt.Errorf("context here: %s: %w", action_type, err) } (or wrapping some custom app error with context for the trace)

Of course you don't want to leak private details, but often there is a public component that is worth adding. This also brings up the concept of dual-error chains where what you log and what you report to the client are different.

You surely know that it is very common to rethrow the original exception wrapped in languages with try-catch — it still saves you the time to write that (plus it is safe, no code, plus stacktrace). It is still strictly better than whatever go has.
I've written a lot of try/catch but no one in those languages do that for every branch, it's usually just everything wrapped in a big try/catch.

Go makes you deal with the error every single time something could go wrong, so you have a chance to do the right thing and append context at every single failure path.

Go makes you believe you dealt with the error, but instead you just forget about it.

Also, checked exceptions are very much not like that, and being able to handle errors of a logical unit is also beneficiary (e.g. transactions). You are free to use the scope-size that makes sense for the given use case.

> Go makes you believe you dealt with the error, but instead you just forget about it.

Can you explain that? Touching every context of an error propagation seems opposite of forgetting about it.

> Go's insistence of dealing with it here and now means you have to think about what is exactly happening when it errors out

I read it as "let's stop pretending we are wizardy wizards and can do things right way in 100% cases and let the language to enforce us to handle errors, producing better result for average Joe SR SWE".

Am I right with my readings (i'm not a dev guy at all)?

Yes, every process a person/language/company/country/etc.. adopts has a cost. It's not free. The question is if being forced to always deal with errors is worth the cost of always dealing with errors.

I think it is. I will gladly trade a couple of hours of extra error handling code every week in exchange for no pager duty alarms on the weekend.

It is good to be forced to recognize errors and manually send them up the stack. Too many programmers have a "not my problem" attitude about errors.

1. Immediately when the error occurs, send it to logging with all required data to find the exact point of failure + data to understand the "why".

2. Send the error up the stack.

If you do these two things, your systems written in golang will be resilient.

So rely on the programmer countering their laziness instead of having these things the default in the language also known as exceptions?
Not only that, but all of the features Go doesn't have means that you don't have to navigate the "which subset of the language does this project use?" problem. Every project uses pretty much the same language features. Similarly, the code formatter is universal and minimally configurable, so the code formatting looks the same across the ecosystem. Virtually everyone uses the same test runner too. Also, you get documentation (including links to third party and standard library packages!) for free--no need to set up a CI pipeline for publishing documentation packages. Same with package hosting--no need to set up a CI pipeline to publish packages.

There's a ton of this kind of stuff that amounts to a whole lot of productivity that gets overlooked in all of the pennywise-pound-foolish type-system navel-gazing that programmers fixate on.

One of my mantras is "what you say no to is more important than what you say yes to," and the Go team says no to a lot. It can be frustrating and Go can be incredibly inexpressive (and, frankly, boring) compared to other languages, but it's hard to find another stack in which any random developer can be as productive as quickly on any random codebase. There are other languages where my personal velocity is a lot higher, but at a team level I haven't found anything that beats Go.
> I don't want it to win over Rust though, Rust is awesome.

Why even perpetuate this red herring comparison—maybe over a decade old?—by making unprompted mentions of Rust though.

Many of those things are a consequence of the language, or at least the philosophy behind the language.

e.g., tooling is good because the language is easy to parse

I don't think they do, but with Go which is indeed a simple language, the focus is on letting developers build abstractions rather than providing them in the form of opinionated language features.

And that is, in particular with an eye on performance, usually the right call. One of the worst traps in modern programming is rather than people building specific abstractions tailored to their own needs organically, starting out with them and trying to impose language features on a problem.

(comment deleted)
Go has almost everything going for it. I only wish it was a bit more C-like and you had the ability to run it without a garbage collector, and that it was more suitable for systems and embedded programming.

Perhaps one day someone will come up with an implementation of Go that doesn’t use a GC.

It’s just such an incredible language, and it deserves the type of Rust fanaticism that Rust has, but I suspect Go users are just a different type of person and we really are fanatics, we just don’t publicize it.[1]

[1]: https://survey.stackoverflow.co/2022/#most-popular-technolog...

Just got told about tinygo on another thread where I was saying rust is the obvious only choice. But tinygo looks nice. I've written very little go professionally, and rust only for a few months more. Go does seem super easy. But rust doesn't really seem hard either. And the fearless concurrency is really attractivento not have to think much about data races too much.

Either one is nicer than c++ which I have a quarter century of experience at.

TinyGo still has a GC, right?
It does. https://aykevl.nl/2020/09/gc-tinygo

Edit: but that said with sich constrained resources do people really use gc or even dynamic memory allocation often in embedded? I work embedded and it is usually standard practice to preallocate everything to avoid fragmentation. Unless it is a more performant device in which case I'll add the quotes to "embedded". But for those you could just use normal go, or python for that matter.

> I only wish it was a bit more C-like and you had the ability to run it without a garbage collector

"import C, import unsafe".

You absolutely can, at least for performance critical sections where you cannot afford garbage collection.

https://dgraph.io/blog/post/manual-memory-management-golang-...

You don't even need CGo or unsafe to deal without a GC. Go's GC is written in Go and it obviously doesn't depend on a GC! You just need to know which features imply a GC/allocation and you need to take care to avoid those features. Conceivably, you could devise a linter that statically verifies your no-GC requirement.
> You just need to know which features imply a GC/allocation and you need to take care to avoid those features.

This is very insightful. Would you mind sharing more resources regarding that? I'd be interested in a list of such features.

The GC can also be used manually by setting GOGC=off, then be triggered manually at the most convenient time.

I don't know of any resources, but basically you can intuit pretty easily about how to avoid allocations in Go (don't allocate or grow slices, don't use maps, avoid returning references to anything you allocate inside of your function call, etc), and there are flags you can pass to the compiler to log allocation points. If you don't allocate, then there's nothing to collect, and you can even turn off the GC altogether.
Tinygo lets you build your program with the option -gc=none that results in a link error at every point in your program that would allocate memory on the heap. So that's a very easy way to find them. Tinygo is a completely separate implementation of Go, so not every program is going to be portable 1 to 1, and I suppose that where Tinygo might allocate memory is different than where normal Go would allocate memory, so you'll have to take the results with a grain of salt.

It is of course possible to use Tinygo-compiled programs for your production use case as well. It has a focus on WebAssembly and microcontrollers, but produces perfectly-serviceable amd64 binaries as well.

Well, I've been a huge Go fanboy telling everyone who'd listen (or not!) why it's the best thing since sliced bread. I use Go for building servers at work and have been quite happy in general except for the verbosity. I had zero plans on using Rust and was actually getting annoyed with the non-stop Rust talk everywhere, especially on HN. Then we had to urgently add Web Assembly to our frontend, and to my shock Go was generating gigantic WASM files, so I ended up trying Rust, being forced really. But once I experienced Rust, I've become a convert singing a completely different tune. So those of you also annoyed by all these "fanatical" Rustaceans telling you how wonderful it is, please try Rust (for a real project) before passing judgment.
What exactly is fantastic about it, objectively? I really can’t come up with anything besides goroutines. It’s quite inexpressive and is pretty much like the litany of other managed languages (I am honestly baffled how it gets compared to Rust — wouldn’t you be surprised if someone was going on about C vs JS? That’s the same thing)
Personally, I hate writing Go. It's a very dull and boring language, but it's an amazing language (objectively speaking) for software development.

1) It's performant. The language itself is very fast, the GC is very fast and go routines make concurrency fast.

2) The tooling is great. Once you have the Go CLI installed, everything else "just works." Cross-compilation is super easy. Install dependencies is easy. Generating code is easy. Embedding files is easy. The ecosystem is really mature. Modules are easy to create, export, and import. Compilation time is fast. The list goes on...

3) The generics system is amazing. It beats any system that implements generics with type erasure.

4) The language itself is very easy to read and write.

5) The language is relatively new, which means less outdated cruft. The language also puts an emphasis on having only one way to do things.

So comparing it to other languages:

- Python: Terrible tooling. Not performant

- C# Bad tooling (do I need .NET, .NET Core, Mono? How do I create a project? What is this crazy project xml file? How do I export my project to be consumed by others? How do I import a dependency?). Also, as an older language, it has a bit of cruft, e.g. it has optional types, but they also aren't required? Maybe if I knew more about C#, I would find it better. But using it with Unity didn't leave a great taste in my mouth.

- JavaScript: not performant, especially in multi-core environments.

- Rust: Rust is too hard to write. It's not worth the overhead if you can afford a GC.

- C++: Same as rust, + more foot guns and unsafe memory management.

- Zig: Don't have much experience with Zig, but I'd like to.

- Ruby: Not performant, not typed.

- Swift: Tooling is bad. Compilation is slow. Cross-platform is not a priority. Swift Package Manager is buggy. Using Xcode sucks.

- JVM languages: bad tooling (maven, sbt, gradle are all a pain), generics have type erasure.

Of course, there are reasons to use other languages. If you’re writing an iOS app, the tooling for Swift is best-in-class. Or if you’re doing ML, the ML-specific ecosystem in Python outweighs all other considerations.

The GC is low latency, but its throughput isn’t great. The key is avoiding the heap by mostly brute forcing trivial data structures on the stack (which is why you see so many repetitive O(n) loops).
Not sure generics are amazing. I wish I could do something like

addresses := persons.map(func(p Person) Address {p.address})

Actually what I really want is

addresses := persons.map(p => p.address)

But I understand Go doesn't allow that level of readability.

So you just want implicit returns?
I want to be able to define generics on methods, not just functions.
As someone who wrote scala and elixir for a few years and recently switched to a Go job I also dearly missed map and filter. However, people tend to loop unnecessarily often when it's so easy.

Say you have a list of classes and want to pull out certain fields. With immutability as default and easy map functions many people write something like this:

a = my list.map(e => e.foo)

b = mylist.map(e => e.bar)

This may or may not matter performance wise but I think Go has a strong culture of of making something like this easy vs writing a for loop that does everything in one go.

Yes, succinctness is almost never in Go's favor.
1) It has okay speed. Also, the GC is a very naive one, not “good” compared to other managed languages.

3) Besides C#, which languages doesn’t have typed erases generics? Most languages implement it through erasure. Also, Go’s generics are basic as hell..

4) Not sure about write, read.. deeply nested for loops, error handling taking up place everywhere, literally more verbose than even Java

5) it has plenty of hard-coded shit already, due to missing generics for a huge time (e.g. built-in data types)

Javascript is quite performant, but I have to agree on concurrency.

JVM languages: I don’t think it has bad tooling at all, and it is fucking performant (with much better GCs).

Why exactly is Go so special? For a compiled language it's not very fast and doesn't seem to be making much traction. I think people simply like it because of it's Bell Labs heritage and pretty syntax.
(comment deleted)
In no real order

1. wide range of OS's you can target

2. no dependency on libc, you can build and copy over a binary to a machine and it will work

3. fast compiles

4. amazing stdlib, makes writing no to low dependency code very attainable

(imo) there's very little reason to start a new backend project in ruby/python/js when you can just use go and get better performance

> doesn't seem to be making much traction

Its one of the most in demand languages, at least according to linkedin in my area.

> doesn't seem to be making much traction

What do you mean by this? By what metrics and what would qualify as making traction? Many companies use Go and many prominent and widely used open source projects are written in Go.

> it's Bell Labs heritage

I have never met a person (in person, offline) who has cared about this at all.

> pretty syntax

One of the most common things you hear as a knock against Go are things that explicitly do not lead to "pretty" syntax, such as its error handling.

Overall, your comment confuses me.

> > it's Bell Labs heritage

> I have never met a person (in person, offline) who has cared about this at all.

People care about it because the language has the Bell Labs "feel". They don't care about it the way a dog breeder would care about a dog's ancestry.

(And of those who care about the "feel", some view it as a positive, others as a negative...)

> People care about it because the language has the Bell Labs "feel"

I'm not attacking, I am honestly asking: What does that mean? That it feels C-like? Or something different? A lot of languages are C-like in syntax, so Go is not exactly unique in that aspect.

The belief that you can always improve some code by making it do less.

Contrast with e.g. Haskell which is based on the belief you can always improve some code by making it more general, or Rust on the belief you can improve it by making it safer, or Java that you can improve it by breaking it down into smaller chunks.

Languages carry aesthetic values. Most are aren't committed to them above literally all else - we all want our code to be somewhat simple and general and safe and isolated etc - but other than the most kitchen sink-y of them (C++, JavaScript) they have priorities among those which show through. (And even in those you can find some, muddled as they are.)

The difference between C and Pascal was a certain pragmatism. Pascal (at least the original) could make it hard or impossible to do certain things. C let you do things, even things that the language designers hadn't thought of.

The difference was that C was written by people who were trying to write an OS, and they had to create a language that was able to handle all the odd things involved, like writing directly to hardware. Pascal really didn't want you to do that.

I'm not saying that Go lets you talk directly to hardware - I don't know whether it does or not. But I think that Go has the "get out of the programmers way and let them write code" approach more than, say, Haskell, or even Rust. That (plus the C-like syntax) probably is the Bell Labs feel.

I don't think I've ever heard Go described as having "pretty syntax".
Your comment has been good at generating some discussion around what makes Go special.

Since the sibling comments already addressed everything else, I'll talk about this one:

> For a compiled language it's not very fast

This is essentially true, _but_ a lot of Go users are coming from Python, JavaScript, and maybe Ruby and Go is much faster. Additionally, it comes _close enough_ to Java while typically having a much smaller memory footprint that it really performs quite well in a cloud environment.

I never got the feeling it was slower than Java, but I've only used the standard JVM. In benchmarks, they don't really differ.

But the memory footprint is amazing. I've got three servers plus up to 10 test environments running on one small VPS (Virtual Private Server), and everything runs as smooth as can be; memory usage stays below 1GB, leaving enough space for the db server. In a previous job, we had a Java/Tomcat server, and it was filling up memory without doing much. I've inherited a Python server in my current job, and if you don't take care, it slowly grows to 4GB. Go is really a blessing for the back-end.

I was trying to be charitable given that I haven't seriously used Java in quite a while and have seen some indications that the JVM can be speedier (it _has_ had an incredible amount of optimization work done on it).

But, yeah, my experience with Go matches yours. It's really amazingly light on resource usage.

The reason the Tomcat filled memory is that for the longest time Java would only run GCs if you were actually running out of memory. The assumption was, if there's RAM there doing nothing then why would you waste CPU and electricity on cleaning up the heap. If you needed that RAM for something else, OK, tell the JVM there's a cap on how much it can use. It'll then do enough GC work to use that much (ish).

The problem is, that wasn't really well advertised or understood, so people would see a Java program using lots of memory and assume that this is how much it really required. The elasticity wasn't apparent. These days the JVM can kick off background collections from time to time to reduce memory usage if the app is idle. However it still won't aggressively collect if the app is running because, again, if you haven't capped it then it figures that it's better to serve requests than do lots of "pointless" GC work.

I think Go shows that that's a false dichotomy. Go can GC very quickly, almost without pauses, and the pauses are sub millisecond. That should be fast enough for almost all backends. Capping isn't great, although doable if you've got a steady workload.
The argument for the Java behavior doesn't depend on how long pauses are. It's purely about efficiency. If your runtime is collecting then it's not spending that CPU time on your apps workload. If you're GCing to reduce your heap from 300M to 100M when you have 16GB free, your server is running slower than it needs to because GC work is more efficient when clearing a lot of garbage from the heap.

Fundamentally with GC there's a throughput vs memory usage tradeoff. Go faces the same issue which is why GOGC exists:

https://tip.golang.org/doc/gc-guide#GOGC

> For a compiled language it's not very fast

…really? Go seems blazing fast to me…

It's fast at compiling, and it's decent-to-fast in performance. It doesn't have the best performance for a compiled language, though. That was an explicit tradeoff that the Go team made. Getting better performance requires implementing more compiler optimizations, which would slow down compilation. And compiler slowdowns are treated as regressions. Speed has improved though, over the years. [0]

1.17 saw a major performance boost because of the switch from stack-based to register-based ABI for go function calls [1]. The release notes claimed a 5% average improvement, but benhoyt (in that discussion) has benchmarks showing much greater improvement for GoAWK.

[0] https://go.dev/doc/faq#Why_doesnt_Go_have_feature_X

[1] https://news.ycombinator.com/item?id=28201732

It's a language with a dynamic feel, that isn't dynamic. It's 2 times faster than Java on average with half the resource usage in average.
Interesting to see many comments regarding "Go is my favourite programming tool, but not my favourite programming language". I totally agree. I love the tooling (go build, go fmt, etc.), the performance, the ecosystem... but the language itself is not the best out there. I would love a mix between Python and Go: Python as the language with Go's tooling. That would be amazing!
Maybe that's Nim (or Nim2)?
(comment deleted)
Write python and transpile to Go.
Go has been my default language for a couple years now. I don't use it for everything, but I use it for most things.

It makes some of the best tradeoffs I've ever seen, which is maybe the highest praise I can give as an engineer.

Anyone else feeling put off by golang? The syntax, the crazy error handling, etc. To me it's like taking a step back in programming, or actually 20 years back. Not better than Java (which is annoyingly verbose), maybe better than Pascal.
Java is in middle or to the end of userspace, it is verbose and less opinionated. Golang is very close to systems programming hence very opinionated, you can easily build API applications with it but that’s the most non system engineering you can get out of it. The error handling is probably the best design I feel from language that’s in category of c, c++
Golang tend to be used for application programming these days. Not only system works. Including today's top page article.
Yes, but in the end it's because people who like Go because they prioritize a certain set of features (language, community, ecosystem, etc), and those who prefer non-Go languages prioritize other features.

I don't like Go personally, but I have advocated for Go to be used in many situations depending on context. It's unproductive to start a language war here since it's generally situation-dependent.

Error handling at worst is an annoyance. The syntax? You will get used to it. Everyone has to adjust to every syntax whenever they learn a new language whether they realize they are or not.
I like Go's tooling, but I am consistently put off by the language. It's best described as "meh."
As someone writing all my code in Pascal, I feel offended

Pascal had exceptions for error handling and got generics before Go

I switched to Go in 2013.

I was tired of the dogma of wrong in PHP. Everyone was an armchair expert trying to tell me how wrong everything I was doing for the last 11 years as a professional was.

Go was a relief. At first I didn't get the hype, why were variable definitions the other way around? But then I took the tour and bam. Instant love and i learned so much about everything using it.

It's not perfect but good enough for my needs.

I feel like it's a language that is the product of a higher language, like swift. Because it's so primitive. I also find it easy to translate thought to code with Go.