Hacker News new | ask | show | jobs
by usrbinbash 951 days ago
> ignoring decades of programming language

True, and because of this, the language can be learned over a weekend or during onboarding, new hires can rapidly digest codebases and be productive for the company, code is straightforward and easy to read, libraries can be quickly forked and adapted to suit project needs, and working in large teams on the same project is a lot easier than in many other languages, the compiler is blazing fast, and it's concurrency model is probably the most convenient I have ever seen.

Or to put this in less words: Go trades "being-modern" for amazing productivity.

> It always feels like a regression when switching from rust to go.

It really does, and that's what I love about Go. Don't get me wrong I like Rust. I like what it tries to do. But I also love the simplicity, and sheer productiveness of Go. If I have to deal with the odd nil-based error here and there, I consider that a small price to pay.

And judging by the absolute success Go has (measured by contributions to Github), many many many many many developers agree with me on this.

10 comments

It's funny how I always hear the point about new hires for Go. My team is a Rust shop at $DAYJOB that I created basically from scratch, so I had to onboard every new hire on the codebase. It's amazing how confident they are due to the compiler having their back, and how confident I am their code won't blow up that much in prod.

> code is straightforward and easy to read

I have to disagree. I don't want to read 3 lines out of four that are exactly the same. I don't want to read the boilerplate. I don't want to read yet another abs or array_contains reimplementation. Yes it's technically easy to read, but the actual business logic is buried under so much noise that it really hinders my capacity to digest it.

> the compiler is blazing fast

much agreed, that is my #1 pain point in rust (but it's getting better!)

> and it's concurrency model is probably the most convenient I have ever seen

this so much. this is what I hate the most with go: it pioneered a concurrency model and made it available to the masses, but it has too many footguns imho. this is no surprise other languages picked channels as a first class citizen in their stdlib or core language.

> Go trades "being-modern" for amazing productivity.

I don't think those two are incompatible. If we take the specific point of the article, which is nil pointers, Go would only have to import the sum types concept to have Option and maybe Result as a bonus. Would this translate to a loss of productivity? I don't think so. (oh and sum types hardly are a modern concept)

Also, there may be a false sense of productivity. Go is verbose, and you write a lot. Sure if you spend most of your time typing then yes you are productive. But is it high-value productivity? Some more concise languages leave you more time to think about what you are writing and to write something correct. The feeling of productivity is not there because you are not actively writing code most of the time. IIRC plan9 makes heavy use of the mouse, and people feel less productive compared to a terminal because they are not actively typing. They are not active all the time.

> Also, there may be a false sense of productivity. Go is verbose, and you write a lot. Sure if you spend most of your time typing then yes you are productive. But is it high-value productivity? Some more concise languages leave you more time to think about what you are writing and to write something correct. The feeling of productivity is not there because you are not actively writing code most of the time. IIRC plan9 makes heavy use of the mouse, and people feel less productive compared to a terminal because they are not actively typing. They are not active all the time.

This is my sense. "False sense of productivity" is an accurate statement - I've also found that it seems to be for a very specific (and not necessarily useful) definition of "productive", such as LOC per day.

It's not as bad as dynamic languages like Python, but very frequently Go codebases feel brittle, like any change I make might bring down the whole house of cards at runtime.

> It's funny how I always hear the point about new hires for Go. My team is a Rust shop at $DAYJOB that I created basically from scratch, so I had to onboard every new hire on the codebase. It's amazing how confident they are due to the compiler having their back, and how confident I am their code won't blow up that much in prod.

Same. Started a company, onboarded just about everyone to Rust. It went very well.

>It's amazing how confident they are due to the compiler having their back, and how confident I am their code won't blow up that much in prod.

I get what you're saying, and I'm glad you are having such a good experience with it. Disclosure, I am not talking down to any language here...in fact I actually like Rust as a language, even though I don't use it professionally.

I am just saying that Go is incredibly easy to learn, and I don't think there are many people who disagree on this point, proponent of Go or not.

> I have to disagree.

We'll have to agree to disagree then :-) Yes, the code is verbose, but it's not really noise in my opinion. Noise is something like what happens in enterprise Java, where we have superfluous abstractions heaped ontop of one another. Noise doesn't add to the program. The verbose error handling of Go, and the fact that it leaves out a lot of magic from other languages doesn't make it noisy to me.

> I don't think those two are incompatible.

Neither do I, but that's the path Go has chosen. It may also have been poorly worded on my part. A better way of putting it: Go doesn't subscribe to the "add as much as possible" - mode of language development.

> But is it high-value productivity?

