Brian Lovin
/
Hacker News
Daily Digest email

Get the top HN stories in your inbox every day.

dualvariable

> if err != nil is the feature, not the bug. It forces you to look at every place something can go wrong and decide what to do about it.

No it really doesn't. It litters your code with if statements that are all just about the same, except that one that needs to be different, and you go blind looking at them all and can't spot the difference. And these days people probably just type "tab" and their LLM assistant fills out the entire block incorrectly in one keypress copying the pattern from everything else.

But LLMs didn't create that problem. Having to type something never meant you had to think about it, or thousands of "sudo shutdown -r now" commands would never have been run on production databases, because typing "sudo" would have magically made someone think about it, rather than just being keyboard memory.

And the problem of reviewing the code and spotting the one error handling block that should be different from all the others is always going to be there for human reviewers.

Rust converts the common case boilerplate down into one character: ? which lets you focus on any exceptional error handling rather than a wall of if statements that almost all look alike. And the compiler can see that you're ignoring a Result from a function call and force you to explicitly do something about. Plus you can then use a monad without knowing a single thing about monoids, endofuctors or category theory, and impress your friends.

potato-peeler

> No it really doesn't. It litters your code with if statements that are all just about the same

Wrong thing to be bothered with. This allows static analysis of every possible path the code can take. Try to do that with throw/catch. There is a reason many industry guidelines like misra, jsf, etc just outright ban hidden paths. They have been literally catastrophic. There is a reason why many modern languages like go, rust, etc, want errors to be explicitly handled.

Go documentation specifically reasons why they treat errors-as-values and why is it like that - https://go.dev/doc/faq#exceptions

> Rust converts the common case boilerplate down into one character: ? which lets you focus on any exceptional error handling rather than a wall of if statements that almost all look alike

Again, wrong way to look at errors. Errors are not a problem for a compiler to resolve. Errors are part of your business logic. They need to be explicit and handled. The syntax of one language vs another is not the point of error handling. Errors are not a distraction. It is your job to review each path the code can take.

Code may be written once, but code is always reviewed or audited or referenced more than once. No one is impressed if you write some meta magic one-liner. I would rather be more impressed if I can read your code and form a mental image on how many paths your code takes to achieve an outcome in a single sitting, instead of pinging you for a quick call to explain yourself.

> you go blind looking at them all and can't spot the difference

> And these days people probably just type "tab" and their LLM assistant fills out the entire block incorrectly in one keypress copying the pattern from everything else

Now, if someone is 'going blind' looking at error checks, its because their function is doing too much and has too many failure points. Perhaps one may need to focus on honing their software engineering skills on how to structure their code, instead of hitting that 'tab' more so often.

jesseschalken

Go has plenty of hidden code paths in defer, panic and recover. You can minimise the use of those things, just like in other languages you can minimise use of try-catch in favour of a Result sum type, which is already better than Go's "multiple return that you have to pretend is a sum".

potato-peeler

Which is what should be discussed about go, rather than if-err clauses. That is hardly an issue in go. But Go has these little quirks which, if not careful, can become a problem. But instead most go critiques discuss only the error handling syntax. Look at the other comments.

The article is on point on the strengths of go, and in many cases, those other languages you say, you will find it really hard to do the same thing with the ease with which you can do in go.

What the article glosses over, is the footguns with working with the language. No compile time nil checks, implicit interfaces, lack of sum types, like you said, which more so often leads to bloated structs, member visibility which is just private and public. You can find more.

But these problems were not the main focus of go. Go was build because google devs wanted a simpler way to manage and deploy their software across thousands of services, not having to wait forever for their code to compile, not worrying about writing cpp code in the BEST possible industry standard, ability to do parallel processing of hundreds or thousands of workloads without choking your cpu, managing dependency hell. These were actual roadblocks. And google was not the only one facing these problems. Go solved all of it. Which language was solving this before go?

nurettin

> No one is impressed if you write some meta magic one-liner.

I am very impressed if they can express the entire algorithm succinctly with a one liner. That's the kind of abstraction every piece of code should strive for and be shamed to the ground if they haven't.

> I would rather be more impressed if I can read your code and form a mental image on how many paths your code takes to achieve an outcome in a single sitting, instead of pinging you for a quick call to explain yourself.

