My team has ~200k lines of Go code and I'm exceedingly happy with the state of the codebase. Having previously maintained a C++ codebase of similar size, I can say that the pace of changes is higher, the effort necessary for large scale refactorings is lower, and our ability to reason about the system is similar.
A few examples:
- Refactorings are simpler due to the use of consumer-side interfaces. Say you want to inject an in-memory cache above a backing store. To do that you probably have to change the constructor and provide an implementation matching the 2-4 relevant methods. That's it.
- Tracing code is slightly worse, due to having to track callers through said duck-typed interfaces, but on the flip side multi-threaded code is sufficiently simpler to reason about that I call it a wash. Having previously had to do threading in the form of "control flow" state machines, and then fibers (which were better but not perfect, and still aren't widely available), Go constructs are great. Locks where appropriate, channels where appropriate, overall very fast and clean code.
- Performance is good, and reliable. Not as good by cycle-count as C++ - and e.g. the comparable RPC libraries are definitely less mature than Google's very-well-kicked C++ libraries - but on the other hand it scales almost linearly. We started a system at ~5 cores/task under AutoPilot, and then when we next got around to adding more tasks it was peaking at ~60 cores/task at essentially the same per-core throughput. I've never managed to write a C++ server that can accidentally scale concurrency by >10x without hitting _some_ bottleneck.
- We use Go for ~everything. Server code, definitely Go. Client tools, also Go. Simple scripts, bash until they need their first 'if' or flag or loop, then Go too.
- I'd prefer real generics to interface{}, but the number of places it comes up is minimal enough that it's no more than a minor annoyance.
I can't speak to the issues of package management - we dropped compatibility with Go's native package structure fairly early on and went all in with blaze/Bazel (http://bazel.io) to coordinate builds and dependencies and whatnot, and haven't had reason to try modules yet.
If you don't mind, can you give a little insight on what this looks like in practice? I'm not sure how to use a compiled language as a script. I've played with executing go as a script using a shebang hack, but I somehow don't think this is how others are doing it.
For reference, the shebang hack I was using looked like this:
//usr/bin/env go run $0 $@; exit $?
package main
import "fmt"
func main() {
fmt.Println("i am a script")
}For something of this form, if the standard library has the functionality, we us it - os.Mkdir() instead of `mkdir` and so on. But to simplify shelling out, we have a little library that includes the interface
type Runner interface {
Execute(dir, name string, args ...string) (*CmdOutput, error)
}
so it's easy enough to call miscellaneous programs and get the exit code / stdout / stderr. It also supports printing and executing a command, etc.Iteratively executing a program of this form looks like `go run whatever --flag=value`, though your shebang hack looks like it'd also do nicely.
Naturally those of us coding since the early days have experience with programming languages without generics support, yet a large majority eventually adopted generics.
Even C has minimal generics support since C11.
Personally, in any typed language, I want generics, not having them feels very limiting.
I do think that generics are needed if go wants to become more useful in contexts others than network middleware or data plumbing, but i'm also pretty sure that adding them will help cripple a lot of codebase in a very short term.
-- Rob Pike
From https://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Fr...
And there are other hints on https://talks.golang.org/2012/splash.article
I'm a relative expert in some domains, but I just find the fancy languages tiresome. I even find so called experts who find it condescending to use a straightforward language tiresome. Seriously - these so called experts never actually deliver any actual product. They just loop around writing line noise that is unreadable.
If you code with others, look into the idea of write-only languages. I think you'll find some of the ideas behind go make some more sense if you understand what they are working to avoid.
I don't hate generics, but don't think they are critical to go's success.
You may argue if Go finds the right compromise between giving enough power to their users and not allowing them to shoot themselves in their feet, but the general premise seems to be a very sound one to me.
I believe the designers of Go are onto something.
It's not just the code, but how the code will evolve over time, after a few years of having dozens or perhaps hundreds of programmers modifying it.[1]
In a large shared code base the following dynamic plays out.
* A programmer is assigned to fix a bug, or add a feature.
* His reward for that task is limited to whether he succeeded in achieving that task.
* Even if he is rewarded for improving the codebase overall (refactoring), this introduces much more risk than simply making his changes and getting out.
Basically, everyone wants to get in, make their change, and get out. Now iterate this a few thousand times.
Really it's just the tragedy of the commons, and codebases rot because of it.
The problem is magnified if the language encourages lots of complicated abstractions and meta-programming, because abstractions are difficult to evolve incrementally.[2]
Honestly, I just see Go as a reaction against C++ in this regard.
I still believe however, that the Go team went too far in leaving out features, especially in the areas of generics and error handling. I mean both create more code, so what if it's simple code?
Then the problem becomes that you just have more lines of code to maintain and test.
[1] And for twenty years I was a freelance consultant brought in to untangle big pile of mud codebases for projects in crisis.
[2] See the concepts of assimilation vs. accommodation in psychology. Once your abstractions need to accommodate an inconvenient new fact, then it takes a lot of disruptive effort to fix the problem.
Enjoy the video from 00:20:40 up to 00:21:10.
I wasn't suggesting that you'd deliberately make up the quote, but you can find it all over the place without a proper citation, so I thought it was possibly apocryphal.
Just remember, they probably think the same thing.
That said, any major additive change to Go, especially generics and/or try/catch will push me away from the language. If I need a well designed language, I have Rust. Go's sell for me is it's so naively simplistic it's actually useful when your team members are idiots.
If they bolt on type variables, well that's just a different language, and we'll just end up on the hedonistic treadmill towards another Java. No thank you.