Hacker News new | past | comments | ask | show | jobs | submit login
Ask HN: Go programming language is over ten years old. What do you think of it?
192 points by asim 40 days ago | hide | past | favorite | 300 comments
What are your thoughts on the Go programming language?

Its a great programming language that is helpful to solve problems without wasting time.

I really appreciate the simplicity, I think it totally makes sense at work where not everyone wants to deal with weird code. I think maintaining old golang code will not be a major problem in the future for example. The garbage collector make things a lot simpler too.

The dependencies management is not great compared to npm/crates/rubygems/... but it's still better than the Java world and it exists and it's being used.

I wish generics were already there, and I'm not a fan of the interface{} keyword. I'm also sick of the if err!=nil everywhere, I wish it could have a better error management like Haskell/Elm/Rust/...

I use Rust in my latest personal projet and while I prefer Rust, it's a bit too complex quite often and I spend more time thinking about rust things than thinking about the problem I'm solving.

This is not the case with Golang. It's not a fun programming language but it does most jobs very well.

Go is carefully designed to be adequate for the large majority of problems that don't need much, meant for use by the large majority of programmers who don't know or need much, whose attention is elsewhere. It is hard to get too deep into the weeds by accident. It builds fast, so programmers who code by successive approximation, making more or less random changes until something works can get there quickly. It runs pretty fast, so programs usually don't need much optimization attention.

It competes in the same niche as Java, but lacks Java's complications that, more often than not, are purely obstacles.

Before Go, before Java, there was Visual Basic. Visual Basic coders didn't go anywhere. Go acknowledges and welcomes them.

Yes, what Go failed to acknowledge was how those decisions impacted Java and Visual Basic language evolution and actually learn from them.

Go's designers have very different goals from others. They did not set out to design a language they would, themselves, like to program in. They meant to make a language that very distracted programmers would still be able to get things done using, and other distracted programmers could read and maintain.

Java also was not a language its designers wanted to use. They would rather code Lisp.

>Go's designers have very different goals from others. They did not set out to design a language they would, themselves, like to program in. They meant to make a language that very distracted programmers would still be able to get things done using, and other distracted programmers could read and maintain.

They never said that. They said they wanted a language for programming at Google scale. But their preferences and likes are all in the language...

Actually they said something else,

"The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt. – Rob Pike 1"

"It must be familiar, roughly C-like. Programmers working at Google are early in their careers and are most familiar with procedural languages, particularly from the C family. The need to get programmers productive quickly in a new language means that the language cannot be too radical. – Rob Pike 2"




According to them, Java (a language that was designed with the same cog ideas as Go) is already worthy of PhD level.

Ironic, when they needed the help of well renowned PhD researchers that were part of Project Pizza, for finally adding generics.

They said what I said, but perhaps more diplomatically.

Still no excuse no to learn from the mistakes of others, Go has already so much "we don't do that here,...., ok we really should support that" that will eventually be just yet another Java, but hey it has cool designers.

If Go survives, it will end up as complicated as other languages. Rust, too, if it survives.

You have to be arrogant to design a language and expect it to go anywhere, so you also tend to think you know better than everybody else, and that nobody has anything to teach you. It's an occupational hazard. Designing a language you don't mean to use yourself manufactures special failings.

Rust's designers started with unusual humility, and have kept it. It saved them from many mistakes.

People who enjoy Go will probably tell you that Rust is already one of those complicated languages.

Personally I don’t agree, I think Rust is right in the middle, but that’s because to the left of Rust is Idris, Agda, Coq, Ada and Prolog. Most SEs don’t have those on their radar.

They are on my radar but I’m unlikely to steer my sub in that direction as I don’t have the time!

Go has found a space in networking.

What Java and Python offer is a single language with networking (sockets), familiar and/or easy syntax, portability, and GUI capability.

Visual Basic was not in the same league (failed portability), but it too had these features.

This is on point. I would add that successive approximation here is quicker than finding solution on SO.

> so programmers who code by successive approximation, ...

> making more or less random changes until something works can get there quickly.


First clause doesn't logically match second.

Successive approximation does not imply optimal successive approximation. A random walk that gets there qualifies: a succession of steps. A linear search is generally more efficient than a random walk, but depends on an accurate assessment of the direction and magnitude of each step. A binary search also depends on a global ordering criterion. All that would too much to ask of many users of Go.

>Successive approximation does not imply optimal successive approximation. A random walk that gets there qualifies: a succession of steps.

Your first sentence (quoted above) is right.

But IMO, the two sentences quoted above, together, are not right. Because, even if not optimal, successive approximation means getting closer at each step, which is unlikely or impossible with a random walk.

With each step, you get closer in time to when you will stop. You don't know, at any step, how far you are from a solution, until after the last one.

You may laugh, but more code is written this way than not.

>With each step, you get closer in time to when you will stop. You don't know, at any step, how far you are from a solution, until after the last one.

Still think the logic is wrong or at least farfetched, but not going to argue it more.

>You may laugh, but more code is written this way than not.

I know that quite well, having been a dev for many years and a manager for some, so seen things from both "sides".

In fact I can say I've seen it from a third and maybe even a fourth side (perspective, really), ha ha, since I've been an independent consultant and trainer for a while now.

Okay, then.

"Programming by successive approximation" is more polite to say that "flailing randomly", but it would be a mistake to imagine one being more orderly than the other. But it was good enough for our protist ancestors, so it would not do to say it is wrong, as such.

golang is anemic in front of Java (the JVM, the language, and the ecosystem) and introduces its own set of gotchas. Once Java gets green threads (by means of Project Loom), golang will be an extremely difficult sell for anyone considering a new project.

Until Docker, Kubernetes and its ill fades into obscurity, even us on Java and .NET platforms now need to keep a passing knowledge of Go for tooling related on the eco-system.

Sure, but I was referring about the state and design of the language in general, and its viability for new projects.

Java's place in data processing is as secure as COBOL's. Go's is not. But, as with COBOL, it is not because Java is good, or getting better. It is because--like COBOL, FORTRAN, C, C++, Javascript and Python, and unlike Pascal, Ada, PL/I and myriad others--Java got a miracle. Absent a miracle, languages fade away.

Java is getting better. It's getting records, value types, and green threads, and pattern matching, and more.

Doesn't matter, just as it doesn't matter if it is good. Its secure place in the programming landscape does not depend on its improvements, or even on whether they really are improvements.

It’s a unique and different language that clearly hit some kind of niche. It’s great when a language has any users and this one has quite a bunch. That’s super cool!

Personally I don’t like it. Not low level enough for when I need to go low level, not high level enough when I want to go high level. Also I would have wanted generics right from the start; that should be the norm for typed languages unless the types are really super simple (Go’s are complex enough to warrant generics in my opinion).

But, maybe the reason why Go has the niche it has is because other programmers prefer it the way it so. So it goes with languages - it’s an aesthetics thing just as much as it is a technical thing.

