Golang
Patterns
Good Resources
- Slice Tricks
- Slice Tricks Cheat Sheet
- The ecosystem of the Go programming language
- CodeReviewComments
- Darker Corners of Go
- Practical Go Lessons
- Learn Go with Tests
- Going infinite handling 1M websockets connections in Go
- Going infinite handling 1M websockets connections in Go - GitHub
- Package Management in Go
- Go Programming Language
- Google Go Style
- Clean Go Code
Testing code that uses goroutines
I’m using github.com/stretchr/testify/mock
:
type SomeService struct {
}
func (ss *SomeService) SomeMethod(parameter string) {
go doSomethingElse()
}
func doSomethingElse() {
// some thing we want to do
}
type SomeServiceMock struct {
mock.Mock
wg sync.WaitGroup
}
func (ss *SomeServiceMock) SomeMethod(parameter string) {
defer ss.wg.Done()
ss.Called(parameter)
}
func TestSomething(t *testing.T) {
someService := new(SomeServiceMock)
someService.wg.Add(1)
someService.On("SomeMethod", "someParameter").Return(nil).Times(1)
// execute code that we want to test using the someService mock
someService.wg.Wait()
mock.AssertExpectationsForObjects(t, someService)
}
Convert float to string
s := fmt.Sprintf("%f", 123.456) // s == "123.456000"
Books
Cool Projects
- Typer
- PTerm
- bokchoy - simple Go library for queueing tasks
- coffee - tool for studying parallelism, contention, utilization, latency, and throughput
- conc - better structured concurrency for go
Frameworks
ORM
Tools
- Autostrada - Application scaffold generator
- Statsviz - Real time Go runtime metrics
- Eventhus - CQRS/ES toolkit for Go
- gocov - Coverage reporting tool for The Go Programming Language
Swagger on Go projects
Interesting Posts
- Concurrent API Patterns in Go @ DeliveryHero Tech
- Introducing Clean Architecture by refactoring a Go project
- Introducing basic CQRS by refactoring a Go project
- Too modern Go application? Building a serverless application with Google Cloud Run and Firebase
- The Repository pattern: a painless way to simplify your Go service logic
- You should not build your own authentication. Let Firebase do it for you
- A complete Terraform setup of a serverless application on Google Cloud Run and Firebase
- Repository secure by design: how to sleep better without fear of security vulnerabilities
- Microservices test architecture. Can you sleep well without end-to-end tests?
- Combining DDD, CQRS, and Clean Architecture by refactoring a Go project
- Common Anti-Patterns in Go Web Applications
- 4 practical principles of high-quality database integration tests in Go
- Introduction to DDD Lite: When microservices in Go are not enough
- Robust gRPC communication on Google Cloud Run (but not only!)
- Modern Business Software in Go - Series of posts
- Safer Enums in Go
- The Go libraries that never failed us: 22 libraries you need to know
- JSON vs FlatBuffers vs Protocol Buffers
The Zen of Go
See the original source here
Each package fulfils a single purpose
A well designed Go package provides a single idea, a set of related behaviours. A good Go package starts by choosing a good name. Think of your package’s name as an elevator pitch to describe what it provides, using just one word.
Handle errors explicitly
Robust programs are composed from pieces that handle the failure cases before they pat themselves on the back. The verbosity of if err != nil { return err } is outweighed by the value of deliberately handling each failure condition at the point at which they occur. Panic and recover are not exceptions, they aren’t intended to be used that way.
Return early rather than nesting deeply
Every time you indent you add another precondition to the programmer’s stack consuming one of the 7 ±2 slots in their short term memory. Avoid control flow that requires deep indentation. Rather than nesting deeply, keep the success path to the left using guard clauses.
Leave concurrency to the caller
Let the caller choose if they want to run your library or function asynchronously, don’t force it on them. If your library uses concurrency it should do so transparently.
Before you launch a goroutine, know when it will stop
Goroutines own resources; locks, variables, memory, etc. The sure fire way to free those resources is to stop the owning goroutine.
Avoid package level state
Seek to be explicit, reduce coupling, and spooky action at a distance by providing the dependencies a type needs as fields on that type rather than using package variables.
Simplicity matters
Simplicity is not a synonym for unsophisticated. Simple doesn’t mean crude, it means readable and maintainable. When it is possible to choose, defer to the simpler solution.
Write tests to lock in the behaviour of your package’s API
Test first or test later, if you shoot for 100% test coverage or are happy with less, regardless your package’s API is your contract with its users. Tests are the guarantees that those contracts are written in. Make sure you test for the behaviour that users can observe and rely on.
If you think it’s slow, first prove it with a benchmark
So many crimes against maintainability are committed in the name of performance. Optimisation tears down abstractions, exposes internals, and couples tightly. If you’re choosing to shoulder that cost, ensure it is done for good reason.
Moderation is a virtue
Use goroutines, channels, locks, interfaces, embedding, in moderation.
Maintainability counts
Clarity, readability, simplicity, are all aspects of maintainability. Can the thing you worked hard to build be maintained after you’re gone? What can you do today to make it easier for those that come after you?