Writing the verbose parts of go, like error checking, isn't time consuming, because it's very simple...in fact, these days I leave a lot of that to the LLM integration of my editor :-)

Is is high value? Yes, I think so, because I don't measure productivity by number of lines of code, I measure it by features shipped, and issues solves. And that's where Go's ... how do I say this ... obviousness really puts the language into the spotlight for me.

I honestly have started to wonder if the popularity of languages like Go and JavaScript are due to the lack of features in the base language. JavaScript in particular has had an incredible amount of effort invested in creating fairly limited, scattershot, and duplicative support for features that are "just part of the language" in Kotlin, or Rust, or even Java. It makes a really rich field for people who are interested in compilers, but did all this really make Uber a better company? Would all that effort have been better spent solving problems core to their business?

At my employer we have a pattern of promoting people who have done things like write a proprietary application gateway. The dev got a couple promotions and moved on to another company and we got stuck maintaining a proprietary application gateway with a terribly messy configuration and poor observability.

> it pioneered a concurrency model and made it available to the masses

Isn't it basically just what Cilk did, but with fewer feaures?

I think that it’s not just about something being possible with a tool, but people actually using it.

Go, for whatever reasons, has gotten people using it.

This is the core of the problem. Of course you can learn the language in a weekend, but you're bound to make the same mistakes developers have been doing for decades.

This may be ok, as you say, if you allow errors here and there because you are fine dealing with those problems. But at the other end, it may be a user that is affected by the error. Which may be ok as well, but why should it be? We lament the quality of software all the time.

Compare this to other engineering fields: unless you study the knowledge of those who came before you may not even be allowed to practice in the field. I would not want to use a bridge built by someone who learned bridge building in a weekend.

Software is different though, it's rarely a matter of life or death. Given that, maybe it's ok to not have the highest quality in mind, because the benefit of productivity far outweighs the alternative.

I'm torn.

Go is just making a certain set of tradeoffs. If you try to fix all the "mistakes developers have been doing for decades", you get Rust. And considering that Rust is already Rust, there is not much point in trying to make Go another Rust.

The line has to be drawn somewhere. I think everyone has certain things they'd put on the other side of that line, and strict nils are probably at the top of the list for many, but overall it's good that the Go team is stubborn about not adding new stuff. If they weren't, maybe there would be better nil handling, better error handling, etc. but compiles would also get slower and the potential for over-engineering, which Go now discourages quite effectively, would increase. At a high level, keeping Go a simple, pragmatic language with a fast compiler is more important than any particular language feature.

The argument is that Go is making a _wrong_ set of trade-offs.

It was designed, specifically, as per Rob Pike, for _bad_ developers. Developers who couldn't be productive at Google because they weren't properly taught at unis [0].

Then it caught momentum and then here we are, discussing a bad language designed for bad developers as if there is nothing better we can do with our lives.

[0] https://news.ycombinator.com/item?id=16143918

If you think developers at Google are bad and weren't taught fundamentals then we live in different universes.

Pike's point is that peak PLT is too lofty to be productive or even useful for folks who are actually technically competent and literate relative to the rest of the industry. No one will get anything done if they're spending all their time teasing an advanced type system into inferring the required program.

How does it apply to this specific issue? What is so lofty and unproductive in proper null handling?
I did not suggest it applies to this specific issue, I replied to a comment containing inflammatory remarks, but I'll bite: To answer that you need to first produce the minimum change to the language that provides this functionality.

A solution might be optionals, which might require sum types, which might require generics (which Go just learned), which most definitely requires a more complex type system, which almost certainly involves longer compiler times.

Is that all worth it? I don't know. The Go team certainly didn't think so.

Languages that I'm aware of that do solve this are Scala, Rust, Kotlin to some extent, Haskell... languages which do not have a reputation of being stable, easy to learn, easy to read and understand, compile quickly, etc.

Nothing, the question is whether it is important enough to be included in the language.

Just because person A thinks this is hugely important, doesn't mean person B has to agree, or that B is a bad developer.

> It was designed, specifically, as per Rob Pike, for _bad_ developers.

Mind showing us the source for that?

Go wasn't made for incompetent developers. I'm fairly certain that people who land a job as devs at Google are pretty competent.

Go was made to facilitate rapid onboarding, easy digestion of large codebases, and working efficiently in large teams where people are guaranteed to have widely different educational backgrounds, experiences and ideas about programming.

That's why the language has to be simple, obvious, and be focused on readability. That's also why Go is strongly opinionated.

Here you go: https://www.youtube.com/watch?v=uwajp0g-bY4

I think Go could still work without completely neutering the type system.

Algebraic Data Types and pattern matching are not difficult... python already has them.

