Eleven Years of Go
blog.golang.org
blog.golang.org
* https://github.com/pion/dtls
* https://github.com/pion/webrtc
* https://github.com/pion/turn
Go is really great as a 'teaching language'. I have seen lots of software pop up that was inspired by reading the Pion code. I think that is great, it is much harder to read and learn from other languages. Also really great for contributions. I get so many people making their first Open Source commit. The languages simplicitly really makes it easy.
The most frustrating thing about Go isn't even the language, it is the community for me. It is very business/corporate focused. Everything is all about Kubernetes/cloud. I apply for conferences/meetups non-stop, but never have any luck. It is always the same company reps. I am envious of the Rust community here, but maybe grass is always greener on the other side?
As someone who's been writing Go full-time for over eight years, and has also done some Rust as well, I'll say that the business/corporate focus that you're noticing is a fairly recent development. My impression is that it's mostly a function of the size and the market demand for the language - ie, as the language has grown and is being used for more mission-critical stuff, a wider range of businesses have a vested interest in it.
Rust is a bit younger than Go. If we're judging by the first stable release, it's about half the age (5 years vs. 8). It's not as far along the adoption curve in many ways yet, although it's growing fast. Rust is being used for more things today than it was, say, three years ago, but it's still a narrower range (and number) of companies that are interested in Rust. Given Rust's trajectory, I think that's just a matter of time, so what you see today in Go will probably be more visible in Rust in a year or two. It's already drawing a wider range of corporate interest than it was even one year ago, and if it continues to be successful, that's inevitable.
I don't think that's a bad thing, though. The communities get wider, but that doesn't mean that the stuff you're looking for isn't there; you just have to look a little harder for it. That's definitely true for Go, and I would bet it will be true for Rust as well.
I also think that Rust and Go are not really comparable, and it's a shame that they often come up together so often in discussions, because they're compared more often than is really warranted IMO. But that's a separate matter.
For the record, it _can_ be done.[0]
What does Go do to fundamentally deviate from Java? IMO it is basically the same space with a few different opinions. It has GC, is C-style, a more modern stdlib than C/C++.
Eventually they might come up with a solution to get their huge WASM files down to bearable size. Sure its because they have to ship the bigger part of their VM with it.
A "Hello World!" using 1.15.4 on linux/amd64 takes up 1.2MiB of space (731KiB after UPX).
Using https://github.com/tinygo-org/tinygo, it drops to 21KiB (unable to be compressed further using UPX), making WASM size a non-issue.
And, like a breath of fresh air, I started remembering how Go works: it's a very simple, stupid language -- in the best possible way -- and it just works. You start writing code, and then you write more code. You write a function. And then you write another one. Slowly, your program gets bigger. Sometimes you need to split up functions.
And that's it. No fiddling with project structure, with taxonomies, and with hierarchies. I forgot what it felt like to just sit down and write some code. I've gotten used to battling tooling, fighting with opinionated frameworks, and having a zillion ways of doing something -- stuck in an eternal analysis paralysis.
I missed Go.
Someone else mentioned go just works, they don't have to fight the tooling, or that they worked on a go program that works five years later (which language does this not apply to). ??? I've had quite a few problems with things like plugins for go deleting unused imports because I am in the habit of saving frequently. I resolved it, but I still had to fight with the tooling. Then there was the whole go mod situation.
Go code is a language that has succeeded in a niche market (It's magical to just spin up a web server that works out of the box with great performance. ) while also handicapping itself so that complex code is too difficult to write. So it doesn't get associated with the problems some other languages have. Java is a good language that people ruined with horrible enterprise culture.
I don't even really think that go is that painless to learn. The small size of average projects has led to some dire fault lines. The biggest one for me is that it's structural typing for interfaces isn't explicit. Often times I'll read some code that uses an interface, and I'll have a real hard time mentally mapping what uses that interface and how to write code. 90% of the time this isn't an issue because the projects are small enough that I can just keep the whole thing in my head, and I can make educated guesses, or I only need to remember a handful of common cases. This doesn't scale well. Yea, I know there's plugins for these things - and I use them, but I am not a fan of tying development to non-standalone tools.
Anyways, rant aside, looking forward to another 11 years of go.
This is not the language or tooling's fault, but likely your IDE's doing. You can set it up not to run gofmt/goimports on save.
Doesn’t apply to most that I’ve used. Java, JavaScript, Ruby, python, PHP.
I don’t even remember what I was using for Java 1.2. Maybe ant and struts? I doubt there’s a good path to update such things without a giant rewrite or refactor.
Concise code with heavy abstraction can help eliminate errors, but it definitely doesn't make reading it easier. It also doesn't make solving problems any easier. Solving problems is all about algorithms and data structures, not the language you are using.
You might then notice that there is some kind of sweet spot in complexity: fewer features and footguns than, say, C++, but more features and abstractions than machine code.
If you want an example of something moderately complex but well defined, go implement a min-heap or some other basic algorithm.
I feel like Go isn't so much aiming for simlicity but actually for easiness in that line of thought, and thus its only a matter of time until complexity is encoded within the Go code.
Now, aiming for simplicity is not easy, at times definitely hard but it can lead to actual simplicity.
[] obligatory note that while I like this talk I don't agree with all of his utterances.
Ex doing string processing in python vs golang. In golang or java even it's far more lines of code and far harder to understand.
The template declaration and invocation hierarchy seems unnecessarily complex and is almost 100% undocumented. It just hurts my brain and doesn't make any sense to me in comparison to jinja, whatever jekyll uses, etc.
It has very very poor documentation. Some docstrings are not a replacement for actual documentation when your approach is that unorthodox.
And it doesn't produce any error messages or warnings when you do something wrong - just renders a completely blank template.
EDIT: to be clear, my beef is with the template hierarchy, poor docs, and lack of errors/warnings, not with the general capabilities of the library, which are quite complete.
I guess I am just used to Python where there are 1-2 all-star 3rd party packages for each use case that the community tends to coalesce around. Rust appears to be heading in a similar direction.
Since Go has so much web/http stuff baked in, it is more likely that must people just stick with the stdlib approach, even if it's a bit of a PITA.
1. The {{}} pairs confuse editor syntax coloring (in emacs, anyway).
2. Expansions that introduce or drop line breaks will cause the runtime/dlv to report line numbers that disagree with the pre-gen source you need to correct.
The future is bright for Go, it is for sure the language that defines Cloud development for backend services and APIs. It has spawned an ecosystem of docker, kubernetes and beyond and countless companies have come to rely on it for their software. It is amazing achievement to see something go from nothing to this. It is unclear whether it would have had the same success had it not been for being within Google, but that's just part of it.
I don't want this to come across as negative but I really don't get this. Can you expand upon this if you don't mind?
What is it about Go specifically that you make claims like that? Realistically couldn't you have done everything you did in Go, in say Java or something else? (I make this as a general comment, I am not aware of the things you have actually written in Go :) )
It is SO nice to not have stuff just break out from underneath you.
(Modules wrecked this a little bit; I think it was a mistake to go 1.0 before they worked out a packaging/distribution solution. But what they finally came up w/ looks really nice.)
(†my std "kick the tires problem" -- boggle solver + high-score board finder)
For Go I guess I can comprehend the statements of praise by Go-users, but I have a hard time with really understanding it. I fail to see the appeal.
This comment is not meant as an offense to Go-lovers out there, to the contrary, pick the languages and stacks you like. Its more about myself; me wondering at my own lack of understanding.
But if you come from doing shell scripting or C, then Go will be much nicer. And then it also has some actual nice things and removes some obstacles that exist in other languages.
I think with more experience, people will start dislike Go more and more and switch to other languages. But for beginners it is easier to learn and you are less distracted by nuances like formatting or a difficult typesystem.
My pet theory to the popularity is that it feels satisfying to just churn out many lines of code and in Go it seems you do that instead of using abstractions or "magic" as in many other languages. So Yeah, I expect popularity to drop as more "legacy" code bases need to be picked up by devs ...
> But if you come from doing shell scripting in C
Sure I'd switch to Go in no time!
Sadly I was proven wrong about the second part, and it has followed the path of doubling down on Java 1.0, including the grow warts that Java has gotten in 25 years. Here Go has learned nothing from its predecessors.
With the success of the container ecosystem based on Go written tooling, means that at some point using it in some fashion becomes unavoidable, even if your main tools happen to be something else.
https://github.com/golang/go/issues/32437
https://github.com/golang/go/issues/32437#issuecomment-51203...
https://golang.org/doc/faq#assertions
> Why does Go not have assertions?
> Go doesn't provide assertions. They are undeniably convenient, but our experience has been that programmers use them as a crutch to avoid thinking about proper error handling and reporting. Proper error handling means that servers continue to operate instead of crashing after a non-fatal error. Proper error reporting means that errors are direct and to the point, saving the programmer from interpreting a large crash trace. Precise errors are particularly important when the programmer seeing the errors is not familiar with the code.
> We understand that this is a point of contention. There are many things in the Go language and libraries that differ from modern practices, simply because we feel it's sometimes worth trying a different approach.
But error handling is just awkward. I do not mean the retval, err := funccall(a,b) syntax but rather the absence of a standardized way to get stack traces.
Fundamentally, the Go team made the correct choice between explicit and implicit error handling. Yet one would hope for a bit terser syntax and language supported stack traces, 11 years after launch.
For a good overview of the issue: https://go.googlesource.com/proposal/+/master/design/go2draf....
The Go team tried, but the community shot it down.
Your link was the first proposal, and this was the second proposal: https://github.com/golang/proposal/blob/master/design/32437-...
Simple cases are already simple. It's the harder spots that need help.
func whatev() (err error) {
defer helper(&err, "mightFail context to help debugging", some, vars)
try(mightFail())
defer helper(&err, "alsoMightFail context to help debugging", some, vars)
try(alsoMightFail())
}
if the second call fails, your error has now been wrapped twice.There are of course ways to deal with this. You can add a "already wrapped" sentinel of some kind (say, a * bool that they check and modify), or use a `helper := once(helperfn)` to do that same kind of thing internally... but that's more verbose. And can't be done generically without reflection (though `func(*error, string, ...interface{})` would be quite common, which might be good enough). Inline handlers shown in many examples are even worse since they can't be composed as easily, so you'd probably end up maintaining a bool var between all handlers. By hand.
They both also require naming your `error` return value, which requires naming all returned values, which has been a depressingly large source of shadowing and not-initialized-value mistakes in my experience. Improperly-initialized values especially, as those tend to cause problems a fair distance from the cause, and sometimes go unnoticed for quite a while.
tl;dr it adds more easily-forgotten boilerplate to non-trivial error handling, and makes it and related code more mistake-prone. I'd rather have `return fmt.Errorf(err, ...)`, it fairly naturally resists growing more complex as your func grows more complex.
I wrote a ton of various code in Go [1] over the last 10 years and had never experienced the need in generics. The last my project in Go is VictoriaMetrics [2] - fast and cost-effective open source time series database and monitoring solution.
Before Go I was writing some code in C++ and was constantly struggling with C++ templates in stl and boost libraries. This was absolute nightmare from debugging PoV.
Well you don't have to use them and you won't have to use any of the libraries that use them. Just like C++ templates, some C++ shops forbid their use. But that's your problem, don't make everybody else suffer from what is considered a burden and a flaw in that language.
People want compile time type safety, not having to resort to runtime reflection, which Go std lib itself does . There is nothing unfathomable about that thought process. Go has a poor type system at compile time while being way too permissive at runtime.
It would be great if you stopped patronizing people who just provided you an explanation you just don't want to hear.
It has its' shortcomings, but what i have learned due to just writing stuff instead of using enterprise frameworks was eye opening and i would be a comoletely different lerson today.
Thank you dear Go team & community!
I tried Go, but I couldn't get into it. I don't know why. I guess it's like leaving prison. You can't go anywhere. Then suddenly you can go anywhere you like, but you feel paralyzed. The freedom itself is also overwhelming.
//full-blown tuple support
var person (string, string) = ("John", "Doe")
//records are just tuples with named fields, but can use existing struct access syntax.
var point #{x: int, y: int, z: int} = #{x: 1, y: 2, z: 3}
//due to structural typing, we can just use a type alias
type Person = (string, string)
type Point = #{x: int, y: int, z: int}
var person Person = ("John", "Doe")
var point Point = #{x: 1, y: 2, z: 3}
//or just infer
person := ("John", "Doe")
point := #{x: 1, y: 2, z: 3}
//destructure
fname, lname := person
//destructure record/tuple
{x, y, z} := point
//destructure array or slice (c is always slice)
[a, b, ...c] := myArray
//pattern matching on tuple
switch person {
case ("John", "Doe"): //do exact match
case ("John", _): //only match first name
case (fname, lname): //use new variables
default: //this is the same as `case _:`
}
//pattern matching on record or struct
switch point {
case {x, y, 0}: //do stuff with variable match
case {1, 1, 1}: //do stuff with exact match
default: //do stuff
}
//same style of error handling with more flexibility
switch getPointWithPossibleError(point1, point2) {
case (_, {type: "error1", msg}): //handle error 1
case (_, {type: "errorN", msg}): //handle error N
case ({x, 0, 0}, _): //handle exceptional case
case ({x, y, z}, _): //handle default case
}They then go on to say that they needed to revisit the design in the square brackets case to insert the keyword “type” whenever generics were used, which means this example would presumably become c<type d, type e> which resolves the ambiguity.
It seems more likely it was an issue discovered atbut not revisited when the issue was fixed, and now inertia is keeping it as is.
On one hand I'd like to try and learn something new,
on the other hand, everything I could do in go I would probably do better in java (as in I know how to do it already)
First, similarities: Go seems like a really good language for building software for teams. It's straightforward and simple, and generally what you see is what you get. That's great.
Where it wins:
- having a standard formatter is amazing
- having a compiler that is fast, and an executable that starts fast and runs quickly is great
- the built-in http stuff seems better than what was available in Java's stlib for years
- channels are nice
- lower resource usage, especially memory
- by breaking with the JVM, you get to abandon a lot of bad patterns because they simply aren't available
Where it loses:
- I simply don't need pointers for the places I want to use Go
- the GC, though different, still needs tuning/consideration for high-load, and if I have to mess with GCs I want the options available to me on the JVM
- the dependency management story was rough for a while, though that seems to have settled into something more mature
- the error handling is unacceptably anemic
- the type system is unacceptably anemic. I'm not even talking about generics (maybe I am) - it just seems moronic to have to subvert it with "interface{}" all the time, which also renders the documentation pretty opaque to me.
Overall Go is a solid tool, and I see why it's so successful, especially for network stuff and command-line tools. It's hard to overstate how great an experience it is to have a fast-compile loop and I really envy that (I feel this way any time I use most languages not on the JVM).
And for learning something new, there's nothing novel that I've seen in Go. If you want to use it because it is pragmatic and efficient, that's a great reason.
As pjmlp mentioned, The concurrency package is pretty nice to know, and offers functionality to chain work or fire off an async task or enqueue stuff to be done as messages, but it's not as trivial as marking a function async. Clojure and Scala have more direct paths to this sort of convenience. Loom will be interesting!
Well, sure, but the code I don't need to write in the first place because of well used and vetted libraries, or higher level abstractions, is pretty cool too. It just isn't as keyboard clackyclacky.
The performance boost argument is a joke when we have horizontal scaling and and multithreading.
Go lang also made some fundamental re-useable interfaces that are leveraged across all its packages making the stdlib consistent to learn.
I still prefer Java though - because Intellij rocks and I am personally faster with development. But for newbies, golang is easier.
https://www.dice.com/jobs?q=golang&location=New%20York%20Cit..., NY,%20USA
2) Just an opinion, but I would guess the majority of companies on Dice would trend towards older tech stacks. My employer has 4 open Go positions but is not found on Dice.
- Angel.co shows 10 Go-related jobs in NYC
- Linkedin shows 293 Go-related jobs in NYC
Seems there may be a "cultural divide" on where job listings for different types of companies/languages show up.
A third party could take it on, but that would require a different governance structure for the project. Outside entities have little influence now.
You'll get a feel for the language constructs. Once you're ready to graduate to tinkering with your own stuff, then go install Go: https://golang.org/doc/install
Some noted differences from C# (my C# knowledge is 5-10 years out of date, mind you):
* Go is a much simpler language; it lacks classes, inheritance, generics, exceptions, etc--you can be productive in a weekend, conservatively * Go has a formatter that everyone uses, so everyone's code has the same style * Go lacks generics, so everyone generally writes in the same imperative way (few personal, creative flourishes)--it's very boring in this regard (which is a feature for many people) * Go has value types (like C#) but they are much more idiomatic--it also has pointers and other reference types * Go's interfaces are implicitly satisfied, which is like statically-typed duck typing if you're familiar with any dynamic languages (no need to write `extends IFoo` or whatever, unlike C#) * Go statically compiles everything by default (the "VM" is compiled into every individual program) so you can just pass around a single compiled artifact * Go has lightweight threads ("goroutines") * For web service things, you don't need a Netty or any other web-server in front of your service; the web server is just a library (part of the standard library, in fact) and it is production-grade
Go and C# occupy the same ballpark in terms of performance.
You raised an interesting point in your question though: how do you reap benefits? I did not find a good answer to this. I've yet to see any convincing real-world reason to switch from .NET Core to Go. I thought perhaps it had an edge in the streaming media industry, but I didn't find that argument to hold significant weight either. If you go searching online, you might find some articles that show Go to be more performant using large numbers of threads or doing exclusively mathematical operations, so that might be where you could reap some benefits?
EDIT: I hope what I said about reaping benefits doesn't come off as any sort of fanboy-ism towards a specific language. This is just my personal experience as C# dev. Use the language that's right for you and right for the task.
I wasn't sure if there were any obvious benefits, but from looking at various Go codebases recently, they usually seem so succinct by comparison to the typical C# codebase I see - I love C#, but that has piqued my interest. Also, I don't try new languages often, so might be interesting just to have a go (yes, pun intended :)
I never thought of seeing "smooth" and "go modules" in the same sentence; but I hope he is right.
* Great code readability. I can open any project in Go and instantly start reading and understanding the code. This is because of simple syntax, which doesn't provide ability to write implicitly executed code, and `go fmt` tool, which formats everybody's code to a single code style.
* Go discourages unnecessary abstractions and encourages writing essential boring code without various "smart" tricks. The end result is much shorter code base, which is easy to read and refactor.
* Fast compile times. For instance, my latest project - VictoriaMetrics [1] - contains almost 600K lines of Go code excluding "_test.go" files. Go builds the final `victoria-metrics` executable in less than a second during development. This means that I can quickly iterate on code modifications and testing without the need to wait for minutes and hours for build process to finish (hello, Rust and C++ :) ).
* Single statically linked output binary with relatively small size. For example, VictoriaMetrics is built into 19MB statically linked binary with Go 1.15.4. And this binary contains all the debug symbols. Binary size shrinks to 12MB when stripping debug symbols. Such a binary can run on any host without the need to manage external dependencies - just upload the binary and run it.
* Ability to build executable for any supported target platform by specifying GOOS and GOARCH environment variables. For example, I can build binary for FreeBSD on ARM from my x86 laptop running Ubuntu.
* Great and easy-to-use tooling for race detection and CPU/memory/locks profiling. There tools significantly simplify code optimization and allow to catch data races at much lower mental cost comparing to race-free Rust :)
P.S. I hope Go will never adopt generics. I didn't need generics during the last 10 years of active development in Go [2]. Before Go I was working with C++ and was experiencing constant pain with reading and debugging C++ templates in stl and boost libraries. I don't want to experience the same feelings with Go.
C++ templates are a nightmare agreed. What C++ does is just one way, a poor way, to implement generics. Please do not conflate generics with C++. If you've ever worked with C#, Java, TypeScript, or others you'll know that "generics" come in many different flavors. Some are quite nice. Generics are coming to Go ([https://www.gophercon.com/agenda/session/233094]).
Slices, chans, maps, arrays are all generic in Go - and they are great! Stuff like the sync package (https://golang.org/pkg/sync/) with interface{} all over? Not so great. Generics solves a real problem.
Coming from dotnet I'm kind of spoiled here, with both Visual Studio and Rider being excellent choices. They also bring things like a great debugging dev UX, and the possibility to change variable values and code during an active debugging session - is there anything like that for Go?
Even without having used much GoLang this last year it gave me a very clear view of what to be looking for!