I very much disagree, though, about what you said about mutexes and channels. Channels are by far the most idiomatic way to transfer data in a concurrent model. Timeouts, worker patterns, and program architecture are much much more idiomatic and less error-prone when using channels.
Mutexes can be used properly, but it's rare that there's a situation where using a mutex would be better than using a channel. When I was first learning Go in 2017, I had to emerge from the shell of using mutexes that I had entered after working in other languages. After switching to predominantly channel-based communication, I found that my architecture became much less error-prone, and that things just worked. Prior to that, I would spend tons of time trying to figure out the exact situation that had put me in a deadlock, where several goroutines were waiting for a mutex to be unlocked that never was, or where a goroutine was trying to unlock an already unlocked mutex.
Can you explain a bit more about this?
Any articles I can dig into explaining the difference?
https://sendgrid.com/blog/when-writing-unit-tests-dont-use-m...
You no longer need to set your GOPATH. Just create your project directory and `go mod init`. I prefer the Goland IDE or VS Code with the Go extension; however, lots of people get all the tooling set up in vim easily enough.
Basically, getting started 101: Create a project dir, run go mod init, write something in main.go at the root, and then `go run main.go`.
You'll want to read Effective Go (https://go.dev/doc/effective_go).
For everything else, it is much more simple than you are used to in Python. Use the latest version of Go. Use Go Modules takes care of your module versioning. A tox-like tool doesn't make sense in Go, just use `go test`. If/when things get more complicated, most people use a simple shell script or makefile. No need for anything like flake8, use `go fmt` which auto-runs every time you save, along with `go imports` if you set your tooling up and also makes it so you don't have to add/remove imports when hacking around. Pro-tip, to avoid unused variables when hacking, just assign them: `_ = myVar`.
The directory structure starts off super simple: a main.go and main_test.go in your root - you can get more clever as your project needs it. There are a few other conventions. You can check out https://www.wolfe.id.au/2020/03/10/how-do-i-structure-my-go-... for general reference. Apparently people are now using a `pkg` directory; I never do that (all the nested dirs there would just be top level in my projects). `cmd` is used for any number of executables you want to create after you outgrow a single main.go in your root.
When you start with integration tests, those make sense in their own directory, otherwise, all unit tests match their counterpart go file (*.go -> *_test.go).
With a single main.go, you just run `go run main.go` or you can run `go run .` if multiple *.go files are needed.
Some other general recommendations: avoid ORMs, avoid global state, avoid web frameworks (just use the stdlib or a request router like Chi), avoid mocks (have 'fakes' that match interface functions), don't go over the top on channels (often a mutex is best), and if you find yourself fighting the language, you are probably doing something non-idiomatic.
Best of luck!