I don't think anyone is suggesting that Go should be like Rust. It's too late for that. We're suggesting that people should just use Rust (or Haskell, or F#, or any other robust functional programming language) instead.
> We're suggesting that people should just use Rust (or Haskell, or F#, or any other robust functional programming language) instead.

And how well did that work out for Haskell?

https://gist.github.com/graninas/22ab535d2913311e47a742c70f1...

Just because one person thinks Functional Programming is the right way to do it, doesn't mean another person has to agree. The same goes for every paradigm, and language feature under the sun. Different people want different things, different projects have different needs.

No single language gets everything right, no single paradigm solves every problem, no feature is a "must have" in every language. A functional approach might be great or a productivity killer depending on the use case. A GC may be the best thing in the world or a performance nightmare. OOP may be a really good idea or a path to unmaintainable crap depending on the implementation.

There are no silver bullets.

The only thing that is ABSOLUTELY certain: When people get told "Our way is better, you should use our way", despite the fact that there are no silver bullets, people will resist. And that resistance can lead to languages vanishing into obscurity.

> And how well did that work out for Haskell?

You know this post is speculative fiction, right? It's actually about what could kill Haskell, not what could kill Rust?

> It's actually about what could kill Haskell, not what could kill Rust?

Here is the articles title: "What killed Haskell, could kill Rust, too"

So no, it's not about what could kill Haskell. In 2022, ~0.3% of all code pushed to github was Haskell. To put that number into perspective: vimscript was ~0.25%

> And judging by the absolute success Go has (measured by contributions to Github), many many many many many developers agree with me on this.

Yeah, I truly hate this field

PHP was once an extremely popular language too...
Famously something you could learn in a weekend. It allowed you to start being productive right away, even if you were writing terrible insecure code!
> And judging by the absolute success Go has (measured by contributions to Github), many many many many many developers agree with me on this

