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.
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.
Java also was not a language its designers wanted to use. They would rather code Lisp.
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.
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 never said that. They said they wanted a language for programming at Google scale. But their preferences and likes are all in the language...
"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"
Source:
http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Fro...
https://talks.golang.org/2012/splash.article
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.
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.
> making more or less random changes until something works can get there quickly.
???
First clause doesn't logically match second.
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.
You may laugh, but more code is written this way than not.
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.
"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.
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.
The language is simple, performant and writing concurrent code is intuitive (although I feel Go developers tend to get carried away using channels/goroutines).
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.
But, personally, C can get really tedious.
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.
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.
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.
Code size was about the same due to stuff being implemented in Python manually being handled by Go runtime.
Plus all the ways that Python is more convenient than Go.
Go is not about abstractions, Go is about solving problems here and now.
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.
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 }'.
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
If you want something basic, gunicorn can run flask with one line.
Also if a single line is enough, maybe adding it clearly to the doc would be nice.
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?
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
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. 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.
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.
I guess the first attempt is always tricky, let's see if Go 2 can find a smooth upgrade path.
Try learning Kotlin. I think you'll find it a significant upgrade in safety and productivity compared to Go, but especially compared to C.
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.
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.
(looking at you, java)
At what point are we just creating a new language that compiles to Go, though?
* Exceptions for error handling
* Streams (map/filter/etc.)
* Generics
* Superior collections library (Set, ConcurrentHashMap, LinkedHashMap, etc.)
* Records
* Enums
* Upcoming feature: pattern matching
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.
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.
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.
Wrapping everything statement which can produce error is more tedious.
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.
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.
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.
The Perl world has solved this by renaming Perl 6 to Raku (https://raku.org using the #rakulang tag on social media).
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.
That is the whole point, we know better 25 years later.
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 :)
- 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).
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.
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.
He doesn't cancel things when he gets bored with them.
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.
But if I'm trying to do a personal project, that amounts to a waste of time with 0 value.
/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
Why do we need virtual env in 2020?
It's one of the worse "modern" language to build, even Java / NodeJS are better.
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.
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.
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.
How many years of Java experience do have?
- 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
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.
Is picking a jvm not tweaking a parameter?
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.
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.
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
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.
Or it's _almost_ the correct hammer! If they'd just fix those damn
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...
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.
The golang standard library and high-quality third-party libraries are really great
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.
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.
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.
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 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.
Also, the dependency management.
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".
Also googled and found this:
http://lambda-the-ultimate.org/node/4554
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.
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.
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.
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.
> 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.
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.
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."
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 :-)
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.
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...
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.
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.
- 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.
This is the biggest pain point of Go throughout my journey and I have been waiting so long for Go 2! PLEASE!
The tooling is also very good, though I wish more of golangci lint would move into stdlib so I could just run golint.
I've switched most of my personal projects over to Rust and so far I'm not looking back...
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.
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.
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.
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.
At this point gopls works way better than godef+gocode ever did for me.
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.
* 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-...
Go's dependency management was designed after analyzing Cargo and its problems: https://research.swtch.com/vgo-mvs
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).
* 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.
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.
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 :).
- great community
- ample libraries
- ecosystem support
- no breaking changes (yet)
- no magic, limited and simple
- inbuilt testing/profiling/dependency manager
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.
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.
junior devs
edit: it might sound like sarcasm, but i'm deadly serious.
A still maintainable code base 5 years along.
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.
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.)
> 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?
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.
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.
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.
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_.
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 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, ...
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.
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.
Downside is no generics but otherwise great.
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.
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.- 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.
Just this week i wrote an entire toolchain for developing with an embed device, that now i can distribute to anyone that need to work on that project.
Before go i used nodejs/python due to the large ecosystem of library, but go nowadays has a similar ecosystem and allow me to build static executable without any dependency. That is super useful since i work with different OS (ubuntu, fedora, windows and macos). Also even sloppy/draft code is really fast and i don't need to start optimizing and profiling from the start like in python (i often process large dataset).
Lastly the language is very simple and my colleague who ,didn't even know go, learned in a week and, most importantly, is able to navigate my large codebase without much help (that for me is the biggest advantage of go)
With every other language, you checkout the code in any directory you want (as long as there are no spaces in the path), run either a language-specific tool or the traditional configure/make combo to compile and optionally install, and you're done. With Go, not only you had to put the code in a specific place in your home directory, but also every code written in Go you'd ever want to compile had to also be placed in that specific directory in a specific way, mixed with the code of other projects, instead of allowing you to place them separately in your personal projects directory hierarchy (which you already use with every other programming language).
- It's fast - given I work with PHP for a living it's no fair comparison really, transposing a processor-intensive algorithm into Go is like lifting a veil. In one instance I think Go ended up being ~100 times faster, and that was without splitting computation across multiple cores.
- Getting async multi-core concurrency feels smooth with golang, it's not that much of a stretch to transfer concepts from my experiences with async JavaScript / nodejs applications, only that it feels more natural dealing with flow control, race conditions, and shared resources in Go - JS tools have improved in recent years but being single threaded they only really approximate the experience of concurrency in Go and that can sometimes bite hard.
- It's fairly intuitive to pick up and use. I used to be a big fan of C#'s syntax, and prior to working with PHP I learned development with C-style languages (including C/C++) so anything with a similar syntax feels natural to me. Golang feels to me like JavaScript got in bed with C# and made a baby.
tl;dr: I'd definitely welcome the opportunity for some professional work using Go, and will certainly consider implementing some of my future projects with it to gain experience.
What I like about Go:
- explicit error handling, one is forced to write correct code and think how exactly the errors will be handled. I now prefer Go's approach over exceptions.
- code is readable, I can look at the code of my coworker and very quickly see what exactly it does.
- surprisingly, I found myself not missing OOP features
- If project is compilable on localhost, it will be compilable on the server... this is easy to achieve and also deployment is straightforward.
- it forces to write "stupidly simple" code. for example, it doesn't allow function parameters with default values. The consequence is, that I sometimes need to spend a little extra time on refactoring, but then have more readable code.
seqA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
seqB = [2, 4, 6, 7, 11, 12, 14, 16, 17, 19, 20]
You could argue that seqA is simpler because it follows a regular pattern: the range of ints from 0 to 20, inclusive. Or you could argue that seqB is simpler because it contains fewer numbers.Go seems like seqB to me. It is simple in that it provides a limited set of features, but to me it ends up feeling fractured.
The only thing I dislike is the CSP concurrency paradigm the community and language try to push. Every time you ask someone how to implement something more complex using locks or atomics, they just parrot the "share memory by communicating" lemma.
i like: being able to cross-compile things into a single binary. raw speed in case of simple services
i dislike: the way generics and package management were (are handled/ignored?) and the super-verbose error handling.
go would not be my first choice but i can understand why people find it appealing
But the simplicity of the toolchain and breadth of the standard library makes it the most pragmatic major language for backends.
I think the backward compatibility has been the number one feature for me followed by the tooling and batteries included standard library.
It is nice to be able to do most things without depending on a 3rd party library.
I can usually understand everything happening in Go with a single pass without jumping around too much in the code.
Generics are the most contentious issue, and people have been disparaging Go since day 1 for not supporting them. And yet, somehow we've spent the last 10 years writing all kinds of programs in Go, and the lack of generics has rarely been a showstopper. Go does have generics of course -- it just doesn't have user-defined generics. My hot take is that this is actually an area of the programming space that deserves more exploration. It feels like a Pareto phenomenon, where adding a small number of generic types and functions covers 80% of the usecases. And indeed, it seems that generic arrays/slices/maps/channels, plus a generic 'append' function, a generic 'range' keyword, etc. really are sufficient for the vast majority of programming tasks. These "reified" generics have significant advantages: for one, you can build deep compiler optimizations and tooling around them; and for two, perhaps more importantly, every Go developer is familiar with them. Reducing mental overhead when reading someone else's code is a big part of the Go philosophy. (Having a really high-quality standard library contributes to this as well, for obvious reasons.)
People made similar arguments about operator overloading. It's true, operator overloading allows for more "expressive" code. It also allows people to write code that is much harder to read, harder to debug, and whose performance is harder to analyze. I think it could be cool to have an 'expressive {}' block, like Rust's 'unsafe {}', where overloading is allowed; but when I'm reading normal code, I don't want to have to worry about 'x + y' jumping away to some other piece of code.
(As a side note, I see many people arguing for generics based on their desire to write map/filter/reduce functions in Go. My feeling is that map/filter/reduce (and other HOFs) are unlikely to become commonplace in Go code, even if generics are added. The reason is lambda syntax: even the simplest map or filter will require three lines of code (or one long one), including multiple type definitions and a return statement. It's not enough of an improvement over a 'for' loop to be worth it. If you could write 'xs.map(x => x*x)', it'd be another story, but that would require a much deeper language change.)
Anyway, Go wasn't designed by idiots, which means you should apply Chesterton's Fence. If some aspects of the design seems wrong, or violates your sensibilities, your first instinct should be "perhaps this is optimizing for some usecase I haven't considered," or "perhaps this is targeting a particular niche that I'm not familiar with," rather than "this is clearly a mistake." The steelman argument, then, should be usually be of the form "It seems like Go is optimizing for X, but it falls short of X in significant ways," or "Go is targeting X, but doing so comprises value Y that I consider more important."
I’ve always loved playing with languages, so over the same period of time Rust was a language I’d often tinker with. It never really felt great for web stuff, but I kept going back to it to toy around with maybe a CLI here and there.
Fast forward to the pandemic and I found myself with a lot more free time at home, coupled with a newly mature async story in Rust, and everything changed for me. The error handling due to algebraic types is phenomenal (particularly with the ‘anyhow’ and ‘thiserror’ crates), rust-analyzer in my emacs is awesome, type safe SQL with sqlx is amazing, cargo is great, compiler error messages are super clear, and the list goes on.
I just started a new project with a client and the backend is written in Go, and honestly it’s a bit of a let down having to go back to it. It’s totally fine, it just doesn’t feel beautiful to me. Seeing empty interfaces with type casting makes me cringe (I mean it always has) and I’m really going to miss the Rust error handling. Thankfully a lot of this project I’m going to be able to use AWS lambda for and I’ll just use TypeScript since the front end team is using it there (no one is comfortable with Rust).
tldr; Go is a simple language that will let a team get things done and it’s easy enough for any developer to onboard with it. But personally I wouldn’t reach for it ever again because of Rust (and even TypeScript).
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.
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.
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.
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.
Any golang function can panic, so you always have to account for it anyway.
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).
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.
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.
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.
> 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.
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 not always good though, in Java world you have to choose between multiple competing tools, while other ecosystems have mostly only one, often adopted from Java version (except Javascript, Rust, they have their own zoos). But IMHO Java is still the nursery for all kinds of tools for development.
https://greenlab.di.uminho.pt/wp-content/uploads/2017/09/pap...
Also, Go does not suffer from some fundamental Java problems, though they slowly get corrected. Go has value types, unboxed arrays. Generally, no struct in Go is boxed automatically. You have first class functions in Go and of course, it is statically compiled. It is true, that Hotspot often produces very good code, but for a lot of szenarios, a statically compiled Go executable beats it, as run-time compilation is not required.
Value types are around the corner and I bet they will be done first than generics in Go.
"Well, let me tell you about this other language called Rust..."
Classic Rustacean: sees zero daylight to inject oneself, does so anyway.