Does this mean you're impressed by the ability to read go's if spaghetti?

potato-peeler

> if they can express the entire algorithm succinctly with a one liner > That's the kind of abstraction every piece of code should strive for and be shamed to the ground if they haven

This obsession with one liners is inversely proportional to professional experience.

treis

There's a lot of merit in this. I call Go the Honda Odyssey Minivan of the programming world. It doesn't do anything exceptionally well but it does lots really well and in a way that's simple and reliable. Especially for the backend serving react front end niche.

But it's also a pig to write and comes with a lot of foot guns. Especially the Null handling. Somehow they made it worse than every other language.

lanstin

Nilaway linter FTW here.

a012

Why the Null handling and in Go is worse than others?

oconnor663

Some weird nils don't compare equal to other nils. It's surprisingly easy to construct the weird kind by accident, because the conversion is automatic and invisible.

kevinmgranger

Another reason why it's worse is how it breaks logical laws thanks to the nil interface bug: https://go.dev/doc/faq#nil_error

Well, they'll call it a design decision, not a bug. I guess I'm being charitable.

treis

It sets primitives to 0, "", false etc. Which is almost always but not always fine. And if they're complex objects you still get NPEs

To get true nullable fields you need to use pointers. That's a whole topic in itself but they're awkward.

It's much worse than true nullable objects that your compiler can check for NPEs. It throws fewer NPEs but at the expense of data integrity where you don't know if your 0 is actually a 0 from the user or a null.

Lvl999Noob

What if Go went all the way? Referencing a zero pointer (nil) gives you the zero value of the pointed to type. If you try to access a zero map, it tries to deference the zero pointer to the underlying buffer. The zero pointer gives you the zero slice with zero length. The presence check fails without crashing and you get some pretension of reasonable behaviour.

MintPaw

I think there's a large subset of programmers now who consider null checking (or even the existence of null) to be bad, and prefer something else like exceptions or option types. I don't get it personally.

chuckadams

The existence of null is not the problem, it’s when null populates every single non-primitive type, making every access into a logic bomb unless explicitly checked. When null is a distinct type, there’s no problem at all.

thomascgalvin

I like go, but a lot of little things stop me from loving it.

Like, enums. I get a lot out of the box when I use an enum in Java or Kotlin. Converting to/from a String is trivial. Type safety ... exists.

I can do that in Go, but I have to hack it in, for every single enum type I want to represent. Enums are not a thing in the language, which means its easier to keep the language in your brain all at once, but at the expense of making it harder to keep the software I'm writing in my head. Is this "enum" the same as that "enum"? I have to go read the code to figure it out.

But Go is excellent at a lot of things. Compile times, static binaries, resources compiled right into that binary, execution speed ... there is a lot to love.

nick_

The convention of explicit error handling after every method call is absolutely bonkers to me. I would never use it for anything serious.

therealdrag0

I recently vibe coded a large app in Go, it’s so mind-numbing to read. Like half the code base is error handling.

nick_

The anti-exception mind virus of the 2010s did a lot of damage. Go designers finally caved and added panic & recover, but jeez, the damage was done. The whole ecosystem has exception derangement syndrome.

ntrianta90

I really wish they had added Enums instead of the stupid generics.

pjmlp

I would already be happy with enumerations Pascal style from 1976, without the const/iota dance.

codegeek

I love Go. But I prefer .NET for web development that also compiles to a binary and has a great ecosystem of libraries and packages. Go is great if standard library works (and it can for many cases) but when you need to start looking into non standard libraries, Go can hit limitations.

For example, to build a full production web application with database in Go, there is no great out of the box migration tool. There are some good 3rd party libraries of course but compared to something like EFCore in .NET, they don't come as close.

For me, it is now .NET and then Go. Of course, I use Go when just doing a lot of non web stuff as well.

bronlund

Comparing Go and .NET is like comparing a Honda with an aircraft carrier with wheels.

MarkSweep

Eh, at least for people consuming .NET apps compiled with NativeAOT, the experience is similar. Applications can be compiled as a single file with no dependancies. A hello world in .NET is half the size of one in Go:

https://github.com/MichalStrehovsky/sizegame

cloudfudge

> that also compiles to a binary