To me it is a modern C with the lessons from Python learned. It is simple in the ways C is and the gotchas are all things i do naturally based on C. I like having structs and like having to check errors in each call. It forces one to concretize the idea that any reference to anything off of the CPU or the RAM might do anything. You have to have an error strategy for robust code. I don’t think people complain that Linux code does a lot of error checking. To be sure I mostly use to for network servers or making a lot of network calls quickly. (E.g. I have tools to do stuff in AWS that can reliably get S3 to start throwing 503 errors in Go. But they can re-encrypt a ton of data using one host and a small amount of time.

Entirely agreed. I'm relatively new to Go, but it strikes me as very much like a modern C.

But, personally, C can get really tedious.

I agree as well. Going from C to Go as still a newcomer to C, I liked that Go seemed to be more "defined". For example that there is one style and doing something like placing a bracket differently results in a syntax error. This is good for newcomers because different styles and philosophies about a language can quickly make one feel lost.

For me, C only seems tedious because to every question I have about it, I will inevitably find 10 different answers, maybe all from different periods of time. And I understand that I can look at the C standard but there are things that it doesn't address. Also, venturing into embedded programming, you can quickly come across implementations of the standard libraries that aren't nearly as well-defined.

So considering these things, I think it is a good "modern C" as long as you don't go too low level.

What lessons of Python? Go is basically Limbo with Oberon-2 syntax for method receivers.

Go has wide enough adoption that I don’t think you can call it niche anymore. It’s become somewhat of a de-facto systems/infrastructure programming language and is replacing Python in a lot of places where it was traditionally used for automation.

The language is simple, performant and writing concurrent code is intuitive (although I feel Go developers tend to get carried away using channels/goroutines).

Because every time I need to use a lock I know I am not smart enough to do so. Channels and go routines, I can reason about. (Tho they are slower - I have a runtime stats package that just stuff the numbers into a channel and then returns to the calling goroutine. I did an implementation using Atomic’s and it could sustain like 20x more calls per second before using all the callers CPU. But I still use the channel implementation.

Oh, I didn’t mean lock vs channels/goroutines. I meant in general prematurely optimizing by using Go’s concurrency primitives. It’s so easy to goroutine-ize a simple for loop with a waitgroup, but it can quickly get complicated with stop channels, cancellations, error handling/propagating, graceful shutdown which is then a source of bugs.

If guess if you don’t need concurrency just use Python :) I have fifty AWS utils in python and just 2 in Go.

Rust is ideal for this scenario: it lets you use the faster locks / atomics, and the compiler will check everything for you (assuming you can use a standard implementation rather than needing to implement your own - but there are well tested libraries for most common use cases).

It is on my list of things to learn but learning go made me so happy the urgency has decreased. Did they ever finish that rewrite of the core unix cmd line utils from C to Rust? Like find and if config etc. a rust busybox I guess.

It's not finished, but a decent amount is implemented: https://github.com/uutils/coreutils

In general there has been more effort put into trying to improve on the default tools through projects like ripgrep (grep), bat (cat), lsd and exa (ls), and fd (find) rather than creating drop-in replacements that don't provide all that much over the battle tested originals.

I did not mean niche in a small way. It is a big niche. Maybe I could have used a better word.

I find myself reaching for it over other languages when I want to build small servers with a bit of in-memory state or a bit of heavy processing. For little search-engines, Go is perfect. While writing servers in Flask + Python is much more convenient, I still prefer Go because I don't run into the limits that Python has.

The development process is fluid enough that I wish the language was suited to more usecases. When I need to handle complicated data (e.g. abstract syntax trees), I use Rust or Haskell because of their rich data types. But it tends to be much harder to get things running in those languages (borrow-checker and monads, respectively). I want to reach for Go to make those problems go away, but then realize that it would be really painful to try to express the program in Go. I understand that other people are unhappy with Go's development process, particularly if you deal with package versioning.

The language design itself may have been backed into a corner where adding generics will create an ugly mess (what will the standard library look like if it tries to maintain backwards compatibility?). Time will tell.

The tooling is mostly pretty good, but many things feel half-baked (compared to more mature ecosystems). On the spectrum of 'written in a weekend' to 'dozens of developer-years of work', the package "net/http/pprof" feels closer to the weekend side. There are bright sides, like having the production parser available as a library.

It's a language that is frustrating in different ways from other languages. Instead of fighting with Cabal/Stack, you have to write a million `if err != nil {}` statements. Part of what makes it frustrating is seeing how good it could have been.

OCaml is good for handling abstract syntax trees (strong typing, pattern matching) and its mix of imperative and functional programming don't require using borrow-checker nor monads. You can also consider Reason, if you prefer a more C-like syntax.

Also consider F#! In some ways it is more limited (no first class modules) but in others it is arguably better (operator overload, C# ecosystem, a slick take on do-notation).

I really ought to learn OCaml (besides just the toy programs I've written). I think if I got past the messy ecosystem (is tuareg the right thing to use, or is that the old thing?) I'd really like it.

I just migrated fully from OCaml to Rust. Couldn't be happier. I cannot even start to explain how big a difference a stable and mature ecosystem (including tooling) makes.

Language-wise, Rust leaves some (small) things to be desired with regards of ergonomics, but this isn't even close to offsetting the general feeling of developer friendliness and fitness to the problems it aims to solve. The borrow mechanics/lifetimes are godsend and one starts to wonder why all languages are not like this.

Rust completely exhausted the problem domains of these languages (for me): Java, Go, OCaml, even Python for something that would require more than 50 lines of code. Once you "get it", it's not harder to write than these.

OCaml still has some very advanced features (types) like polymorphic variants, GADTs and others, which I have used in the past to elegantly solve (in a type safe way) problems. But at least for my use-cases (and the software that I migrated) these were mostly problems created by the functional, garbage collected nature of OCaml. With Rust's imperative (but still very type-safe) nature I just don't need to apply such complicated idioms - it seems to fit perfectly to the "real world problems". And on top of that I get 3-4x speedup "for free" (for the program that I migrated). I added some very "lightweight" (as in "easy to do") parallelism and I got another x3 speedup (for the most frequent use case). This kind of speedup makes a dramatic difference. It enables me to do things that were previously not possible in the OCaml version.

And yes, on top of that I get to use Cargo, which feels decades ahead of every other package manager I have used and the IDE support (at least in IntelliJ/CLion) is pretty good (OCaml is nowhere near).

PS. And Traits are extremely cool

I always wonder if I just showed up late when I hear about the ecosystem! I just get opam for packages, dune for building, and merlin for editor integration (I did pay the complexity task using a language server for neovim, but merlin integrates with vim pretty trivially). It was easier than setting up Java + mvn|sbt|whatever + Intellij, easier than Python... there's maybe more going on than Go because the tools aren't all in one distribution, but man are the returns over Go massive (for the things I value).

> While writing servers in Flask + Python is much more convenient

The only time I had to use Flask, it was a nightmare to deploy. To this day I still fail to understand why I had to mess with Nginx, gunicorn, WSGI or whatever just for a basic backend. The dev experience was fine, but I don't remember it being more convenient than using Go

Because web servers look simple from the outset but are, underneath it all, very complicated. And it's not a web server.

If you want something basic, gunicorn can run flask with one line.

it's a history thing: back then, everything was serverless^WCGI and you didn't need to do any http servery things at all: just read stdin and write to stdout. web servers were an afterthought and turns out if you needed performance, you had to do it in C anyway, so nothing came prepackaged. then the web 2.0 happened and suddenly hello world is being done over http instead of printing to stdout, so new entrants obviously designed their stdlib around this.

But I still need gunicorn, don't I? My point is that with Go, I just have to execute my single binary and I'm good to go. It doesn't prevent me from adding a reverse proxy and a loaf balancer if I want to, but I don't need gunicorn or other runners.

Also if a single line is enough, maybe adding it clearly to the doc would be nice.

> But I still need gunicorn, don't I? My point is that with Go, I just have to execute my single binary and I'm good to go.

I don't see how your point is relevant. I mean, with interpreted languages you need to run the interpreter, but if having to write an additional word or line of code bothers you that much then you should not worry about it if you dockerize the app or, god forbid, use a launcher script. I mean, in non land no one ever complained about how hard it is to add scripts to mom's start and prestarte targets to launch a server by doing mpm start. So why is this suddenly relevant with Go?

You don't. Some frameworks are stand alone. It just happens flask uses wsgi, and so you need gunicorn.

What limitations do you run into with the flask/python setup? (I’m not asking what ARE the limitations, I’m asking which ones specifically you run into)

For large data sets, it sometimes spends all day in GC while just loading the data into memory (this is actually more of a problem in Ruby than in Python, but it still exists here).

It's also not very efficient about using memory, so it can be easier to get a dataset to fit in RAM in Go vs. Python.

Python also makes it hard enough to make use of multiple cores that it becomes easier to just use Go.

The same goes for making it fast. It can be done in Python, but I tend to be happy with the performance of my Go code out of the box.

Have you ever tried Pypy? I know when I had issues with GC and some memory usage (specifically dealing with large datasets as well, I was parsing large MySQL query results -- many millions of rows) Pypy didn't really drop memory usage much, but it did give me an almost 10x speedup. Enough that I went "oh, guess I don't need to use a different language."

I had a python server written in Python with greenlets to hat I switched to PyPy and the load it could handle went up maybe 4x. (5k per second to 20k per second. I took two weeks to switch to go and it went up another 10x to 100k per second and had less bugs (in fact a parsing bug in the python caused messages greater than 64k to be dropped; the Go handled partial reads properly and starting sending 2M msgs (to Kafka). The subscribers started getting the long messages and barfing making the system go wonky. Oddly all the giant messages were corrupt data anyways.

Code size was about the same due to stuff being implemented in Python manually being handled by Go runtime.

I’m interested in learning more about Ruby’s garbage collection issues. Could you recommend any resources on this? Or any tools in the Ruby language that would allow me to test this myself?

https://www.joyfulbikeshedding.com/blog/2019-03-14-what-caus... is a pretty interesting resource about this.

TypeScript might be good for your use case, expressing programs in terms of their types. You could also try OCaml/ReasonML.

Which python limits do you run into, for which you prefer Go?

What is it that you find more convenient about Python/Flask?

Flask is better in the little mechanics of interacting with HTTP - parsing a parameter out of a URL (in Flask, it's a function argument; in Go, it's at least one extra line per arg), returning JSON (just return a dictionary in Flask), handling errors. Flask also is able to auto-reload the code when it changes (turn on development mode) which is really convenient.

Plus all the ways that Python is more convenient than Go.

But...you are comparing a web framework with a Go's standard library. I mean, it's not a fair comparison. I'm pretty sure there are Go modules that support those handy things.

There are, but Go's type system doesn't really accommodate them very well. `interface{}` tends to abound, and it tends to feel less magical because you're still generally wiring things up yourself (i.e. you still have to explicitly unmarshal the request JSON).

In some ways, that explicitness is better because you know what's happening. In other ways, I'm incredibly tired of typing out 'if err := req.Bind(myStruct); err != nil { return err }'.

> But...you are comparing a web framework with a Go's standard library. I mean, it's not a fair comparison.

Why do you feel that the comparison isn't fair? Aren't we talking about modules that provide the features necessary to implement a type of application? Would it make any difference if tomorrow there was a PEP making Flask a standard component?

Unless you are aware of any Go module that addresses the downsides of Go with regards to Python, your observation is mute and unhelpful.

doesn't go stdlib contain an http server library "that you are supposed to use in prod"?

It does. It's rock solid and most people use it in production. But the parent is more concerned about quick and handy functions. IMHO, such functionality is usually part of third-party libraries and is certainly an option in Go. It's just idiomatic Go favors KISS principles, less external dependancies and a more self-contained software. The benefit is that pretty much all the Go source code is more-or-less identical, regardless of an author. It makes it super simple to understand and deep dive into a random complex piece of software.

Go is not about abstractions, Go is about solving problems here and now.

After 9 years of ruby, I finally switched to Go as my professional language 3 years ago, after a couple years of side projects with it.

My feelings after building for that long : safety and productivity.

I initially thought it was because of moving to compiler and type checking, but then I learned C for my personal use (an other thing passing through Go allowed for me), and was surprised to realize my C compiler was not allowing me close to the same peace of mind than my Go one. Go is just a well-designed language that makes everything straightforward.

The main reason I started learning and building with C is that I'm not confident the code I write in Go will age well. The discussions around Go-2 make me extremely nervous (it has something that reminds me of angular-2, python-3, symfony-2, bootstrap-3, and others major redesigns who left developers in a lot of troubles, I hope time will prove me incorrect).

So basically, I'm enjoying it, but I worry if it will last. I guess that means I really love it.

Re. Go 2. The Go Team people have repeatedly said that they want to avoid the Python 3 situation, so even if there will be a real, compatibility breaking Go 2, it won't be anywhere near that level of incompatibility. They'll probably just fix a few nits like string(int).

Re. C. You really should use at least one static analyser when programming in C. Clang-tidy has been my go-to tool for that, and it's been working pretty good.

Thanks, I'll have a look at it.

Can't say for the rest, but Symfony 2 was a resounding success. Current Symfony 5 is very similar to Symfony 2, with just a ton of refinements and facelifts.

Oh yes, I'm not saying those are bad projects. What they have in common is that migrating from previous version to them was especially difficult and painful.

The few symfony developers I know indeed loved symfony-2 very much. But migrating their projects from symfony-1 to symfony-2 was felt basically as a rewrite.

You're right, Symfony 1 -> Symfony 2 was definitely a rewrite, but from then on, the general design was kept almost intact (again, not counting the evolution and streamlining of the framework).

I guess the first attempt is always tricky, let's see if Go 2 can find a smooth upgrade path.

I'm not sure taking C as your other example of a type safe language will make the concept look that good.

Try learning Kotlin. I think you'll find it a significant upgrade in safety and productivity compared to Go, but especially compared to C.

I've been using Go as my main programming language for the past 5 years or so, and it's still my favorite language (picking from PHP, Java, C++, Python, and a few more I have experience with). Its simplicity makes it relatively easy to write high quality software and reduces maintenance, as there are no breaking changes in the language itself. Tooling is on point, you get everything you need, standard formatting (which helps to keep all code bases in the wild consistent), and cross compile.

One of the things that might make it even better are generics, but I haven't really missed them so far. In conclusion, I don't think I will switch to anything else soon.

My fingers are getting real tired from typing if err != nil all the damn time.

I do like it for small things - compiling to one (albeit huge) binary and its relative speediness is nice. I wouldn't use it for large systems though. The amount of code really balloons over time, relative to, say, python and that SLOC correlates to bugs and maintenance cost.

When writing quick-and-dirty Go programs, I define a function called "bailif" (pun intended) that turns non-nil errors into panics. i.e. `func bailif() { if err != nil { panic(err) } }`. Each error can be "handled" at first by just `bailif(err)`.

This works best in main(), while library functions can just return errors upwards to it.

Have you considered using a snippet or macro to type the 13 characters for you? Snippets or macros are available in most editors.

if everybody could use a macro in their editor or IDE, it's a strong hint that the language could do better.

(looking at you, java)

I have debated this. I was actually going to formulate mine into a pre-processor so that if I can change a macro it will retro-actively update the other ones. I thought it would be useful for this, and for structs that I want to replicate for various types.

At what point are we just creating a new language that compiles to Go, though?

I did not like it at first but now I kind of got used to it. It makes errors very explicit. Other language alternative is wrapping in try catch block.

Wrapping everything statement which can produce error is more tedious.

There are other alternatives as well. For example, Rust uses a Result tagged union. You can also shorthand it using the ? operator, which does the same as the if err != nil { return nil, err; } but without all the boilerplate. There are some inconveniences, like needing to specify the error type by hand, but overall the amount of code is far smaller, it is a lot harder to mess up return types, yet it remains equally explicit in forcing you to handle every error, which is something that Go doesn't do.

Curious, what's your definition of a large system?

I'm not the person you're replying to, but I've had similar experiences with larger programs in golang. It's quite terrible to deal with, and overly verbose. I'd say anything more than 2000-3000 lines and golang becomes tedious to manage (and that isn't much given how verbose golang is).

Interesting. I currently maintain 2 repositories, 20k and 60k LOC, and I consider both applications fairly small as it's super easy to cruise its modules/files. In my books, a large system is probably around 500k LOC and I'm curious how that'd work with Go. It's all about project structure and organization of packages. A single file with 3k LOC is most likely unmanageable. 20 files with 150 LOC each is also not much helpful. However, 5 files with 600 LOC each is a much more balanced split. You are not getting lost in files and each file is of reasonable size. Obviously, it's not always possible and highly depends on application specifics, but I'm just trying to make a point. One should always strive for a project structure that doesn't require too much of a brain juice to comprehend the structure. Otherwise it's possible to get lost in 2-3k LOC regardless of a programming language.

I work on a program that is 100+ KLOC. It's quite atrocious honestly. I can't help but think that if it were written in Java, it would have been much shorter, probably at least 50% if not even more.

Can you go into more detail about how java would make the program shorter?

Some examples:

* Exceptions for error handling

* Streams (map/filter/etc.)

* Generics

* Superior collections library (Set, ConcurrentHashMap, LinkedHashMap, etc.)

* Records

* Enums

* Upcoming feature: pattern matching

This is not really what I had in mind. I know java has these features, but how would they make your program shorter? Could you be more specific? Maybe share an example of a real problem that is made easier by these features?

Often people will give made up examples, but the trouble with made up examples is you can't tell if they actually matter in practice or not. Sure, if we invented a way to turn popcorn into gum, itd be easier to turn popcorn into gum, but no one wants to do that in reality. I'm after real examples.

I can give some concrete examples.

Exceptions create drastically shorter applications versus Go's explicit returns. I'll have to see if I can run an analysis later; I would wager that 20-30% of the lines in our monorepo are related to error handling. This is in large part due to the style of having the if err return err take 3 lines, but that's the prevailing style. Additionally, it requires an explicit handling block everywhere that might return an error, even if you don't want to handle that error there (i.e. in a web app, not handling an exception defaults to a 503 error. It takes 0 code to return a 503 on an error, which is usually the default error path).

For generics, I needed to write a map merge for two map[string]interface{} maps. Fairly basic, for scalar values prefer the value from the first map on conflict, for slices append the two arrays, for maps do a recursive merge. I used a type switch to get the type of each value, when I realized that []string and []interface{} were different, and that I wouldn't be able to append to a []interface{} unless I convert the []string. Fine, so I need a function that creates an []interface{} and converts each index in the []string to an interface{} and puts it in the array. Except because of the type signatures, I now also need one for every scalar type in Go. Is it an overwhelming amount of effort? No, but it did turn 6 lines of code with 12 lines of unit tests into something like 50 lines of code with 100 lines of unit tests (more type switches). Generics should have allowed me to say that I don't care what type the input is (and interface{} doesn't work here). Worse, now if I adjust a unit test, I have to do it in several places, not just one.

Those are the things that frustrate me in Go. It ends up feeling like I'm brute forcing software development; it's not the fastest way to do it, but it'll work if we're just willing to rewrite the same function 10 times with different type signatures, or write if err statements 10 times so that we can get the error up to the 11th caller in the stacktrace, who's actually going to do something useful about the error.

My tolerance would be about the 2000 line level.

That isn't the point where I'd consider it non maintainable or anything, it's just the point where I'd probably consider the trade offs less worthwhile.

Great tool, two big negatives. First, checking result values is dumb. It adds 50% more code. Add exception handling.

Second, the way imports work is obviously due to some internal google kitschy-ness. Remote import paths are so dumb. People set up entire domains and CDN's just to host some code. The import path has to have a specific format, you can't have three levels. github.com/me/sub1/module won't work, so everyone creates mypersonalgithub.com/sub1/module. It feels like one L3 at google set this up on a Friday, and the industry has to live with it. Of course all this code will be broken in 3 years.

There is no easy way to point imports to a local development copy of a repo because google uses a monorepo and everyone else has to live with it.

Your first negative is a matter of perspective, so depending upon your coding preferences you may be right.

For me personally, even with nearly 2 decades of C# (exceptions are used extensively) I prefer the Go way. Checking results values is not dumb, it's just not the way that suits you. And that's fine. Variety of preference is one of the reasons we have so many programming languages to choose from.

As for modules, despite extensive familiarity with packaging systems (nuget, npm, etc) I prefer the Go way. The import path is totally developer-defined (eg "github.com/russross/blackfriday/v2" which is three levels), and a single line in a Go module file redirects a repo import to your local development copy (by local folder location).

For me personally, with also nearly 2 decades of C# and C++, exceptions is the best possible way to handle errors there is, especially for large projects. I have worked on projects which were built on exceptions, and on projects which were built on return codes. Exceptions is hands down the cleanest. Instead of becoming a mess of call/error handle blocks, you code can clearly separate business logic and error handling. If I had 10 cents for every time I've fixed a bug when an error analysis is skipped because it is not supposed to happen until it is...

Golang way is terribly flawed. As a matter of fact I've watched a video yesterday on golang best practices, and got very excited. And then I realized, this is still a language where you can't throw an exception and that made me very sad, as I realized golang is going nowhere. It will be exciting for a few more years, like ruby was once, and then everybody realizes what a PITA it is for large codebases, and it shall become another ruby.

And for me having worked in both, errors as return values have been hands down better. The complexity added by an entirely different flow control has resulted in far more bugs for me to track down than anything else.

Similarly, it's for good reason that there's a whole other class of languages using things like the Result monad over throwing up all over everything.

That's not to say that there isn't better code written in exception based languages, but that your view is pretty myopic. The ergonomics are different in each approach, with different tradeoffs.

I'm pretty sure exceptions are better, for a couple of reasons.

The first is that the idea all Go programmers reliably propagate or wrap error codes, without losing important context, is not true. One of my first encounters with a serious Go codebase was at a consulting client, where I had a task to use their API. I sent it some input and got back a 500 Internal Server Error, no other info. OK, not ideal, but it was in development so I asked them to check the logs and find out what was going wrong. Guess what, the logs were useless. It logged at one or two places that an error had occurred, mostly close to the top level HTTP loop, but the actual location where the error was originated had been lost. Several layers in this app would convert error codes from one level of abstraction to another, also losing information. They shrugged, mystified. Just try things until you figure out what the issue is.

With exceptions this could not have happened. An exception has a stack trace. The developer needs do no work to get this valuable debugging aid, it's always there unless some bad code strips it somehow. Additionally, exceptions can wrap each other as causes, so code can work at high levels of abstraction whilst developers who are debugging can get precise error data from deep down the stack.

Another problem is the idea that Go developers never forget to propagate errors. Error handling in Go is tedious and there's no visible indication if you forget to do it or don't do it properly so sometimes it goes AWOL. The exceptional control flows still exist, just as they would if using exceptions, but now you have to write them manually instead of having the compiler write them for you.

A final problem is performance. Go has notoriously quite low performance, the people who say it's fast are usually comparing it to something like Python. Scattering hand written error handling code all over the place makes it harder for a compiler to move it out of the hot paths, because it's just a bunch of if statements. Exceptions by their nature tell the compiler that those error-handling edges won't execute very often, so they can be put out of the way in places that won't pollute the icache.

I agree with you on the stacktrace. The libraries that provide context via wrapping are good but woefully underused.

> Error handling in Go is tedious and there's no visible indication if you forget to do it or don't do it properly so sometimes it goes AWOL

This is also true, though I would expect teams to fail on linting so I don't share the concern. Is there an equivalent for unchecked exceptions (honest question)?

I'll take your word on the performance being better for exceptions, but I'm less convinced that Go's performance is a significant sticking point for the language.

A super important principal for large code base is locality. I should be able to look at a function and understand everything about its possible code paths. Exceptions make this impossible (most of my experience with exceptions coming from C++ and Python). People that think like me would forbid exceptions every where.

return values don't say anything about possible code paths. If you are saying the function should document what could go wrong when it is called, Java solved this 24 years ago when each method lists the exceptions they throw. If you need to know what the back trace/call stack was when the error happened, well that is stored in the exception.

People that use return codes simply don't check every possible value, otherwise they would see that it increases program length by at least 50% and makes the code unreadable. A common pattern in Go is this

if (err == MY_ERROR) { log my error; notify user; return MY_ENCAPSULATING_ERROR_CODE }

for high quality code this has to occur for every function call in the entire codebase. So you'll get huge functions which are mostly 'if' statements checking error codes, logging the results, and returning another error code that has to be checked again one step up the call stack.

moving from c++ to java 20 years ago it was clear that the c/c++ community didn't really know what they were doing in this regard. A lot of c/c++ dogma boilerplate code was holding it back. Too bad some of it made it into golang.

Explain the Java services that only ever return an HTTP 200 and either return the proper XML response or a java stack trace formatted into HTML.

And my point is those if statement are wanted by the people that write high reliability software in C. There just isn’t a substitute for thinking about errors. Even well done exception throwing systems will specific “this layer can throw, this middle layer won’t worry about errors, then this higher layer will restart or retry or whatever”. And in go within one layer you can usually have all those steps in one function and for error cases with the same handling just check for if err != nil. When I read code without explicit error handling I worry. I guess this debate is probably an aesthetic debate, and different type of code have different urgency. The extra check for error return is like the check for parameter correctness at the top of a function, which is common in robust C code. You can’t trust the callers too much and better to log an error than terminate the process.

> And my point is those if statement are wanted by the people that write high reliability software in C.

Because that's basically the only way of handling errors in C. That kind of software is written despite C's poor error handling, not because of it, and frankly, C is not the best example in writing high reliability software. Undefined behavior is a huge issue with it.

> I should be able to look at a function and understand everything about its possible code paths.

Any golang function can panic, so you always have to account for it anyway.

What? That's patently untrue.

What do you mean? A panic can happen anywhere and it is not reflected in the signature.

Non-exported struct fields are a way of preventing your library from being composable. Checking if a value implements an interface is a way of writing surprising bugs. The standard library uses both of these features extensively, to give users bugs and prevent itself from being composable.

Checking that the user uses all values bound to variables but letting them discard errors by not binding them to a variable is a weird choice. Channels panicking instead of returning an error is a weird choice.

It's ok and I would like to to use it instead of Java.

Can you elaborate on private struct fields? The same criticism would apply for any OO language with access control / visibility of fields right?

The particular combination of things is, some library includes some method signatures that accept and return a specific struct (not a specific interface). Then, also, there is no exported way to make one of that struct, or to set its fields. Now you cannot write a library that wraps the original library and exposes the same API (unless you use unsafe and reflection).

In the OO languages I have encountered there seems to be a stronger convention toward using interfaces instead of struct or class types in this sort of situation, but people are certainly free in Java to use a final class type as the type of their parameters and return values if they want to make their library less composable.

Is this a different problem from private constructors / setters in OO world?

No, but it's a more prevalent problem in the actual standard library of Go. The Java standard library does not regularly use final classes with private constructors and setters as the parameter and return types in its APIs (which would also prevent people from implementing those APIs). It uses interfaces.

I guess is the typical critic of people used to monkey patch libraries in dynamic languages.

Related complaint, the standard library taking structs as input is kind of annoying. I'm specifically thinking of net.Dialer having a net.Resolver, where that's a struct and not an interface, so it's difficult to sub in custom logic there (but still possible, at least, with some contortions).

Single-binary deployment and (these days) good dependency tracking (downloading multiple versions of a library) is great; I don't worry about trying to get somebody else's source code built I do the way when I get python packages. The python side is largely because setting up a venv is error-prone (at least, for me, somehow) and leaky. I still think having the version baked into the import path is kind of dumb, but I suspect that's really just because the documentation failed to explain the reasoning and good workflows (I understand just changing the path in go.mod may be enough).

I wish they had things like generics, but I fear that an incompatible version break would cause too much strife, much in the same way of python 3 / perl 6. Given how the vendoring tools story went, I'm not yet confident the core team can manage the community very well.

On the positive side, the error handling has been decent for me so far; an error interface is easy to understand, compared to rust (where I think you can use Box<dyn Error> but they don't actually say that in the basic documentation). I also like the decently sized standard library, again in comparison to rust where things are too minimal. I think ruby has a decent compromise, where the standard library is (mostly) also available as downloadable packages if you need a newer version. Somehow I've had worse experience with ruby gems compared to go modules, though; I think that's because the bundler documentation is rather bad — it feels more like an essay rather than a reference.

Not having a central repository of packages is a definite plus in my book for go; it leads to less of a problem with people taking the good package names (since most names are bad anyway). Less worries about cutely named libraries like devise (ruby, for auth) or hyper (rust, HTTP). IMHO, libraries should be plainly named, apps can have weird names if they want.

> incompatible version break would cause too much strife, much in the same way of python 3 / perl 6.

The Perl world has solved this by renaming Perl 6 to Raku (https://raku.org using the #rakulang tag on social media).

Go is a welcomed simplicity to the stack in a world where everyone is over complicating software engineering by jumping to Kubernetes and micro-services. Simplicity really is where Go stands out against languages like Java. Yes, you may write more boilerplate and break DRY, but your code will be readable and won’t contain any hidden magic.

We spend most of our work day reading and trying to understanding code. What you see is what you get with Go!

Side note: Go also runs fabulously on Kubernetes and is great for building micro-services :)

All of this, 1000%.

We have a few go web apps to maintain and, honestly it's not a pleasant experience.

- Package management is terrible. - Development on Mac is not a great experience (someone in the team purchased a paid IDE, everyone else just run everything through a docker image, are we missing something obvious?). - The language is not flexible enough (lack of generics?) and code is significantly longer. - Lots of quirks.

I'm sure you can learn the language and overcome these small annoyances (and that's exactly what we did, because we wanted to squeeze more performance out of our services), but personally I think nowadays we have better languages which perform just as well (eg. Rust).

Since they added go mod I find the dependency management to be ok. And I use emacs so Mac or Linux it is a pleasant experience. You have first class lambdas so you can do pretty much anything in the language that doesn’t depend on syntax. It is a language with the aesthetics of C - small and simple syntax. Things that make the code more verbose, eg error checking, are things C programmers think you should do anyways and are morally suspect if you set up some invisible throw/catch system between your lower level code and your top level. Everyone typing code must reason about errors.

Worth noting that Go Modules are still relatively new (Blog post from March, 2019 - https://blog.golang.org/using-go-modules) so some people in this thread might not be aware of them. I find that dependency management works well enough now.

Go works fine for me on the Mac with VS Code — in fact I recently used it to develop a Windows service so I wouldn’t have to deal with Windows (except for testing, of course). The cross-compiling is really easy.

Go taught me how we tend to rely on overly complex structures to solve our daily problems, which makes our code look smart, but in fact is harder to maintain and understand by newcomers.

99% of the time, trying to use idiomatic go instead of fighting the type system lets you have more maintainable and easier to understand code.

Now, every time i'm back to using another PL, i tend to rely on advanced type systems feature much less frequently, and only after making sure i'm not making the problem more complicated than it is.

So true. I was nervous about not having generics in Go but haven't missed them after primarily working in it for several years. You have to stop trying to write C++ or Java or C# in Go.

You can perfectly write C++ (from around 1993), Java (until version 1.4) or C# (until .NET 2.0) in Go.

That is the whole point, we know better 25 years later.

It was a large mistake to ignore the lessons of Java and now the language is full of quirks as well, including the upcoming generics design.

Regardless of the whole systems programming language polemic from the anti-GC crowd, several projects deployed into production like gVisor and TamaGo prove otherwise.

Finally thanks to killer applications like Docker, Kubernetes and everything around them, Go has become unavoidable for many developers, regardless of our opinion towards the language.

> several projects deployed into production like gVisor and TamaGo prove otherwise

Do they? AFAIK gVisor absolutely had to fight with Go, and it has a huge performance impact, and lots of larger projects have done some semi-absurd work such as implementing their own generics impl via preprocessors.

Just like C then.

I don't code with it but I use Hugo (a static site generator) that's written in Go because it's ridiculously fast. Ruby and JavaScript static site generators I've tried end up taking several seconds to generate a handful of pages when Hugo spits out hundreds of pages in less than a second.

It's pretty crazy how used we are now to slow dynamic languages.

I also like how Hugo is available as a single binary file - much simpler compared to the complex build chain of a typical JavaScript app.

I slapped together a static site generator in (mostly) Bash the other day and it renders all of my 6 shitposts (Node for MD -> HTML conversion) in 2.7 seconds flat and I'm perfectly happy with it. Or I was. Now your post made me want to rewrite it in Go, for science.

My Node.js (well, Typescript) static site generator that I wrote for my blog is carefully written to be fast enough for me. I am considering rewriting in Go too, "for science", but I'll probably continue to use the Node.js version if only for the npm packages I use.


Being backed by Google contributes a big deal to its (relative) success. I think it is the most limited and ugliest of all the "modern" languages (Rust, D, Zig (never tried Swift)). And at the same time it seems to have the most significant output of all these (docker, kubernetes, gitea...).

I find it more important that it is backed by Rob Pike.

He doesn't cancel things when he gets bored with them.

maybe he should

It's meh. I find no matter how much I use it (going on like 6 years on-and-off now), I do a lot of tedious work. If I'm getting paid by the hour or salaried, that's fine. I'll get paid to do compiler-work by hand.

But if I'm trying to do a personal project, that amounts to a waste of time with 0 value.

They tried to make a better C++ and accidentally made a better Java and a better Python.

Agree over the java part. But Python is still orders of magnitude more productive, being it because of the ecosystem, available libraries, frameworks, or almost any other metric. The only advantage over python is performance, and maybe static typing depending on your taste.

Go more than makes up for this in the deployment aspect, since deploying Python in 2020 (Python 2, Python 3, conda, pip, venv, system interpreter, poetry, etc.) is still a mess.

Only a mess if you use python 2, conda, etc... What’s difficult about this?

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/in...)" curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/maste... | python - brew install pyenv // update shell with pyenv pointer pyenv install 3.9.0 pyenv global 3.9.0 poetry init poetry config virtualenvs.in-project true --local poetry install

For deployment, Docker python3:alpine, install poetry, copy over pyproject.toml and install

> What’s difficult about this?

Well, for starters, that particular multi-liner:

- installs an entire package manager, which may or may not conflict with system tools or a different package manager,

- assumes pyenv will build Python against the system libraries without additional flags (it won't if you're running Big Sur, you'll need zlib from Homebrew),

- assumes you are on macOS, and does not attempt to work on Windows or Linux, or account for the idea that someone might not want to deploy a program using Docker.

Plus, it does so non-reproducibly and downloads random binaries from the internet.

Which makes rolling back a bad push really interesting. Give me a append only repo with my static binaries that my compute nodes deploy from. I even went to the trouble to put Python into RPMs for this.

pip is broken in no many aspects, between pip and pip3, things installed in your home folder vs /var, "binary" entries that don't exist after installing a tool, cryptics stack traces because of missing libs / not compatible for your platform ( native build ) ect ...

Why do we need virtual env in 2020?

It's one of the worse "modern" language to build, even Java / NodeJS are better.

It may be more productive if you are the sole consumer of your code. If you need to ship code to be run by third parties Python tends to result in overly complex and tedious setup process. One shouldn't ignore that users of Python applications often have to pay the price.

Go makes arbitrarily large codebases feasible. I find Python to be exponentially more painful as codebase size increases. For personal projects this is fine, but for industrial use cases Go has a significant competitive advantage.

>Go makes arbitrarily large codebases feasible

For me, this is the consequence of a reasonable static type system. Without generics and the current error handling story, it almost feels like Go makes large codebases necessary in the projects I've used it, compared to other statically typed languages such as Kotlin, Java, Rust, and C#.

Go 2 on the other hand looks like it will solve this problem, and I eagerly await its release.

In my codebase at work there are a few cases where code is (poorly duplicated) that would benefit from a proper generic solution. But I don’t expect go2 to significantly reduce our codebase size, even if we replaced the aforementioned components.

I agree that some form of static typing is critical to scale a codebase, or an ecosystem. Let’s also not forget the importance of a packaging system with robust version management. Go modules and proxy.golang.org are also critical to Go’s success at (code) scale.

> Agree over the java part. But Python is still orders of magnitude more productive, being it because of the ecosystem, available libraries, frameworks, or almost any other metric.

How many years of Java experience do have?

I’d be really interested in hearing more about the ‘better Java’ point, if you have the time.

Not the OP, but i would have guessed that the (subjective) axes of "being better" are uncontroversial - by which I mean prepend "there are many who value that" in front of each:

- a GC that can has relatively smart defaults without having to tweak a ton of parameters

- don't have to ship a vm

- concurrency baked in from the start

- simpler language

- doesn't try to square everything into the OO paradigm

> a GC that can has relatively smart defaults without having to tweak a ton of parameters

Depends on the JVM being used

> don't have to ship a vm

You get to ship a runtime instead.

Which Java also allows since around 2000, given how long commercial AOT compilers do exist for Java.

> simpler language

Java 1.0 was also Go simple, and just like Go does nowadays, full of code generation tools to work around that simplicity.

> doesn't try to square everything into the OO paradigm

Interfaces are part of OOP, introduced by Objective-C protocols.

> Depends on the JVM being used

Is picking a jvm not tweaking a parameter?

Just like picking Go, gccgo, TinyGo, TamaGo or whatever might come down the line.

Nobody really does that.

They do, otherwise they wouldn't exist.

I use go (long term) in places where I need as part of my DR plan the ability to fix stuff without expert access. The toolchain is simple enough that I can maintain it in this scenario alone, and meanwhile the stdlib is complete enough as to be useful for solving a multitude of problems, while offering the ability to produce programs that run on many systems and integrate with them with sufficient efficiency. It's a really usable systems glue language, and it powers chunks of my telco infrastructure. This pro is mostly about the implementation/engineering design.

My biggest gripe with the language is that it doesn't scale that well for teams. It requires a certain culture & approach to produce maintainable programs that goes against the grain for many developers, and while I enjoy the simplicity it offers, that simplicity leads to cost of ownership problems in many team scenarios. This con is mostly about the language.

As a specific tactical item, the runtime multiplexer could do with a refactor. The merging of the pollers essentially could continue until it is much cleaner, there are more layers there than necessary to solve the problem. You can follow the FD close path to understand the strains of the current factoring. Such a cleanup could probably remove some overhead for io heavy workloads too, maybe even drop some lock contention. Oh, and the spins should all be removed from the runtime before we really start using it on very high core count systems. This opportunity is mostly a side effect of a long history of incremental improvement.

Could you elaborate on why you think it doesn't scale well for teams?

Imho because it is highly opinionated and the designers of the language also suggests best practices. They don’t do anything wrong in suggesting best practices although some of those best practices only work when you write infrastructure code and not micro services (with business logic) and some programmers cannot really compute that and they stick with the religion. I also had the same experience of not scaling for teams for this reason, because you had people who would not allow for discussion. It is honestly never happened to me before. Given it could only be a matter of having found just a bad apple in the team, my first reaction was that this language doesn’t scale with teams.

> Imho because it is highly opinionated and the designers of the language also suggests best practices.

In my experience this is actually a plus. I've lead a department that made the transition from Java to Go, and one of the really noticable things were entire classes of fruitless (and frankly, unnecessary) discussions disappeared. Having an opinionated language that comes with a canonical formatter and lots of established idioms made it easier to scale up to more people because more of the code looked homogenous more of the time.

However, with any new language someone takes up there is the risk that people will try to bring along their old habits from other languages and be unhappy if they can't just continue writing C/C++/Java/Python/C#/JavaScript/Lisp in Go. If this takes hold, you will waste a lot of time having fruitless discussions.

It is easier to adapt to a coding style that is shared than it is for every project and every team to invent their own. For instance, I've worked for only one company where using C++ was even somewhat pleasant: and it was in large part because a) the company had a very strictly enforced code standard for C++ (and other approved languages), and b) you had to demonstrate you could stick strictly to the code standard before you were able to approve changelists. At the time that company had some 7000 developers, of which probably 3-4000 had "C++ readability" rating.

The upshot being that after getting used to it, it was pretty easy to understand other people's code. And you would spend a lot of time reading other people's code.

I suspect Go being opinionated is a direct result of this experience.

I do not like it. The lack a proper (1) Class, (2) Enums (seriously, using constants to describe a proper Enum like Java has is a must, we are in 2020 not in 1980), (3) being able to call methods on methods on nil pointers, (4) generics that are available for langauge internals but not for language users, the list could go on. All this just because I am working on it few months now. Let's see how it will evolve through the years, but could not have been worst start.

The lack of classes and generics is probably the strongest argument of Golang, so can’t agree with you there. As for enums, I miss them too. Sum types as well.

I think most Go people will put most anything that goes over the wire into some protocol description language like protobuf or even swagger. Inside your call stack I am not sure you need enums instead of constants. If you are either persisting or transmitting data then you want some formal description which will essentially get you enums as useful symbolic values.

Lack of real enums is one of my biggest annoyances with Go. So so many times I've put the wrong enum value in because it's just an int and it matches another set of enums. I've taken to putting each set of enums into a separate file and separate folder just to help the compiler catch this. The project I'm working on now has about 25 separate enum files+folders. Gagh!

I made this playground: https://play.golang.org/p/S1Yh0Oq24q-

There are a ways to do unique enums with implicit repetition. Like this: I 0 1 2 II 101 102 103 III 201 202 203 IV 10 20 40 80

Yes and no. Most of the time my enums are well defined: ContactStatus, AwardLevel, JugeStatus, for example. I have not control over the names or values and all have a "Revoked" value. When they come in from an external system it's pretty easy to parse them and convert them to the wrong type if someone isn't paying close attention. Then they don't get caught for a week or so because the code compiles, runs, and appears to work correctly except for these occasional odd results.

Typedefing with iota should let the complier catch this, but it is an extra wonky step

The real value is that Google is pumping a lot of money into writing high quality libraries. Any language with similar backing would do equally as well.

The golang standard library and high-quality third-party libraries are really great

They set out to make a pretty generic ALGOL/C-ish/Java "OO"ish language with sharp corners filed off and some decent concurrency primitives put in. They executed successfully (they got the language out there, built an ecosystem, didn't undermine their own goal in the language design or make it unusable.)

If I had to choose between Go and C++, I'd probably pick Go because of C++'s complexity, so they really nailed it.

In the grand scheme of things, it's a really forgettable language. Frankly, I haven't programmed in it. I don't need to. I know ALGOL+(N+1) when I see it.

> In the grand scheme of things, it's a really forgettable language.

Strangely enough, for me that is one of the major plus points. A forgettable language (which is not the same as a language which you forget how to use) is one which gives you the tools for the job and then gets out of the way.

Some other languages feel like prima donnas with their shiny brilliance and complex possibilities. I'll take a small, dirty, simple, and performant back-seat one like Go every time.

Nonsense language. We've only had problems with it historically, both writing and deploying it. It's threading model sounds great in theory, but we've found that bounding most programs to one system thread improves performance in nearly all cases we've come across.

The tooling and ecosystem are ridiculous, the dependency management is a nightmare, and the fact it's backed by an incredibly evil company doesn't help either.

Further, any criticism of the language results in cult-like angry responses, as do most modern toy languages.

How is Go a toy language?

I started using Golang 8 years ago and introduced it to the company I worked for to build several web services from scratch.

The reason it was adopted quickly was that building RESTful Web Services or gRPC Micro Services is relatively easy because all the libraries needed are native to the language. Of course I was building .net SOAP web services for years and knew the customers' needs. Another reason was the developers knew first hand how inflexible was configuring IIS with its web.config and the efforts needed to make it work.

Then the adoption of Kubernetes was easy too because it was written on Golang. The next transition to GCP was natural too. Basically being backed from Google made all this technology development and adoption a natural and I'm glad that I made this choice early.

Another reason I'm using Golang is that my recent project is mobile-GCP solution making possible to get information about 80mil/100GB US Property database in iOS App:


One of the reason this functionality was made possible because I managed to create Gomobile embedded database in iOS which is much faster than SQLite or CoreData for my purpose - creating geodetic solution - tapping on the map is retuning lat/lng which are two numbers and creating just one number representation of location suitable for key-value datastore.

I played with Golang a little bit, it is very boring (and that is good), it is very pragmatic and performant.

I didn't like dealing with JSON data but other than that.. it's great to get work done in a simple way if work == services/backendy kind of things.

I would love to rewrite a lot of our python services in golang, both for the performance gains and to make the projects look more alike.

Python gives way too much flexibility, is a pain to deploy and has too many frameworks for doing the same thing.

I'm glad it enables other folks to build such great tools with it, but I don't think I'd pick it for my own projects or prefer to code in it at work. For the clever stuff they picked up from ConcurrentML (channels), they treat the user (developer) like an idiot due to having to spell out everything in great deal compared to languages that have a better type system.

It's good to get a lot of programmers onboard at Google who may have never written a line of C or C++ in their life. Coming from Java or Python, I can see the proposition of a language like Go. As in those languages, Go doesn't have myriad points of "unwritten code", e.g. having to worry about lifetimes of objects, the "rule of 5/6" for autogenerated methods (and knowing when the compiler generates some and not others) in C++. It's all there in the code with great verbosity.

I think people complain about Go because it's not the hammer for their nail. It's not for folks working with a team of moderate to elite-level C++ or Haskell folks who enjoy writing "beautiful code". It's for a company with an army of developers of varying degrees of skill who all have to maintain the same codebase without exploring the dark arts of the C++ abstract machine, and to do it in a much faster language than Java.

Like Java, it's driven primarily by business decisions for application software that needs to support all sorts of developers for server-side applications. It's very good at that. It's just that playing to that lowest common denominator creates an unpleasant experience for folks who want a more expressive language.

> I think people complain about Go because it's not the hammer for their nail.

Or it's _almost_ the correct hammer! If they'd just fix those damn

It's the correct hammer for a company that employees tons of people who, for the most part, don't really care about the craft of programming and don't want to be bothered by learning anything difficult. They just want to ship their features on time and get their year-end bonus or whatever pat on the head.

Even at a company like Google, I don't think most programmers are really pushing their understanding of software design or tools or techniques. They learn all the silly interview stuff about red-black trees then are just happy using coding within the well-defined bumper car arena of Google's frameworks and coding guidelines (e.g. Golang with no "hidden code" like in C++, where not writing certain things causes the compiler to generate stuff for you).

We all read Hacker News and generally are excited by new advances in things (e.g. C++ concepts! No more stupid SFINAE!), but most people at big companies don't.

It's truly a testament to the genius of Go that it solves this problem: much faster than Java, not complicated because most programmers at Big Corp can't deal with complexity (e.g. no exceptions in Google's C++ coding guideline), and understandable without too much effort by the average programmer at Google.

We all understand what it looks like without this effort: The Monolith of Bug Spaghetti Code

I work for an extremely large software company that is stuck on C for decades. I try to use C++ where I can, but I usually get shot down in code review with a message of "C++ is too complicated for your colleagues to understand what you did". To solve the code mess, they implement their own languages to generate the boilerplate C (e.g. like protobufs, but poorly done and NIH because some guy wanted a promotion to "Principal Engineer").

As much as I dislike Go, I would take Go over the current situation I'm in...

This is so arrogant. Simplicity of Go is a boon for everyone, including super-geniuses like yourself. Keeping the language and ecosystem as simple as possible means you can operate with lower cognitive load, and devote more of your mind to your current task.

Simple languages are great, but simple does not necessarily mean less powerful. C is a simple language (albeit with many footguns) and the ecosystem is simple as possible (make and friends, gcc/clang, etc), but it can be extremely difficult.

Swift is a great example of what go could be. It includes a lot of powerful features (e.g. generics, lacking in Go), but the language is designed so that its easy to start without having to learn advanced language features; one can slowly accumulate these more advanced tools as one progresses. Scala is also like this; a lot of Scala code is basically just less verbose Java. This is in stark contrast to languages like Haskell or Rust, which one has to very quickly learn myriad details about things to be productive (e.g. about laziness and the data / typeclass split in Haskell compared to standard OO, or about the borrow checker in Rust). Arguably these "big brain languages for super geniuses" distinguish themselves by these features, but it inherently makes them less simple.

It's certainly a boon to everyone (including "supergeniuses") that Go has its design for the same reason Python has its design: simplicity begets a great ecosystem. I still use Python all the time because despite its mess of packaging, dynamic typing, and painfully slow execution, there are so many great projects. Go also produces great tools that I enjoy every day (e.g. hugo and kubernetes) and I'm thankful that the language enabled folks to write these applications that run super fast and don't crash because some rarely-exercised code path referred to an undefined object attribute.

Speaking strictly about the language, and not the platform, I'm skeptical about the lack of more modern core library and language features like immutability, pattern matching, and records. They spent almost 10 years saying "we don't need generics", but now are implementing it

Also, the dependency management.

> They spent almost 10 years saying "we don't need generics", but now are implementing it

You're making things up. Not a single member of the Go team ever said "we don't need generics". In fact, the FAQ stated from day one that they "continue to think about it", and that "Generics may well be added at some point".

Yes. IIRC, Rob Pike or someone else on the Go team even said something like "it's hard, so we are taking our time about it, may implement it later". Edit: see [1] below.

Also googled and found this:


which includes this section:

Go might have generics in future

with this text:

The Go FAQ recognizes the issue and says that generics may be added at some point... http://golang.org/doc/go_faq.html#generics

Considering how Java generics turned out I am fine with the Go team taking the time to think about a proper design for generics in Go :-)

By ted stockwell at Tue, 2012-06-26 15:03

[1] The golang.org link above about generics also says this:

We haven't yet found a design that gives value proportionate to the complexity, although we continue to think about it.

They've always also said "show us some compelling use cases", as if those still need to be shown ~15 years after after every other language except C got them.

C has the preprocessor at least, reducing the need for them.

Ah, I stand corrected.

Dependency management is better now with Go Modules. Not as great as e.g. with cargo in the Rust ecosystem but definitely an improvement.


More and more, when looking for (open source) software solutions to adopt for my startup, Go as a development language is a deciding factor.

Go-based software is smaller, faster, easier to deploy, easier to update, easier to contribute to.

I find that the installation footprint of a Go-based service is usually in the 10s of megabytes or less, where the installation footprint of pretty much anything else starts in the hundreds of megabytes.

In the classic world of one or two servers for a small company, sure, the disk space is meaningless, but with containerized workloads, small images are great for quick deploy, and quick iteration.

Fast compilation was one of the main reasons Golang was invented.

The gradual accumulation of #include clauses in C programs scales very badly. A large distributed compilation system was created in Google to handle the slow compilation. One reason is you never know if some #include is actually needed.

In Golang unused dependencies are a compile-time error. The generated object file includes type information for all related import dependencies in binary format. This speeds up the compilation significantly.

I think it's boring, overly simplistic, and ignores the last couple of decades of PL research.

Boring is actually what a lot of people look for.

I never understood why people are looking for things which are boring and I think it is mostly employers who do. They should look for something which is good and solves their problems. I don't know how something with the attribute boring is of interest here as other attributes for a technology/PL might be much more important...

Actually they mean something different with "boring": It is just a lack of courage for simply saying "We are most conservative, do not want to move forwards and are not willing to take any risk. Also we are scared of specialists and talent and want to stay mediocre. Actually working with us is boring."

> we are scared of specialists and talent and want to stay mediocre

This arrogance is just so weird. A High-quality code base is easy to read and change. a.k.a simple. Redis is a good example of this. To have found the perfect abstraction, or perfect feature set fit, for a particular moment in time and feature set, may feel good but doesn't really matter.

> to have found the perfect abstraction, or ...

Didn't even know that I was writing about the perfect abstraction.

Mediocre environments are where you will not find much code of high quality even though every GoF pattern seems to have been applied. That simple code you mention requires professionals and talent. Looking for the perfect abstraction is what you find in mediocre environments.

Redis is actually a good example. It is a highly specialized tool. It might perfectly fit your needs but someone in your company might have defined mysql as boring and Redis to be not... So youl'll use mysql...

This, plus it enables them to fire and replace developers without much difficulty because there's a great number of people who know these languages (see Java...).

You are actually arguing for mediocrity. If someone needs to protect their employment by making their work more obscure so they won't be replaced then they probably aren't a very good hire. Companies tend to want to retain highly competent talent - not mediocre people or incompetents.

I'm definitely not arguing for mediocrity since I'm not arguing for obscure code. I'm saying that companies that have much turnover (which a lot of software companies have) won't choose a language that doesn't offer them a huge pool of programmers.

I think most shops that are run by people who have experience with software development will tend to choose languages that are fairly mainstream. Yes, the recruitment pool is bigger, but so is the entire ecosystem. Tooling, libraries, books, documentation, easy to find knowledge, long term committments etc.

There are few things that are more frustrating than working on something that so few people work on your search results when looking for solutions to problems come up nearly blank.

And despite what high thoughts we programmers have of ourselves: we spend an inordinate amount of time Googling things :-)

In order to have a meaningful discussion, let me expand on what I mean by "boring".

To me, a boring language is one that is easy to learn, easy to read and which encourages somewhat homogenous code - through coding style and common idioms. It has few surprises and few dark corners.

If a language still has major aspects that practitioners struggle with after 2-3 years, it isn't "boring". If you have to expect every codebase in the same language to have different coding standards, it isn't boring.

A boring language is one where what you've learnt on one project has a high probability of being usable in a completely different company or on a project that does something entirely different from what you've done before. It is a language that disappears because you spend more time talking about how to solve problems and less time talking about how to express those solutions in a language.

Now I can understand that some people like the excitement of exploring new languages. And you should. On average you should try to learn at least one new language each year. Even if it is on your own time if your employer is stingy.

However, asking your employer to let you use some language on a project because you find it "exciting" is a big ask. Projects tend to have deadlines, they have quality targets, many projects are built by teams, so entire teams have to be conversant in the language. What if you need to scale up with more developers? How easy is it to recruit programmers in a given language and at a given level of proficiency? How about maintenance after you have left the company or grown tired of whatever language you were once very excited about? And what happens when a language goes out of fashion but the company running the code still depends on it?

People with responsibilities are not scared of talent. Rather the opposite. They depend on talent.

They may, however, be somewhat scared of "specialists" (in the sense of programmers who will insist on niche languages or niche technologies) because if you pay someone to write code, you don't want to have to search for "specialists" to maintain it or limit the size of your recruiting pool. You have an investment to manage.

As for mediocrity: I think you would need to back that argument up with something. Do you really think the choice of language is what makes a programmer brilliant or mediocre? Then why doesn't reality agree with that view? If that were true then for every Donald Knuth, Fabrice Bellard, Jeff Dean and John Carmack there should be lots of even better programmers that do all their work in "exciting" languages. Where are they? They should be far easier to enumerate than the people who use "boring" languages?

If you think caring about the long term value of code is "boring": try to start your own company and bet your own money. If you are that convinced of how "exciting" languages lead to brilliance it shouldn't be a tough choice to make.

> In order to have a meaningful discussion, let me expand on what I mean by "boring".

This is the problem with how boring is used. According to Wiktionary it just means "unable to engage or hold the interest". So how can this be a criteria for using a technology? Why do you redefine the word and deviate from the common meaning? What is the reason behind this? Why is it necessary that your understanding of "boring" has to be explained? It is a simple word which had a defined meaning for hundreds of years probably.

But here is my take on it: This was simply an attempt made by clever people within the industry to redefine the word so that they do not have to explicitly say what they really mean. Just call things without having a proper definition "boring". What is not boring must then be exciting. If we define boring to be good (which is actually crazy since the word itself has a negative connotation) everything exciting must be bad. Simple black/white thinking. And now we can simply call things "boring" as we like and indirectly call everything else bad.

Also it totally makes no sense to call technologies "boring" or "exciting" and thereby categorizing them for suitability. "Boring" is highly subjective. Some find Go boring, other find it to be exciting. Some find VBA boring, others experience it to be exciting. This has nothing to do with how complex, simple, fancy, hyped or old a technology is.

> To me, a boring language is one that is easy to learn, easy to read and which encourages somewhat homogenous code - through coding style and common idioms. It has few surprises and few dark corners.

Why is that boring to you? To me a language where I could easily reach a common ground with my colleagues so that we can together focus on business problems would be a very exciting language.

> Now I can understand that some people like the excitement of exploring new languages. Excitement of exploring new languages does not mean that the explored language is exciting/boring (whatever that means)

>However, asking your employer to let you use some language on a project because you find it "exciting" is a big ask.

One should only ask the employer to be allowed to use a new language if there is some benefit or if it is an experiment which will clarify whether it will be beneficial. This has nothing to do with boring/exciting.

> They may, however, be somewhat scared of "specialists" (in the sense of programmers who will insist on niche languages or niche technologies) because if you pay someone to write code, you don't want to have to search for "specialists" to maintain it or limit the size of your recruiting pool. You have an investment to manage.

True. This is where the risk parts enters the stage. It is always like this. The specialist might put you ahead of your competitors at a high risk while the generalist will keep you with the average at a low risk.

> Do you really think the choice of language is what makes a programmer brilliant or mediocre? Where did I even say this? What does talent have to do with the programming language being used?

> Then why doesn't reality agree with that view? If that were true then for every Donald Knuth, Fabrice Bellard, Jeff Dean and John Carmack there should be lots of even better programmers that do all their work in "exciting" languages. Where are they? They should be far easier to enumerate than the people who use "boring" languages?

Uh, with what view? Definitely not mine. Who says that the programming languages those people use are boring? I think you can simply choose any language and you are likely able to find 4 people who are potentially better programmers than the four mentioned. Not because they are bad or mediocre but because of the sheer amount of programmers in the world. Those four are simply famous but we have no proof/numbers on how "good" they are compared to the rest of the world.

> If you think caring about the long term value of code is "boring": try to start your own company and bet your own money.

And again. I never said this. Which definition of "boring" are you using here?

Also that sentence is dangerous. Even if caring for long term value would be boring (for some it might be exciting!) the real motivation for this is to secure previous investments which is a perfectly valid reason. Whether the way of achieving is is boring or not is completely irrelevant. And even further: If I would find an exciting(for me, since excitement is subjective) way to secure or even grow my investments I should not do it because it has to be boring? Securing investments or caring for long term value has nothing to do with whether something is boring or not.

> If you are that convinced of how "exciting" languages lead to brilliance it shouldn't be a tough choice to make

And once again. I never said this. I just said "Also we are scared of specialists and talent and want to stay mediocre" which not even mentions programming languages/technologies. I was just referring to how the word "boring" is misused and what might be the true intention behind the usage.

So in a summary: Good or bad decisions in business have nothing to do whether something is boring or not. This should never drive business decisions. But it seems like the word "boring" does.

Now you are being contrarian for the sake of being contrarian.

Yep. I use Go precisely because it's boring.

Wow! 10 years already. I'm the founder of HashiCorp and I'd love to share my thoughts.

Credentials: I started using Go around 9 years ago and since then I've built a company of over 1,000 employees with ~250 engineers that write Go full time. We maintain dozens of open source projects and libraries (Terraform, Vault, etc. etc.) all written in Go. We've shipped commercial products that are used by a significant percentage of the F500. Vault for example serves trillions of secrets per year for _one_ company that we know of.

I could probably write pages and pages so I'll try to highlight a few things. Go is not a perfect language or community by any means but I love it.

NOTE: Folks often read this feedback with the opinion of "but language X can also..." or some variant and I hope that doesn't come across. My feedback is not against any other language unless I'm specifically using an example. Another language might solve all these same problems for you! I'm just sharing how Go has been fantastic at these aspects for us.

== Junior and new-hire friendliness

Go is a very simple language. From the beginning of the company until now, we can hire people who have never used Go, point them at a couple resources (i.e. Tour of Go), and they can be committing to production-grade projects within a week. It is incredible.

Go makes it really hard to do anything that isn't obvious. The cost of this is often verbosity or repetition. But the benefit above is well worth it in my opinion. I know lots of people disagree with this but I personally prefer to repeat "if err != nil" 1,000 times versus introducing new control flow options that now must be educated.

I love telling new folks (junior or not): read the file top to bottom, that's how it'll execute. And for the most part, that's true.

As someone who built a rapidly growing team/company around Go, this has been indispensable.

== Flexible

Any "general purpose language" can theoretically write any software. However, I'm sure we'll all agree that its easier to write some software in certain languages, and that's a good thing.

However, I've been blown away by how flexible Go has been without feeling forced. We've written desktop CLIs, web APIs, distributed systems, security software, networking software, infrastructure software, accounting software, bots, etc. all in Go.

And most of these categories are at a scale of millions of downloads per year and successfully in production.

It's very cool to see that coming from a prior major background in Ruby where there were significant tradeoffs when doing these things. It was _possible_ of course but you had to really understand the tradeoffs you were making. In Go, there's certainly some tradeoffs but they've been minimal and Go has just worked.

As both an individual contributor and manager, this flexibility has been awesome in building a company around.

== Cross-platform Compilation and Statically-Linked Binaries

Since the first day I adopted Go 9 years ago, Go has encouraged and simplified static binary compilation and simple cross-platform compilation. Today you basically set an env var of what platform you want to target, run `go build`, and it usually works.

You still have to learn of various gotchas around platforms (API availability, filepath awkwardness, subprocess/signal behavior, etc.) but the compilation aspect alone makes it so much easier to tackle this task.

My previous experience of at-scale software I built was Vagrant in Ruby, and making that work across platforms was a constant, enormous challenge. I had to spend a few months full time just to get installers to help setup the proper runtime environment across platforms.

Looking at this also from the perspective of rising popularity with ARM systems and other less standard architectures (even Power for enterprises...), this property of Go has been very important.

== Culture

The culture surrounding the Go community is generally one that is very philosophically aligned with how I view software. I would roughly describe it as pragmatic & measured.

I think this culture is exactly why many people dislike Go (or seem to "hate" it which I think is a pretty wild emotional reaction to a language for programming computers but I digress).

The Go community doesn't chase the next hot thing or rush to implement the "state-of-the-art" without a very measured, pragmatic approach. Some view this as Go being inferior by some definition but I view it as a huge benefit. Go core in particular appears to me (I'm not part of the core team so purely an opinion) to be a measure-twice-cut-once (or maybe measure like 8 times) approach. And I love that.

== Fun

At the end of the day, the language is generally fun for me. I like writing it. And that's important, too.

== Conclusion

I'm very lucky that Go took off as a language in the industry I built my company in. It certainly wasn't that way when we started using it (it was pre-1.0, pre-Docker, pre-K8S, the majority of infra sw was still in Ruby).

I feel that the language is super productive (we built Vault 0.1 in ~6 weeks for example), it has shown to scale to the largest needs and does so stably (Vault is in the hot path of every trade in one of the major US stock exchanges and has never gone down there), and we've been able to build a large company and active OSS community around it.

Hands down a successful choice for us with zero regrets.

I'm in strong agreement with this summary.

Although I don't write Go so much anymore, I had the benefit of working in Go for a few years on a team that developed a strongly shared style. A few years later, one of those team members sent me a package they had been working on, and it was an absolute breeze to understand in comparison to the Rust and TypeScript I had been working on. This is not to knock on Rust or TypeScript, as there are absolutely solutions that are better expressed in those languages, but if I'm honest, the vast majority of software is "plumbing" - get data, manipulate data, put data - and Go is an extremely pragmatic choice for this in my opinion.

A few other thoughts...

I really don't take issue with `if err != nil` and though I like Monadic types, I'll take errors as values over exceptions 100% of the time.

I've also written a lot of CLIs and Go's build process here is superb.

I think the Go community has a strong testing culture, and although I like BDD, the work put into the std test tooling has really helped form that.

Great summary.

> The cost of this is often verbosity or repetition

This may be seen as a cost but it's also a significant gain during debugging.

Knock wood man. Just shared this with my terraform but also Java team. Also thanks for terraform. It’s no golang to use, but I can read the nice go code and understand what json-ish declarative assembly-language-ish stuff to type to get the weird Jacky deployment thing I want in AWS. And then everyone can repeat it (with or without modification) as needed.

I'm going to come out and risk getting downvoted but I strongly dislike it. I haven't used it in a few years so maybe some of these things have been fixed.

- Type system sucks

- No decent package management system or dependency system

- No generics

- error handling is horrible.

... My fav lang right now is Typescript... You can do frontend stuff, backend stuff (with node). Great tool support, etc.

Frustration over not having a proper parametric polymorphism (Also known as “static generics”) so I cannot write elegant functional style code to promote more code reuse. You wonder how do you do some kind of Map/Filter/Reduce in Golang? Check out go-funk in Github but bear in mind they use reflection to emulate it so the performance can be very dissatisfying.

This is the biggest pain point of Go throughout my journey and I have been waiting so long for Go 2! PLEASE!

I don't hate it, but I don't love it either. For the projects I've used it with, it works fine up until I need some complex data structures, then it falls apart into bunch of runtime-casting-infected code. Also, I don't find that it makes threading less error-prone than a language like Java.

I've switched most of my personal projects over to Rust and so far I'm not looking back...

I really like it. It makes it really easy to have easy to develop monoliths--Go will scale (vertically and horizontally) and can easily handle doing everything. There are some annoyingly verbose and not DRY parts, like adding instrumentation or context, but it leads to less weird side effects.

The tooling is also very good, though I wish more of golangci lint would move into stdlib so I could just run golint.

It does lots of things right

1. Built in support for tooling, testing, formatting etc

2. Trying to make concurrency mainstream

3. Large and developer friendly standard library, there is hardly any programming language where web backed can be written without using a framework or a library. Go is one of those.

I love it, and consider it surprisingly and even “unreasonably” effective.

My favorite feature is its simplicity. It imposes very little cognitive load, allowing you to spend more time thinking about the problem you are trying to solve and less time on language gymnastics. I am way “over” being impressed with that kind of cleverness. I am much more impressed with what a program does than with cleverness at the code level.

Go mostly picks the right features and avoids those that just add complexity, and it mostly gets that right.

It doesn’t get everything right. I am disappointed with channels and I do think it needs generics, though I hope the latter do not add much complexity and are not overused.

Decent for writing web backends, APIs, RPC services. The high number of libraries also help. The language is very simple, which is good or bad, depending on who you ask.

Tooling could use a lot of work though. After switching between vim-go, gopls, and a handful of other language server implementations, I've given up on auto-completion, definitions etc altogether. Maybe it's my setup...

Overall, I'm pretty happy with running a fairly big Go app in production. Would I choose Go for my next serious project? Most likely.

Have you tried GoLand?

No, but I've heard really good things about it, and even tried out the demo when it initially came out a few years ago. I was hesitant to change my Vim workflow though.

Thanks for mentioning it, I think I'll take another look at it now. Having a real IDE probably has more benefits once the code grows beyond tens of thousands of LOC.

FWIW IdeaVIM is the closest thing to "real" vim around, so you can at least use reasonable key bindings with Goland (and all IntelliJ-based IDEs)

Sorry you've had issues with gopls, the gopls deep completions are one of my favorite things!

At this point gopls works way better than godef+gocode ever did for me.

I won't re-hash the many great things about Go that have been said. Instead, I will explain some of the reasons why I continue to use Java (for now) instead of Go. Go is seen as one of the best alternatives to Java, and some see it as a better Java. It has won over many shops from Java. Therefore, I think these reasons are relevant to this question, and not just me doing the usual "my language of choice is better":

1. Tooling and library ecosystem of Java is still better. There are tools for pretty much everything, including high-quality commercial offerings. For example, IDEs, profilers, build systems, other languages that run on the same VM. Nearly every major SaaS I have ever integrated with has a Java SDK.

2. Weight of big players behind the ecosystem. Big companies all over the world are heavily invested in Java. Even Google is still more invested in Java than Go (internally, and in Android, especially). This makes Java an insanely safe choice for us small players.

3. The many, many warts and deficiencies of Java (and parts of the Java ecosystem) are well-known. Experienced developers, at least ones with good taste, know how to mitigate them without pain. For us, a language with fewer warts, like Golang, is therefore not as big of a selling point. It's like getting your hands dirty. When your hands are clean, you are reluctant to make them dirty. When your hands are already dirty, touching dirty things is not a big deal.

4. Golang is not the only language evolving. Rapid improvements to the language and JVM are already here, and more, bigger changes are planned (e.g. Loom, Valhalla) that will nullify some of Go's advantages.

Python and Go is a dream stack. Prototype services in Python and when performance is needed, do Go implementation.

Things I like about Go (in this order):

* useful, not shiny. No new framework every year, just for the sake of it. An actually readable spec. Changes heavily thought through before being introduced. A programming language isn't a place to move fast and break things, as changes often cannot be undone.

* Readable code. You can figure out what a piece of code does pretty easily. Even if you can't do so at first glance, you know where to look next.

* Simplicity. I personally like having full understanding of things. This applies both to the language itself, as well as to the tools (go test, go mod, go generate etc). This is pretty subjective.

* The formatter. As a screen reader user, aligning things with spaces so that they would form nice little tables is a hard task for me. I'm happy I don't have to do so.

* Compiling programs as single, static binaries, assets included.

Some small things I don't like (also in that order):

* No try macro[1]. IMO try was a better solution than exceptions, and I found the arguments against it not that compelling.

* Overrelliance on struct tags, which shouldn't exist at all IMO. I don't want to couple my business logic, storage and view layers. Options for i.e. JSON should be specifiable at call time.

* No generics, or, even better, zig-like comptimes. They're simpler and more powerful, though they wouldn't probably neatly fit into go2.

Overall, I'd give go about 8.5/10.

[1] https://github.com/golang/proposal/blob/master/design/32437-...

Its a good compromise between python and java. We gradually moved our codebases from python to golang. This has reduced our infra cost quite a bit. Some of our mission critical stuff is still in C++ and I'm fairly certain golang would not replace any of those. I hope the golang community takes a thing or two from rust in terms of package management.

> I hope the golang community takes a thing or two from rust in terms of package management.

Go's dependency management was designed after analyzing Cargo and its problems: https://research.swtch.com/vgo-mvs

It's a very mediocre language with many poor design decisions. It only got traction because of the Google name behind it (it's predecessors designed at other companies were barely recognized).

It's ok for writing small tools or simplistic web servers. For complex non-trivial applications, it is very poor at modeling, which introduces a lot of friction when dealing with larger code bases. Performance is adequate and nothing to brag about (it sacrifices throughput for latency) and compiling speed took a hit when they rewrote the compiler from C to golang. Linking times are quite abysmal for larger projects.

Depending on what you want to do, there are superior alternatives. Java and C# are both better in almost every aspect (and keep closing the gap when it comes to low latency and small footprint), and Rust is also a potential viable option if you are running in a memory constrained environment.

It's a lovely language perfectly suited to building network-oriented services in market-driven organizations.

It's basically the only language in meaningful use that optimizes for the right things in this context: productivity at the team scale (rather than the individual scale) and making things easier for code readers (rather than code authors).

It depends on your project. I used Go since 2011 on multiple projects, two of them are http://github.com/chrislusf/seaweedfs and http://github.com/chrislusf/gleam. One is a distributed file system, the other is a distributed computation.

* Refactoring is powerful


Nobody can design well upfront for a large project. Constant refactoring is need for large projects. Go is super good on this.

* Generics is not ready yet.


The distributed computation project was re-written multiple times. I tried different approaches to address the generics problem. I even tried to make it work fairly well with LuaJIT, but still decide to change again due to the need to learn a different scripting language and losing the type checking.

Go is one of my favorite languages. I love that I can build things quickly with just the standard library (my personal website uses no external packages).

There are other "sexier" languages that I sometimes enjoy even more (Elixir, Rust), but Go is rock solid, easy to use, and has never let me down in production.

I use Go for many years now and I quite love it. Not for everything I do, but for a large part of my programming. In my eyes, it hits a sweet spot of not having too much complexity, no magic and being an efficient language. It has a fast compiler, a good module system and a strict type checker. Certainly helped by the module system, there is a rich set of libraries in Go. Compiling to a single file executable also solves a lot of deployment problems.

In my eyes, Go is also the modern times successor to the Wirth family of programming languages. In Europe, for a long time the Wirth languages were far more popular than C was. Only in this millenium they faded from public perception, but with Go many of their concepts and philosphies come back, even when dressed up like a C language :).

I like it for the compiler. So easy to compile Windows application on Linux and vise versa - supporting all major OSes. If you had to go with C/C++ it becomes a much uglier solution and I hate Java.

The most productive new language.

- great community

- ample libraries

- ecosystem support

- no breaking changes (yet)

- no magic, limited and simple

- inbuilt testing/profiling/dependency manager

I learned JavaScript in 1999, PHP in 2003, Ruby in 2009, Python in 2015, and Golang in 2018. Modern PHP (despite its historical reputation) and Python are pretty much a toss-up to me (both have warts, both are a blend of functions and objects, and both can be written very well or very poorly). Ruby and JavaScript are more elegant (I like how everything is an object), but Ruby has historically been very slow and JS developers have forgotten how to program (see left-pad, or the dumpster fire of npm modules).

In July 2018, I decided to go all-in on Golang. I like how fast it is, I like that it forces me to pay attention to my types. I wish that error handling felt less like yak shaving, but I understand the rationale. I write task-oriented servers/daemons, and CLI tools mostly, and it's great because it compiles down into a single binary with zero dependencies (which none of my other languages can claim), and it's extremely fast. It compiles easily for a variety of OS/Arch combinations, and as someone who can follow instructions to compile C/C++ code, dependency management is FAR easier than having to track down which "dev" versions of packages I need to install from whatever flavor of Linux or on macOS (via Homebrew). Overall, it's the best of many worlds for producing something that's easy for other people to consume.

These days, I do a lot with AWS, Docker, Security, Terraform, etc. I'm also going back to my web roots a bit on the weekends and looking at building a GraphQL API on top of DGraph, and the tooling for Go is really good. Lots of static analysis and linting tools are available to help me write the best code I can, and teach me when I've done something poorly.

My only big complaint is when I'm parsing JSON of which I don't know the shape. I end up with this nested mess of `map[string]interface{}` types that I have to sift and cast over. Even when I know the shape ahead of time, I still need to do the work to model it as a struct before I can work with it sanely. This is something that JS, PHP, Python, and Ruby all do with far more ease.

I still write in multiple languages frequently, but Go has become my go-to for a lot of things over the past couple of years.

When you know the shape, you can just use https://mholt.github.io/json-to-go/

I have no interest in Go because it is so limited, more limited than other languages that I have already moved beyond, such as C#.

I am far more interested in cutting edge languages such as Rust (borrow checker) or Idris (dependent types) or even C++ 20 (template madness), since these will allow me to express things that I cannot express in other languages. What does Go bring to the table?

Note that my perspective is of someone who works in a small team of experienced programmers, not a large organisation with more junior ones.

> What does Go bring to the table?

junior devs

edit: it might sound like sarcasm, but i'm deadly serious.

Golang brings a language that can’t be abused by programmers like you who will write incomprehensible template madness for all other devs on your team.

> What does Go bring to the table?

A still maintainable code base 5 years along.

No language will magically give you maintainability. I've worked on several year old golang projects and they've been terrible, made worse by the fact that golang doesn't scale well for teams or larger programs.

> No language will magically give you maintainability.

That's true. But Go will certainly help with it.

> made worse by the fact that golang doesn't scale well for teams or larger programs.

What? That's one of the major recognized selling points of Go.

> That's one of the major recognized selling points of Go.

It's exactly just that, marketing fluff and hype made up by the golang team that is not backed up by actual evidence. Look up several other comments in this thread that mention exactly that. I'm not the only one challenging their false claim.

There were never any specific language features that make golang good for large projects. Quick compilation was touted at the beginning, but that went out the window when the golang compiler was rewritten in go. For large projects, it's in the same ballpark as Java and C#, and in my experiences, even slower especially for incremental compilation due to the bottleneck of the linker. Secondly, its features work against scaling for large programs and teams (no generics, verbose and error-prone error handling, interfaces are poorly thought out and optimized for minority use cases at the cost of usability and correctness, etc.)

You seem to be writing that a simple language is somehow harder to scale for large teams. My experience is the exact opposite - too expressive language make devs have different dialects and be too "clever" for the teams long-term good - and I'm pretty dumbfounded that you can try to make sense of that.

> verbose and error-prone error handling

Verbose (and consistent) is exactly what you want for large teams. What's error prone about it?

You're tone is also suspiciously overconfident which just signals that this is more a you issue.

Do you have an example project that has problematic build times in Go?

> You seem to be writing that a simple language is somehow harder to scale for large teams. My experience is the exact opposite - too expressive language make devs have different dialects and be too "clever" for the teams long-term good - and I'm pretty dumbfounded that you can try to make sense of that.

It doesn't matter what language you use - cleverness will come in anyways. Only the way it is achieved is different. Cleverness in the case of go will just be difficult to understand reflection, where it will be difficult to understand types in Haskell.

With go, every line of code will be simpler when compared to Haskell (picking Haskell as an arbitrary example here). However, the go program will also 10x bigger. And complexity grows over proportional with the amount of code. In the end, the understand what the whole thing is doing you will have a harder time compared to the more dense but also much shorter code in Haskell _if you are very familiar and experienced in both languages_.

I don't agree that dense code is easier to understand at all. It's both harder to understand and much harder to debug. I don't now why more code is in anyway reflective of how hard it is to understand. That would make the Redis code base harder to understand just because it has more features now than 4 years ago. That's just not true.

Haskell is also a good example, if it was easy to understand, maintain etc. we would have seen a good examples of large-scale apps and projects being developed in it by now. But we do not. Barely a single one.

> _if you are very familiar and experienced in both languages_.

This can be translated to "this is so cognitive challenging that you need to be really well-versed to not fuck this up now". That's not a good user-interface for something to scale on large teams.

> I don't agree that dense code is easier to understand at all.

I think we just have a different definition of dense code. Here is an example that shows my view on it.

Dense: adult_users = users.filter(it.isAdult) Not dense: adult_users = list.empty for(i = 0; i < users.length; i ++) { maybe_adult_user = users.get(i) if(maybe_adult_user.isAdult) adult_users += maybe_adult_user }

The first example is not only just "less" code in the sense that the filter function was also defined somewhere before and then just used. No, it is more dense code because it reduces subjective complexity by shifting some of the complexity to existing code _with which the developer hopefully is familiar with_. If the developer is not familiar with it, then the code is actually _more complicated_.

The same thing happens to language features, not only to code reuse. Some languages have generics, some don't. Without understanding and familiarity with generics, they will make everything more difficult. Once they are understood, the developer has a productivity advantage (assuming that generics are helpful).

However, gaining this initial knowledge and familiarity is very hard, hence not so many people use Haskell or similar languages and many more people use javascript and python and php, so you see more large-scale apps written in them. But we also see that all big companies eventually struggle with the not-dense-enough code and build on top of these languages. Facebook with php->hack, stripe with their own version of Ruby, ...

> You seem to be writing that a simple language is somehow harder to scale for large teams

I've worked on a large Scala program before, and because the team generally agreed not to let things get too crazy, it was very manageable. However, I've seen other programs in complex languages get out of hand.

There is a sweet spot in terms of language simplicity/complexity and usability. Make it too simplistic (like golang) and the low modeling ability and inexpressiveness of the language will result in shifting the complexity into the code and onto the programmer, there's no running away from it, it's just reality. IMO, languages like Java, C#, and Kotlin do a much better job at balancing features with modeling power and expressiveness, without sacrificing simplicity.

> Verbose (and consistent) is exactly what you want for large teams. What's error prone about it?

I've seen many instances where errors are ignored by mistake, e.g.:

    a, err := foo()
    if err != nil { ... }

    b, err := bar()
    // oops, error not handled and compiler doesn't complain
    c, err := baz()
    if err != nil { ... }
not to mention things like

   defer file.Close() // Error not handled
and more insidious occurrences (e.g. when was the last time you handled the error for fmt.Println()?)

Consistent is good, verbose just for the sake of being verbose isn't. And honestly golang is not that consistent (e.g. the entire nil interface is not nil issue is just bizzare)

> Do you have an example project that has problematic build times in Go?

At my employer, we use a monorepo and build with bazel. Builds routinely take several minutes, and even more on "less powerful" machines (e.g. 4 cores). Linking time is abysmal, and we routinely end up with 50+MB binaries. It's closed source so I can't share them.

> I've worked on a large Scala program before, and because the team generally agreed not to let things get too crazy, it was very manageable. However, I've seen other programs in complex languages get out of hand.

In my experience this kind of gatekeeping eventually breaks down due to deadlines and similar. There's also a slow rot that always creeps in, and unfortunately seniority doesn't always keep "clever" solutions and general over engineering out.

But it looks like we'll have to agree to disagree here.

> I've seen many instances where errors are ignored by mistake, e.g.:

It's true that there's sometime bugs due to mistyping err != nil sometimes. But I have not noticed that it's too often or that it's somehow is such a large problem that it out-weigh the benefits of the clarity and simple flow brings.

> At my employer, we use a monorepo and build with bazel.

Hard to compare with an anecdote like that, I just haven't heard much complaints regarding compile times, if anything the opposite, and certainly not that it's a significant problem in a pros/cons comparison.

> In my experience this kind of gatekeeping eventually breaks down due to deadlines and similar.

I don't disagree. That's why I said the sweet spot is much closer to Java/C#/Kotlin.

> But I have not noticed that it's too often

That's the problem though. Those silent difficult to detect bugs are what affect reliability and safety. With exceptions, you're going to have to explicitly ignore them, whereas with golang errors, it's much easier to misuse them. Not to mention the fact that golang errors are basically strings, which makes dealing with them very error prone.

> Hard to compare with an anecdote like that, I just haven't heard much complaints regarding compile times, if anything the opposite, and certainly not that it's a significant problem in a pros/cons comparison.

My guess is that most people don't use golang for large projects, and few who do don't care about the long compile times.

Is it really though? Go is not expressive enough to model your problem domain in a concise and coherent way, which leads to very large code-bases compared to other languages. Whilst each line is easy to understand in the small (because there is very little abstraction), it becomes extremely difficult to understand how the code works in the large.

I’ve worked on both large rust and go codebases and I can’t agree with you.

The lack of algebraic data types makes it hard to use, as opposed to other languages. Even Python now has ADTs with type hinting and Microsoft's Pylance language server in VSCode.

After disliking it for 6-7 years, because it doesn't offer the amount of abstraction that others languages offer (Scala, Haskell, Java), Go has grown on me.

I got too caught up in designing elaborate abstractions in those languages. I couldn't avoid it either, since other libraries would also use elaborate abstractions.

With Go, I just write plain dull code, against a suite of good dull libraries. Ultimately I spend less time writing programs (typically internet services) in Go than other languages.

Without too many distracting abstractions I find it easier to evolve a codebase over time rather than overengineering at start.

Insert standard whinge about the lack of generics and the presence of garbage collection. Generally, a type system at least as strong as Hindley-Milner is table stakes for a new language in the 21st century; its omission from Go is a glaring fault that cannot be ignored. And the RAII pattern beats GC in 90% of cases; for the rest, reference counting may be employed. GC is obsolete, and better technologies to supersede it have been around since the 90s.

That said, Go is fun to work in, and is great for constructing microservices that are truly "micro" in terms of small footprint of code and dependencies. Today, enterprise development shops that embrace the microservice pattern hew stickler-like to the "do one thing" bit of Unix philosophy, but slack off on the "do it well" bit, bringing in dozens of dependencies to serve, say, a list of employee names and IDs out of a table. Go's opinionated minimalism helps to mitigate this trend, but Go has also proven itself up to large-ish development tasks like Kubernetes as well.

So that's Go -- capable, but not ideal. A worthy choice if you want to get something out there without fighting the borrow checker -- though you may wish to consider that time spent satisfying rustc now is more than paid for by maintenance time saved later.

It's fast, memory efficient and you're productive in it. It also follows write once, run anywhere without any dependencies.

Downside is no generics but otherwise great.

The structural type system is something I really wish other languages started using.

Structural types are one of those things that annoy me in both theory and practice, when I have to touch Go code.

In theory, it's annoying because it prevents you from expressing constraints (think: monad laws, or Rust's Sync/Send traits). In practice, it makes stuff like go-to-definition nearly useless, which makes it a pain to read code written by other people. Especially when combined with the almost sadistic module system.

When a struct implements an interface one has to keep guessing that it really does so (unlike Java, C++ there is nothing in the syntax of go that would show interface inheritance as a fact), and that really doesn't help the poor guys who have to read/understand/maintain some code. Why did they do that to a language that is based on interfaces?

I often find it useful that implementing an interface is implicit. For example maybe I'm using a library that provides a struct A. I can make another struct B that mocks that functionality for testing, and then everywhere in my code use an interface C that encapsulates A and B. That wouldn't be possible if A had to explicitly declare that it implements C.

If you want to explicitly declare that you implement an interface.. just put it in the comments, or it's common to see a line like this after a struct declaration:

  var _ C = (*A)(nil);
It's a way of asserting that A satisfies C.

- that's an optional convention, and it is not always followed

- not easy to search for this pattern.

You are right that go interfaces give you some flexibility with mocking, but i still think it's a questionable choice.

If the problem is finding implementations of an interface, that's provided by the standard tooling. If you're using VSCode just right-click on an interface and select Find All Implementations.

actually i was not using 'standard tooling'; was using vim with YouCompleteMe plugin for code completion. I couldn't get all the VSCode plugins to work well, somehow.

While sometimes it would be nice to have a source-code confirmation, that struct Foo implements the interfaces Bar and Baz, there is one huge benefit of not requiring to declare the interfaces a struct implements: a struct can implement an interface the struct creator doesn't know about. So you can design new interfaces, which for example structs from the standard library or any third party library happen to implement. You don't have to change the original libraries for that. This is quite a benefit for modularity.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact