This really matches my experience, in that learning the Go language is very simple, but learning to correctly use the Go language to reliably build correct programs is much more difficult. The Go compiler (and other tooling like go vet and the race detector) gives me very little assistance or confidence in my work. The Go standard library is similarly very weak, leaving me to repeatedly re-implement things that are taken for granted in other languages, like common collection operations.

One big example is Go slices, which are "easy to use" and "just do the right thing" up until they don't. If you append to a slice, it might or might not allocate a new backing array depending on the slice capacity, so whether your function is mutating an original or not can suddenly change based on changes in other parts of the code, and it's a huge pain to track down bugs like that.

Go's channels are "Easy to get started with", but very difficult to correctly compose, or do anything nontrivial with. Reading from a closed channel will "successfully" generate zero values instead of failing to read a value. You have to replace your variables with a nil channel instead.

When writing Go, I feel like programming by coincidence. With a lot of careful thought and work and testing, I can arrange for something that works, but it's fragile under refactoring and maintenance due to spooky action at a distance.

None of the stdlib collections are threadsafe, but if you mistakenly share one between threads, it works fine up until you hit sufficient concurrency to have multiple concurrent mutations.

Go is "easy" due to garbage collection, but that only handles memory, and offers you zero assistance in handling any other resources. There's no destructors, and no RAII, so you still have to carefully manually manage resource cleanup for files, database connections, locks, mutexes.

Go's type system is too weak to express a mutex owning its guarded value, so it's left up to careful code review to ensure that the guarded value is only accessed while the mutex is locked.

I feel like "the language" is simple, sure, but to successfully use it, you have to also learn many little constructs you have to repeatedly type out, and techniques to protect yourself from all the sharp edges.

For me, the Go Language was faster to learn than the Rust Language, but learning to reliably build correct programs with Rust was way easier than learning to reliably build correct programs with Go.

I like Rust because I am a mediocre programmer with poor working memory. Rust lets me build abstractions I can rely on, and reason locally in small parts while working on a program larger than I can easily fit into my head.

Go makes some operations "easy", but unfortunately also makes shooting myself in the foot much easier than doing something safe and correct.

The Go language itself is very simple because it abdicates all responsibility for correctness, leaving it entirely up to the programmer. You still need to handle all of the same responsibilities regarding ownership, resource lifetimes, locking, thread-safety, error-checking, exhaustiveness-checking, etc. but Go leaves all of that up to individuals to learn for themselves through trial-and-error, and ignores all advances in PL theory on how languages can assist with these problems.

I could understand some of the appeal of Go if it was people saying they like to use it for half-assed one-off scripts that don't grow to large sizes and don't have any long-term maintenance, but instead I keep seeing people build large production systems with it.