"compiles to a binary" is not a useful criterion. The criterion Go is winning on is "compiles to a single, completely self-contained binary," meaning it does not depend on libc or any external runtime. You can't say that about .NET. You can't say that about damn near any other programming language. It's extremely rare. The fact that .NET uses a binary packaging format is, like... well ok, so what?

kevinmgranger

.Net can compile to a self-contained binary: https://learn.microsoft.com/en-us/dotnet/core/deploying/nati...

cloudfudge

That isn't truly self-contained. It still relies on libc.

0xbadcafebee

Since AI coding, I've switched 90% of my code to Go. It's really great for most things. Lacks a development community large enough to have a really solid UI framework, but the existing frameworks are "good enough". I used AI to make an AI agent that works on Windows, Linux, Mac, iOS, and Android, with CLI, GUI, and web serving. LOC: 5575. Binary size: 35MB.

Also: why can't we vouch for flagged stories now? This post is actually good, and funny, and the conversations are worth having.

undefined

[deleted]

xnorswap

I can't see any reason this list why I should use Go over C# / .NET.

.NET has almost all these upsides, but with a concurrency model (async/await) that is (now) more transferable to other languages.

shantnutiwari

Agreed-- c# has a lot of these advantages and is a lot easier to write (yes, I know this will depend). Plus it has a much larger ecosystem

The only thing I can think of: I dont think c# can compile as easy to a single executable binary, like Go (or even rust)?

xnorswap

It's had support for single exe compilation for a while now, although the file sizes can get large without being more careful about dependencies.

stackskipton

.Net SRE here, you can do self contained executable but there is some foot guns there and if you are doing containers already, I'd just skip it.

2ndorderthought

I can see reasons why people don't want to use .NET if Go is available. .NET has its merits but it's bloated, compilation is slow, and I find it's tooling to be really annoying.

For me go is just above c# and both of those are not super high on my list.

xnorswap

I've never understood what is meant by "bloated", would you mind explaining, so perhaps I could better understand?

If it's "Large standard library", I think that's a selling point. Having anything you need available ( although these days, via optional microsoft.* packages ) helps keep projects consistent between different places.

If it means "Different ways to do the same thing", I can understand that criticism better, and some of that comes with 20+ years of legacy, where the wrong thing was done, and now there's a better way, but a ruthless adherence to backward compatibility means that the old way isn't dropped.

2ndorderthought

The .net sdk is like 1gb, go is like 60mb or something. It's annoying when that matters.

More so, nothing happens quickly with it's tooling, and the tooling isn't friendly. All of a sudden I can't build my project in vsstudio(also bloated but admittedly optional but may be required depending on where you work). Clean build also fails. So I have to go to the command line and type in dotnet restore, dotnet build, magic it works now. ???

Okay time to install a package because msft has its own packages for things like aspnet but now I want to serialize json. Cool newton soft sounds good. Ah now I need a package manager, I'll install nuget. Oops I need to tweak this weird xml file with lots of options.

I press compile I wait 3 minutes to realize there is an error. Okay it builds and looks good in debug mide now I want to send my application to a friend who doesn't have .net runtime...

With go it's more like. Okay I automatically have web access from the std lib. I want a framework oh I already have a package manager. Edit my toml. One command done. Time to compile, poof done in 2 seconds. I send the binary to my friend and they run it.

I understand some of these are 1 time cost things, but I am requesting you to read between the lines as these aren't examples I am trying to quibble over. The point is the friction that go has focused on removing by being quick and small. It has less legacy cruft but I tried to ignore as much of that as I could.

Also keep in mind j am not a big go fan. It's not my favorite language but it is way easier to deal with on a regular basis for me

pjmlp

I only have one reason, devops ecosystem where Go is expected nowadays, like plugins for something.

Otherwise Java, .NET, Typescript (with possible C++ addons).

pjmlp

Yeah, it breaks when the author decides to move from Github into Gitlab to protest against Microsoft.

Time to update all code references to Gitlab across the globe, in every single Go project.

Or spend the time configuring redirects between URL mappings, across everything that depends on it.

Not to mention that except for lacking garbage collection, even Turbo Pascal 7 for MS-DOS was more modern in language features, with faster compile times, on a 20 MHz powered computer.

worldsayshi

