3 ms·
I've built a pretty complex and high performance go-based microservice architecture (~12 services) as backend with testify and gomock for testing. Pretty happy
by WnZ39p0Dgydaz1 7y ago
I've built a pretty complex and high performance go-based microservice architecture (~12 services) as backend with testify and gomock for testing. Pretty happy with those choices, never had any issues. The backend doesn't use HTTP, so no opinion there.
I recently started migrating some of the services to Rust for performance reasons. I would say that Go's biggest strength is perhaps its biggest weakness as well: It "just works". Types are loose (no generics), concurrency is extremely easy. This means I can write working Go code really quickly, but as project complexity grows, code tends to become kind of a mess.
For example, channel-based concurrency can become hard to reason about if you have a complex service. A few times I ended up putting mutexes at various places just to make it work despite knowing that it's not the "right" thing to do. Mutexes then come with their own issues. Once you have a deadlock or race condition, good luck debugging it. There exist multiple packages and tools to detect race conditions and deadlocks, so this seems to be a common problem. I must've spent days or weeks worth of time looking at pprof output. You may say it's my fault as a developer to write sloppy code, and that may be true, but the Go language encourages such code with the decisions it made.
The same goes for types. I never needed generics, but the fact that typing is so loose means you can get away with a lot of sloppy code without being punished for it. This can be great for moving fast, but may come to bite you later on.