You can't make up that other devs' opinions / preferences are identical to yours just because they use the same language, there are other important factors in play (e.g., if your company is using Go, then you'd be more productive in it and be more likely to choose to contribute in it even it Go is less productive as a language)

It could have done both though. It could have explicitly nullable types like Kotlin/C#. Or sum types like zig/rust/Swift. That wouldn’t make the language more complex to learn.
By definition, every bit added to a language makes it more complex to learn.

Sure, it could be done. Lots of things could be done to Go. The people who invented it are among the most brilliant computer scientists alive. It's a pretty sure bet that they know about, and in great detail, every single thing people complain Go doesn't have.

So every thing that is "missing" from Go isn't in it for a reason.

"Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away." -- Antoine de Saint-Exupéry, Airman's Odyssey

Not at all. Otherwise brainfuck would be the simplest language to learn. How do you currently represent a type that is A or B in Go? You have to use an interface. That’s much more complex than using a sum type would be.
Well, Brainfuck is simple to learn. The entire specification fits comfortably on a single page. Simple to learn doesn't automatically imply simple to use for any given purpose. The same is true for Go.

> You have to use an interface. That’s much more complex than using a sum type would be.

More complex how and by what metric?

> The entire specification fits comfortably on a single page

But to understand the specification and how it can be used to do a programming, you need to have at least a cursory understanding of turing machines and related theory, which isn't necessary to learn Java or python.

Under your definition, the conceptually simplest language is something like SUBLEQ, (the specification is only a single line!) but in this case, being able to implement the language, and learning the language aren't the same thing. Learning the language generally means, like, useful for given purposes.

It's definitely not as simple as "More features = harder to learn".

Removing footguns (nulls are a footguns, race-able concurrent APIs are a footgun) can make it easier to learn even though this may introduce new features (in this case sum types) to solve the problem.

It is not like Oberon, Plan 9, Inferno and Limbo were such a huge commercial successes.

Had those brilliant computer scientists not been employed at Google, it would have been another Oberon or Limbo.

Yes, having a good backing by a huge entity is a bonus.

But it's not a guarantee for success either. Google+ anyone?

Indeed, yet it helps a lot, as proven by all wannabe C and C++ successor languages.

The only ones that are actually taking off, have the baking of major corps, even if it is only giving money into the project.

> True, and because of this...

This is a false dichotomy. One does not imply the other.

Go is also not a simple language. It is deceptively difficult with _many_ footguns that could have easily been avoided had it not ignored decades of basic programming language design.

Many things also aren't straightforward or intuitive. For instance, this great list of issues for beginners: http://golang50shad.es/

I'm sorry but nearly all of them are along the lines of "I came from language X and in X we did it this way, but Go's syntax is different". That's not a footgun.

You know what's a footgun? Uncaught exceptions popping up in places far away from where they were created at which point you have very little context to deal with it robustly. Use after frees. FactoryFactoryFactories.

I don’t have too much an opinion on either side here, but as a developer who works full time in Go (and has for >6 years) all these things exist in Go.

Uncaught exceptions -> panics, like what this nil catcher is aiming to solve

Places far away -> easy goroutine creation with no origin tracking makes errors appear sometimes very far away from source

Use after free -> close after close

FactoryFactoryFactories -> loads of BuilderFunc.WithSomething

Lots of other pains I could add that are genuinely novel to Go also, but funny that for everything you mentioned my head went “yep, just called X”

> I'm sorry but nearly all of them are along the lines of "I came from language X and in X we did it this way, but Go's syntax is different". That's not a footgun.

You're right, I meant to link that in reference to how Go can be difficult to learn despite how it simple it seems. Not sure how I a sentence.

The overview of that site explains its purpose/necessity quite well. Some things are footguns, many are just confusing time-wasters. Nevertheless, they are frustrating and hamper the learning process.

> Nevertheless, they are frustrating and hamper the learning process

But that is the learning process. What else is there to learn in a language if the syntax doesn't count? They're all Turing complete and all of them can do everything. All we need to do is learn the exact magic words.

I never said otherwise. My point is that Go is far harder to learn than they're implying. It certainly can't be learned over the weekend — well, maybe it can be, but the code you end up writing will Inevitably be full of resources leaks, panics, nil pointer issues, improperly handled errors, etc. You may be able to put together some basic logic, but you are far from understanding the language.

I don't think it's honest to parade Go as a language that's the paragon of simplicity that's easy to learn when that's simply not true. I also don't think it's honest for people to argue that addressing any of Go's countless warts would somehow make the language more complex or harder to learn.

I agree that it's very unlikely for someone to learn Go in a week and start writing flawless code.

But Go's real strength is in its readability, not writability. I think it's very much possible to learn Go in a week, then read clean Go code like the standard library and understand exactly what's going on. At least that's my interpretation of what it means for a new grad to be productive in Go in less than a week. Nobody is expecting someone new to write production-grade libraries with intricate concurrency bits in their first week, but they're already productive if they can read and understand it.

As a rule of thumb we spend 10x more time reading code than we do writing it (code reviews, debugging, refactors). So why not optimise for it?

> This is a false dichotomy. One does not imply the other.

No it isn't, and yes it does. By definition, the more features I add to something, the more complex it becomes. So yes, Go achieves it's simplicity precisely by leaving out features.

> this great list of issues

I just picked three examples at random:

"Sending to an Unbuffered Channel Returns As Soon As the Target Receiver Is Ready"

"Send and receive operations on a nil channel block forver."

"Many languages have increment and decrement operators. Unlike other languages, Go doesn't support the prefix version of the operations."

All of these are behavior and operators that are documented in the language spec. So how is any of these a "footgun"?

> No it isn't, and yes it does. By definition, the more features I add to something, the more complex it becomes. So yes, Go achieves it's simplicity precisely by leaving out features.

More complex for whom? Not having generics made the compiler simple, but having to copy and paste and maintain identical implementations of a function (or use interface) adds more complexity for users.

Similarly, adding a better default HTTP client arguably makes Go more complex, but the "simple" approach results in lots of complexity and frustration for users.

> All of these are behavior and operators that are documented in the language spec. So how is any of these a "footgun"?

Perhaps I could have been clearer. I didn't mean that the entire list was of footguns, just that there are lots of confusing and unintuitive things beginners need to learn.

Some actual footguns off the top of my head:

- using Defer in a loop

- having to redeclare variables in a loop

- having to manually close the body of a http response even if you don't need it

Not only that but those behaviors are patently not footguns or unreasonable in any way
> "Send and receive operations on a nil channel block forver."

I literally can not imagine a worse behavior than my program blocking forever. Of all of the things my program can do, short of giving remote code execution, blocking is literally the worst one I can think of.

Goroutines blocking is normal procedure in a a Go program. It also isn't a problem, unless my code is broken and allows all Goroutines to block simultaneously...in which case the runtime automatically terminates the program with a Deadlock-Panic.

The behavior of nil-channels always blocking is on purpose, and tremendously useful in functions where we receive from multiple channels via `select`. It allows the function to easily manipulate it's own receive behavior simply by setting the local reference to the channel to `nil`.

Since `selects` can also have `default`, the resulting functions don't have to block either.

> No it isn't, and yes it does. By definition, the more features I add to something, the more complex it becomes.

Yes, and go opts to include features that unnecessarily increase complexity in this manner, such as nil values.

> All of these are behavior and operators that are documented in the language spec. So how is any of these a "footgun"?

By this logic, no language with a spec can have footguns. C and C++, notorious for their footguns, both specify their behavior in the spec, so do they not have any footguns?

We’ve found that something like 50k+ LoC Go projects become impossible to maintain.

“Optimising your notation to not confuse people in the first 10 minutes of seeing it but to hinder readability ever after is a really bad mistake.” — David MacIver

Go is a simple language that anyone can pick up in a weekend, but productivity plateaus once you’re doing anything that requires hard constraints or complex systems (the same is true for JS, Python, and other scripting languages).

As someone who is ~3 months out of uni, I don't really understand this amount of concern towards "new hires". I've interned at a place where I was thrown head-first into a industrial-grade Java codebase. The initial few weeks were rough. There were complex class hierarchies, patterns that weren't documented but had to be followed, interfaces from 10 different packages, really hard to grok names, magical DI, Lombok, complex mock testing all while 10 different linters and code coverage tools yell at you. But I cut through the complexity and finished my task in the end, not through any particular skill but by being stubborn and willing to learn.

None of the Google fresh hires I know personally are stupid. They are talented people who could be just as productive in a C++ or Java codebase. Maybe even better when you have features like Java's streams or C++ templates to throw at non-trivial problems. They might need more time, but it's something easily budgeted for. If new hires have to be productive from the first day, that's a problem the company has created and not the employee. If other languages have too many ways to do something, just enforce only using a few of them, teams have and continue to do that.

I use Golang in my current job. The library ecosystem seems fine. But even as a "new hire", the language frustrates me sometimes. Go's concurrency is "easy", but has a minefield of problems. Just off the top of my head, for-loop semantics [which to Golang's credit, is being fixed but it is absolutely a breaking change], just being able to copy a mutex by accident. These are bugs I've written and not had fun tracking down. In a year I'll have all these footguns memorized, but I could also have spent a year getting better at any other language. Even at my experience level, the Rust compiler gives me enough grief for me to know that when it's happy, whatever I've written will work. Nothing about Golang gives me that confidence.