I often think of go as a "better" python. As in, easy to learn and easy to use. But also performant and the module system and package manager seem to be a little neater. (sorry for flamebait)

But I wonder how well it can cover similar use cases? Go is great for devops and web backends. But what about AI and data science?

jochem9

I used to do lots of data engineering in Python, then started doing all kinds of engineering in primarily Go.

The Go ecosystem for data is very limited. There are no widely supported dataframe libraries (like the og pandas and the newer polars written in rust and also available as a crate). Very few data science libs, a few decent gen AI libraries, but not as popular as their Python cousins.

Most of the work I do now is streaming data and very small batches. For that Go is amazing. I don't need dataframes to transform a json, combine it with a bunch of other data and write to a database. I just need to write that logic and make it go fast. Very easy in Go.

chubot

If Go interfaced with C as well as Python, I’d use it a lot more.

But I’m using the slower language because it still integrates with more things

For example, one reason AI is all in Python is because CUDA is basically part of the C ecosystem (ie build system)

impulser_

Have you tried purego?

You can just embed the C library into the binary of the Go app call it directly in Go. Most of the time I have found calling C from Go faster or on par with calling C from Python.

https://github.com/ebitengine/purego

2ndorderthought

Go is fine for simple applications especially backend ones that connect to the internet. I do prefer it to node/js/ts/etc.

I do think a lot of projects would be better served having been written in go instead of java, or whatever else.

I don't think it's a panacea for anything. It's pretty easy to shoot yourself in the foot with. The easy stuff is easy the hard stuff is really hard.

I like rust a little more, and I don't rewrite things with it. I choose it first. That's my preference but go ahead and gopher on.

jesseschalken

The single executable deployment hasn't been an advantage for a very long time. C# has Native AOT. Java/Kotlin/Scala have GraalVM native-image, plus Kotlin Native and Scala Native.

And of course there is Rust, which compared to Go has better performance, lower memory usage, smaller binaries, safe concurrency, safe resource handling, and a real type system with tagged unions.

The Go team built good tooling around a mediocre language. Now most other languages have caught up, and Gophers are left writing mind numbing `if err != nil` checks with no benefits left to show for it.

shantnutiwari

Why is this flagged? Because of the swearing? Cause there have been previous posts with swearing in them? Or do people in general hate Go so much?

I know the OP said dont use external libraries, but I love bubble tea (And their related libs), they are a great reason to use Go for TUI

that said, I only use Go for hobby projects, I dont know how good it feels if you have to use it for work 40 hours a week

chuckadams

Maybe because the style is just worn-out ragebait trolling now.

mattgreenrocks

I am so over the presumptive blog post tone personally.

indigodaddy

It's likely being flagged because people believe it's more AI-written than not

shantnutiwari

Let me copy paste my other comment:

"This is getting really fucking irritating. Every 3rd comment on every HN post is "This is LLM", which has become a proxy for "I dont like it so it must be llm"

JKCalhoun

Yeah, I suppose I am in the minority here, but I still judge a posts on their merits.

LLM witch hunts are tiresome.

(Perhaps the worst part too is that an accusation can be leveled and there's no way to counter. If it's a vacuous fluff piece, say so—irregardless of who/how it was written. And there we are judging a piece on its merits…)

nojvek

This one has major signs of LLM speak, specifically Claude.

Normal humans who rant like, actually rant. This is AI generated rant.

killbot5000

I love go, but I find it did little to make concurrency management easier to reason about. Race conditions are easy to write. Go routines have all the same concurrency problems of threads.

In the parallel HTTP fetcher, the error is discarded. This will likely result in a panic when the response is nil. Also, what if it a server locks up? Or the underlying socket never connects and never times out?

I know it’s a toy example, but one must consider all these things in a real system. Go does have good pathways for these concerns, but it’s also easy to do it wrong. I still have to manually reason about access to variables/struct fields from multiple go routines.

lanstin

I find I have to design servers from a data flow view; when my performance demands let me do everything via channels to worker goroutine pools (including sending a response Channel back to the request handler) I don’t have to do any locking at all. I find when I have to add in mutexes it means I haven’t thought enough about the problem (or I am fixing some hot spot from the profiler, rarely).

Daily Digest email

Get the top HN stories in your inbox every day.