The verbosity of Go (and even Java for that matter) is 100% worth it for me for all those reasons. It allows me to iterate fast, make changes with confidence and not have to feel guilty that my test coverage is only 92% because I am not going through the trouble of covering some branches of code that returns an error message (that would otherwise require annoying effort to trigger in your tests) as the compiler guarantees that all those
`return errors.New("some error")`
will execute if the error condition is ever hit in production which was not the case when I was working in python unless I went through the effort of having my test cover that bit of code.
Go is a language for teams. All the cogs in the machine will spin, they will almost never get jammed, but nobody's fingers are fast enough to be particularly more productive than anyone else. Maybe this is the gray reality of mature collaborative software development. But every day I work in Go I get this gnawing feeling that software development could be so much better, that we could have been done weeks ago.
I sometimes feel that also, but strongly suspect it is a way of thinking that omits very important parts of the equation. When you build things, most of the time you're not spending writing code, but deciding what code to write. That is the work you want to optimize and Go makes that simpler, I think, as it is often obvious how something should be done in Go. With e.g. Python, there are too many options and that, I suspect, actually slows you down. For me, using Go feels like steady progress all the time, while e.g. JS development involves short bursts of productivity followed by long contemplation and sometimes backing out or refactoring a lot of stuff. Could of course also indicate my JS skills suck I guess ;)
There's so much plumbing that you wouldn't have to write AT ALL in a high-level language.
That is great if you are building the environment for your application, but it's not great if you are building the application, because a differentiated app will always possess a unique application "vocabulary" - it's a thing that gets thought about in terms of a medium that is not the host language. Go resists extension, so most teams opt for primitivism rather than developing proprietary compilation mechanisms to express their spec. Everyone gets worried about the risks of generating code and how to maintain that: it's a "known" to have a larger fungible uniform codebase. The result is that "slow and steady" feeling when working with Go to do relatively high-level things - it lets you do it, but you have to work hard.
These tools also compose really well, despite the absence of generics, because they speak the common language of byte arrays. Now that I think about it, Go is a language for processing byte arrays in the same way that LISP is a language for processing lists. Go is just more subtle about this specialization, and invites you to use it for other things it is less good at.
But line counts are nearly meaningless. Something like a quarter or a third of those lines are going to be a single closing brace character. So mostly we’re saying that Go’s style involves a lot of beeline characters, but that doesn’t tell us much about even boilerplate much less complexity. And to be quite clear, Go does have more boilerplate than many other mainstream languages, but line counts are a particularly bad way of measuring it.
There's the famous line from GLS about Java:
> We were not out to win over the Lisp programmers; we were after the C++ programmers. We managed to drag a lot of them about halfway to Lisp.
And probably there's some equivalent idea about Go not trying to win over the Clojure programmers, but rather dragging the enterprise Python/Ruby/JS/Java programmers halfway to C.
That's just Python and its endless edge cases, caused by everything being special-cased into the interpreter.
If you were using Lisp, the macro equivalent of your ________dunder________ method would likely have no edge cases, so the next guy who makes a change with only a partial understanding wouldn't end up introducing that performance degradation.
Surely that must depend on the problem space you're working in.
My impression is exactly the opposite of yours, though I must confess it took me a bit of time to build up an armamentarium of patterns that I could apply to a problem. This seems like the flip-side of the "go is a simple language" coin.
You should definitiely give Rust a try if you haven't. It has all the pros above without the boilerplate (and even better type safety)
I get the concurrency gains, esp with older versions of dynamic languages without native support for coroutines.
But the test coverage argument seems quite contorted. If you're accepting 92% coverage because the "return an error" lines are not worth testing (fair) then why is that harder to achieve in, say, Ruby or Python? Is the argument that your 92% coverage in Go is _effectively_ 100% because compiler?
I like and use both of them but if you're trying to argue that the testing problem in Python is the same as that of Go or Rust, that argument will fail.
Wasn't trying to make that case, but I can see how my comment could be read that way.
I was genuinely trying to understand what the parent comment meant. Specifically whether they were saying error control code is _the_ critical but hard to test part of a code base (which I don't think is typically true), or rather error control code is just an example among many of less frequently executed paths in code, which categorically is easier with a static type checker. I think they meant the latter and I don't disagree with that.
Additionally with a dynamic language where the codebase has less than a 100% test coverage I would find it very hard to refactor anything without sweating bullets. The compiler is giving you a free test suite.
After you get some experience with Go, you might want to explore other modern statically typed programming languages (I suggest Kotlin and Rust).
When you know the strengths and weaknesses of each, you'll be able to pick the best one for the job. And the concepts learned often translate from one language to the other, which will make it easier to pick up new ones in the future.
Kotlin and Rust also have the "green threads" [1] that Java lacks, both of them can also produce a native binary giving you fast startup. So the claim that "Java solves all the problems that Kotlin does" is not completely true.
Finally both Kotlin and Rust can let you write safer concurrent code, because their type systems actually can enforce constraints on what's shared between threads, and what's immutable and safe to accesss. Go gives you nothing in the type system, but there is a thread sanitizer.
[1] Here's a Kotlin program that spins up 100K coroutines: https://github.com/Kotlin/kotlinx.coroutines/blob/master/kot...
There’s a pretty strong consensus among people who have used Go professionally (irrespective of whether or not they liked the language on balance) that the ability to look at any project and immediately understand it is a Good Thing, and this property follows directly from the language’s simplicity.
That doesn’t mean it outweighs all other criteria—we can say that there is a tradeoff between readability and expressability or something like that—but it absolutely follows that simplicity is a merit.
To boot, some other things that I would prioritize well above any particular type system feature set (at least features in excess of what Go provides) include: ecosystem, reproducible build tooling, general tooling, performance, a solid standard library, friendly community, static compilation by default, easy learning curve. Note that “tooling” must also be simple to be a boon—I don’t want to have to learn a DSL to script my dependencies, wire up test targets, build static artifacts, etc.
It's hardly constructive comparing these two languages due to their different objectives. Yet there's this constant belittling of Go as if it was a menace to Rust somehow.
In professional environments, this kind of emotionally loaded tribalistic judgement has not place. Much less in engineering.
This problem has even been acknowledged by a moderator on HN: https://news.ycombinator.com/item?id=26732757
> Here's a take: Go is a language for engineering managers who occasionally contribute a ten liner. You can learn just enough in between your many meetings to make a simple change. You can feel proud and happy with how easy it was for you to contribute. And you can dump the verbose, repetitive drudgery onto the people below you in the org chart, it's their job to deal with this after all. But in a language that you can easily understand, you'll make sure to push for that.
Edit: I retract this. Looks like the metrics are simply inverses (even though they shouldn’t be).
This could trick someone who doesn't know that there are 25 languages in that survey and that the most dreaded chart is just an inverse of the most loved chart. You could also say that Typescript is the #24 most dreaded language (32.9% of respondents)... it's also the second most loved language. Rust is the #25 most dreaded language (because there are 25 languages on that list).
https://insights.stackoverflow.com/survey/2020#technology-mo...
> % of developers who are developing with the language or technology and have expressed interest in continuing to develop with it
you then get a very strong selection effect which is the main reason why Rust has been at the top of that survey for a number of years now.
You gave away the game when you pointed out it was the "#21 most dreaded". What is anyone supposed to do with that very specific number?
I think you'd be on rhetorically surer footing just arguing that these surveys don't mean anything.
The fact of the matter is that while Go certainly has a squadron of haters on message boards (it made several design decisions that were almost precision-targeted to alienate PL connoisseurs), huge numbers of working programmers really like Go. It's silly to argue that isn't true.
Are they not literally inverses of each other? It's two ways of stating the same number, not something you can take as two data points.
Is the implication here that it's a common experience among startups to pick Go and regret the decision? Can you give any evidence to support this (e.g., a survey of startups that chose Go and whether or not they regret their decision)?
Or perhaps the implication is that Go is uniquely difficult to migrate away from? I have a hard time believing that--it's expensive to change languages across the board except for a handful of special cases that were designed for interop from the start (e.g., Java and Kotlin, C and C++, etc)--I don't see anything that makes Go uniquely difficult to migrate away from.
What makes the argument superficial is that the list of things PL-connoisseur languages have that mainstream languages lack is long, and can be used to exclude almost all programs; the argument proves far too much, and isn't really saying anything. The fact is, it's not managers making people pick Go and Python; the assertion is false.
There are real arguments you can bear against Go. This just isn't one of them.
Eh, I think OCaml is actually the least shiny language in its reference class: Haskell, Scala and Rust are all shinier.
I think that taste for languages varies a lot, and certainly plenty of excellent programmers find Go is to their taste and statically typed functional languages are not. But I think it's also true that there are lot of people who like Go with Java/Python/JavaScript as their only points of comparison, and who would actually prefer OCaml or F# if they tried them.
Oh no you wouldn't. OCaml has at least one pretty serious problem in common with Go, only it's worse because even the basic math operators are functions. Therefore, you x + y to add integers, but x +. y to add floats, and there's no way around that because lol no generics.
https://news.ycombinator.com/item?id=27051629
> Bingo, Go is geared towards junior programmers. Somebody who exits the university has to be able to feel productive within two weeks of using it. That's literally the cornerstone of the entire design and all things follow from it (C-like syntax, attempt to minimize the number of language constructs, and the heavy focus on imperative programming which is what they teach in college).
Your two critiques opine how easy it is to pick up Go. Clear code, like documentation, is drugery. It's not exciting. It's boring and that's fine. It's what I personally want. And looking at GitHub and Gitlab repos it seems a lot of people want it too.
Simplicity is a good thing. Oversimplification in fundamental spaces like the framework for expressing your ideas is a tempting, seductive short-term win that will squeeze the life out of you when it's too late.
Here are some ways in which Go code is harder to read and reason about it because of the language's low capacity for abstraction:
* The need to inspect loop bodies to understand which simple algorithm is being used. Is it a sum, or a find?, or something more complex? Well, let's look for all the ifs and breaks and accumulators, and reason about it, before we can be sure.
* The difficulty in verifying guarantees about your program, for example ensuring that the caller doesn't append to a slice passed to a function if we want to append to that slice ourselves.
* The tendency to have logic spreading multiple screens vertically, because of verbose error handling, and data transformation. You need to keep a part of a function in your short-term memory in order to understand the whole (or zoom out and get a magnifying glass).
* The difficulty of managing and verifying the correctness of concurrently executed code. A wait group needs to be incremented with the number of the joined goroutines manually. There's no abstraction over common operations, like merging channels, and their manual implementation is error prone and complex.
* The need to roll your own implementation of menial tasks, like cloning a map, or getting a list of map's keys. This introduces distractions into the code, almost meaningless sections of it that nevertheless need to be understood and classified as unimportant by the reader, and take up precious visual field.
* No support for immutability makes it less obvious to the reader which parts of the gadget they're looking at can move.
* Almost every more sophisticated data structure needs to have a custom implementation, that needs to be audited for bugs.
I think this belief in the readability of Go code might come from the misconception that if a tool that you use to build something is simple, the resulting thing will be simple too. I can forgive that mistake, but the misconception has to be cleared up. We can't have unsubstantiated arguments about simplicity till the ends of time.
I feel like programming is like electrical engineering. A lot of people graduate expecting to design transmission systems and power stations. But most of the work in the field is simple stuff like wiring up a new light switch at grandma’s house. Go feels like a language designed for that group of engineers - boring as a feature, reliable, simple to reason about, anti-clever (so whoever comes in after you can understand your work). It’s a predictable language for code-as-content. It’s great that people who do that kind of work have better tools. But I also accept that that’s not the type of software engineer I am. I want my code to be clever. I like to push at the limits of expressibility in a language. For the types of work I do (lately implementing various CRDTs), small, tight, fast code is better than simple, verbose code that any tom, dick or Harry could comfortably maintain.
Does this distinction have a name?
> Does this distinction have a name?
Yeah. Write-only code.
I find most Java code bases awful to maintain because there’s always so much code. Simple changes take a long time to make because there’s so much code to understand and modify. Give me the tight rust any day.
One of my favourite tools for teams these days is Hasura, which is written in Haskell. I could not imagine a product like this being written in Go.
yes.
1. Culture of small api surfaces. The number one cause of complexity is having large apis with many arguments and knobs and whistles. Every time you interact with a large api you pay a decision/comprehension tax. Go culls this dramatically.
2. Small api in the language itself. When reading and writing the language itself you do not have to scratch your head at new creative constructs. This is the most commonly cited reason, and it’s a real reason.
3. No inheritance. The lack of immense hierarchies of data structures means it’s dead simple to find out where the implementation for a given function lives.
4. Gofmt
5. Simple error handling. Easy to wrap with all the contextual info you would need to fix a bug.
6. preference for short names. This visually lightens the page when you’re reading code, enhancing clarity.
7. Packages. You always know when code is being imported from somewhere else and precisely where it’s imported from, as you must reference the package every time you reference its code.
8. No constructors.
So no, it’s not a misconception, go actually is very readable and more so than most languages.
Agree, this is something many other langauges/systems can learn from.
> 2. Small api in the language itself. When reading and writing the language itself you do not have to scratch your head at new creative constructs.
Mixed feeling here. Sacrificing expressiveness isn't always worth it. The right tradeoffs are difficult to find, but IMO go is too far in the direction of offering too few useful constructs.
> 3. No inheritance. The lack of immense hierarchies of data structures means it’s dead simple to find out where the implementation for a given function lives.
I actually disagree here. Its no easier than any other language (and often harder), because your function or struct will accept some interface I, and call a function or functions of that interface. Finding the correct implementation of the interface is a matter of figuring out where/how the dependency was injected. Even with good codesearch and cross-reference tools, this can be difficult if there are more than 1-2 implementations of an interface.
Lack of multiple inheritance is absolutely a good thing, and composition in general does have value though.
> 4. Gofmt
Yes.
> 5. Simple error handling. Easy to wrap with all the contextual info you would need to fix a bug.
Mixed, I think explicit error handling (as opposed to raise/catch) is fine. I think better support for concise simple cases (the try macro, better Result types like in rust, or something like Google's ASSIGN_OR_RETURN macro) would all be strictly better for the language.
> 6. preference for short names.
I abhor this. Perhaps it depends on the exact culture, but at Google for example, the result of this is names that are only abbreviations, which requires mental translation to what the thing should be, making reading new code harder. The canconical example of this would be `fpb` vs `foo_pb`. This is maybe okay if you're dealing with a single proto, but if you have a number of related ones, you end up with abbreviation soup trying to remember which of rpcpb, rcpb, crpb, and bcpb you want.
> 7. Packages
This is, I think, a thing that only matters if you're coming from C/C++. So like sure?
> 8. No constructors.
I guess, I'm not sure how this aids readability, I guess you mean that it forces you to have named factories for any complex instantiation, instead of default or magic constructors?
> I think this belief in the readability of Go code ...
I guess you'd be the one to know since you're the one who originally made the claim.
To be fair, Some of the items in your bulleted list are downsides to the language. It can't be all things to all people.
Why shouldn't all languages be easy to understand? Why would anyone want a hard to understand language unless it provides benefits significant enough to offset that? Newsflash, most don't. Go has your cross platform, it has your simple code, it has your single executable and it has your performance. That suits the needs of most developers should they be interested in Go at all.
The fact that someone with little knowledge can contribute easily to any Go project is a huge benefit to the language. It's simple enough and the code most people write is idiomatic enough that you can drop into any project and know where to start. That is something I can't say of any Java or C# project I've looked at before.
- the language with the best and most complete standard library
- the language that is the easiest to read due to: the lack of expressiveness/generics/OOP/etc., the forced convention (by the compiler, go build, go mod, etc.), the fact that gofmt can't be customized, etc.
- due to this it is the easiest language to maintain or contribute to, because you won't spend hours trying to decipher what it's doing. You can't be too clever when you write Golang. In many languages the complexity is on the reader, but in Golang it is much more balanced. People complain about Golang being harder to write, but how boy they should really start appreciating how easy it is to read.
- and in turn one of the most secure language you can use because it's extremely simple to audit and review code in Golang. As an ex security-consultant I've reviewed many codebases in many different languages and Golang codebases were in general the most secure one (albeit nil dereference bugs).
How can this be true when language is still adopting[1] table stakes collection operators?
> he language that is the easiest to read
Generic programming is now a part of Go[2]
- hex / base64 / json / xml / etc.
- TLS (1.3) / x509 / aes-gcm / sha-2/sha-3 / X25519 / ECDSA / ed25519 / crypto.rand
- http / html templates / json-rpc / etc.
- big integer math
- zip
There's just so much more, and the documentation is top notch: https://golang.org/pkg/
Actually, this is just my take on what I end up using, but what's your own favorite package in Golang std lib :D?
https://docs.oracle.com/javase/8/docs/jre/api/net/httpserver...
Literally everyone. Go is not exactly a stand-out XML handler either.
> TLS (1.3) / x509 / aes-gcm / sha-2/sha-3 / X25519 / ECDSA / ed25519 / crypto.rand
The prevailing idea even within the Go community today is that probably too much of that is exposed and instead the stdlib should be focusing on higher-level primitives.
> http / html templates / json-rpc
Go's HTTP stack is very good. Most languages do have a worse one, though.
The templating language is interesting, I'm glad it's there, but I think there will be widespread dissatisfaction among developers coming from other languages within in the next few years and you'll see it languish in favor of a 3rd party library. I would not choose a language based on its stdlib templates (and if I did I'd choose Perl).
I bet they will come to regret jsonrpc in stdlib.
> big integer math, zip
Table stakes.
For personal projects I'd say write the code however you want. But for professional work, I think the emphasis should be firmly on code readability. I'd almost always forego some beautiful abstraction that's hard to grok without the necessary background in favor of dumb abstractions that are easier for most people to understand.
I think much of the reason for disagreement is that this isn't universal. I find boileplate heavy code much harder to reason about than abstracted code. Especially if the abstractions are either standard ones (map, filter, etc), or well-documented.
I guess there's a happy medium. Personally I find Java too abstracted. Go not abstracted enough, and that lanaguages like Rust and JavaScript strike a good balance.
Once systems grow, 'easy' quickly turns bad because of lack of abstraction capability.
EDIT: A good video on this is 'Simple Made Easy' by Rich Hickey.