> There were complex class hierarchies, patterns that weren't documented but had to be followed, interfaces from 10 different packages, really hard to grok names, magical DI, Lombok, complex mock testing all while 10 different linters and code coverage tools yell at you.

And this is exactly what Go avoids, in my opinion and experience.

Unless someone shoehorns Go into an Enterprise Java style (which, sadly, is possible, and sometimes done), the problems you listed with the Java codebase either don't exist in Go, or are orders of magnitude less painful to deal with, even in large Go codebases. Plus, the toolchain is pretty obvious, because most of it ships with the language.

And while my argument mentions new hires specifically, because the impact is most visible with them, this is just as important for mid and senior level developers; yes Go is sometimes more verbose (although enterprise Java and lots of C++ code still runs circles around Go in that regard) than it's contemporaries, it is also obvious. There is little magic, there is little action at a distance, and the opinionated style of the language discourages superfluos abstractions.

I have used quite a few languages so far in my career. Go is the first where I was able to comfortably read and understand std library code within the first week of learning the language.

> Just off the top of my head, for-loop semantics [which to Golang's credit, is being fixed but it is absolutely a breaking change],

It is technically a breaking change. Practically, it isn't, because there simply are no examples of production code in the wild that rely on this unintuitive behavior (As mentioned multiple times in the discussion on Go's issue tracker, the dev team did their research on that), and code that implements the (very easy) fix, will continue to work after the upcoming change.

> Nothing about Golang gives me that confidence.

My experience is different. I know that most of the problems the Rust compiler complains about will be handled by the fact that Go is GC'ed, and most of the rest I avoid by relying on CSP as my concurrency model (Can't accidentially copy a mutex if there's no mutex ;-) )

If a tool is any good I’m going to be using it for years. I’d much rather spend more time studying reusable ways the language can help me solve a problem with less code; that’s what’s productive. A quickly learned language is like a nearly empty toolbox.
From witnessing so many HN flamefests, between Go and Rust, it seems there are people who are genuinely more productive with Go than Rust, and vice versa. Not saying people usually lie when they claim to be more productive with one, but rather that their judgement is very subjective and not scientific. I do wish Go didn't have the data race bugs, though. It greatly weakens Go's "fearless concurrency" selling point for me. To me, Rust doesn't always make concurrency "fearless" in terms of complexity, but at least I'm not fearing actual memory safety bugs in a random library. There's unsafe, but I think the design tradeoff there is quite reasonable and workable.