in my personal experience, a bunch of it is about tooling:
vim-go really hits the spot for me and seemingly never breaks, an experience I've never been able to replicate with any other language
the build story is extremely simplistic but very clear
formatting is just a thing you don't have to think about at all
editor plugins elsewhere: racer (Rust) is kind of a disaster, merlin (OCaml) is usable but needs per-project configuration
neither of them do the neat things my bog-standard Go tooling does
build systems elsewhere: cargo (Rust) is nice, but requires per-project setup, OCaml doesn't even have a single answer to "how do I build my project", your build may involve arbitrary shell scripts, etc
the lack of complex dependency management works out for me in practice because my code falls into roughly two categories:
* self-contained programs or libraries that themselves use only the standard library
* larger amounts of code in my work monorepo, where having n versions of deps is more often unhelpful than not
I've found some value in the limited abstraction capabilities forcing me to build more concretely, instead of allowing me to build inappropriate abstraction boundaries with ease
It's really easy to mess that up in generic programming and never truly realise - I've been forced to clean up a lot of conflation of concerns early due to the lack of real generic programming.
it's not all sunshine, obviously: I spend a tonne of time thinking about data ownership due to the lack of immutability, checked thread-safety, etc
nullable everything is a fucking scourge even though you can sometimes make clever use of it
the lack of enums / tagged unions is the absolute fucking worst and I spend too much time designing data structures because of it
I miss the shit out of Result/Either/etc style error handling, instead of having two nullable return values (no tuples, multiple return values!) that you get to check yourself
nor do you actually have any clue about what your errors look like at the type system level
enjoy your interface{Error() string} and checked downcasts
of course none of the synchronisation primitives actually compose well, other than channels (there's a single, rather inflexible `select` primitive)
so you end up reinventing a bunch of the `sync` package out of channels, or using additional goroutines to wrap them, though g'luck if you want cancellation in the latter case
the uniformity in Go code and project setups also ensures external code is easy to adopt and treat as your own, and thanks to having really solid editor tooling, exploring external code is something I do often and with pleasure