100 Go Mistakes
Go is a programming language that is simple to learn but difficult to master.
Organization
- Variable shadowing
- Unnecessary Nested Code
- Misusing init functions
- Overusing getters and setters
- Interface pollution
- Interface on the producer side
- Returning interfaces
- Any says nothing
- Being confused about when to use generics
- Not being aware of the possible problems with type embedding
- Not using the functional options pattern
- Project disorganization
- Creating utility packages
- Ignoring package name collisions
- Missing code documentation
- Not using linters
Data types
- Creating confusion with octal literals
- Neglecting integer overflows
- Not understanding floating points
- Not understanding slice length and capacity
- Inefficient slice initialization
- Being confused about nil and the empty slice
- Not properly checking if a slice is empty
- Not making slice copies correctly
- Unexpected side effects of using slice append
- Slices and memory leaks
- Inefficient map initialization
- Maps and memory leaks
- Comparing values incorrectly
Control structures
- Ignoring the fact that elements are copied in range loops
- Ignoring how arguments are evaluated in range loops
- Ignoring the impact of using pointer elements in range loops
- Making wrong assumptions during map iterations
- Ignoring how the break statement works
- Using a defer inside a loop
Strings
- Not understanding the concept of a rune
- Inaccurate string iteration
- Misusing trim functions
- Under-optimized string concatenation
- Useless string conversions
- Substrings and memory leaks
Functions and methods
- Not knowing which type of receiver to use
- Never using named result parameters
- Unintended side effects with named result parameters
- Returning a nil receiver
- Using a filename as a function input
- Ignoring how defer arguments and receivers are evaluated
Error management
- Panicking
- Ignoring when to wrap an error
- Checking an error type inaccurately
- Checking an error value inaccurately
- Handling an error twice
- Not handling an error
- Not handling defer errors
Concurrency
- Mixing up concurrency and parallelism
- Thinking concurrency is always faster
- Being puzzled about when to use channels or mutexes
- Not understanding race problems
- Not understanding the concurrency impacts of a workload type
- Misunderstanding Go contexts
- Propagating an inappropriate context
- Starting a goroutine without knowing when to stop it
- Not being careful with goroutines and loop variables
- Expecting deterministic behavior using select and channels
- Not using notification channels
- Not using nil channels
- Being puzzled about channel size
- Forgetting about possible side effects with string formatting
- Creating data races with append
- Using mutexes inaccurately with slices and maps
- Misusing sync.WaitGroup
- Forgetting about sync.Cond
- Not using errgroup
- Copying a sync type
Standard library
- Providing a wrong time duration
- time.After and memory leaks
- Common JSON handling mistakes
- Common SQL mistakes
- Not closing transient resources
- Forgetting the return after replying to an HTTP request
- Using the default HTTP client and server
Testing
- Not categorizing tests
- Not enabling the -race flag
- Not using test execution modes
- Not using table driven tests
- Sleeping in unit tests
- Not dealing with the time API efficiently
- Not using testing utility packages
- Writing inaccurate benchmarks
- Not exploring all the Go test features
Optimizations
- Not understanding CPU caches
- Writing concurrent code that leads to false sharing
- Not taking into account instruction-level parallelism
- Not being aware of data alignment
- Not understanding stack vs heap
- Not knowing how to reduce allocations
- Not relying on inlining
- Not using Go diagnostic tools
- Not understanding how the GC works
- Not understanding the impacts of running Go in Docker and Kubernetes
References
Notes
Writing inaccurate benchmarks
Writing concurrent code that leads to false sharing
Variable shadowing
Using the default HTTP client and server
Using mutexes inaccurately with slices and maps
Using a filename as a function input
Using a defer inside a loop
Useless string conversions
Unnecessary Nested Code
Unintended side effects with named result parameters
Unexpected side effects of using slice append
Under-optimized string concatenation
time.After and memory leaks
Thinking concurrency is always faster
Substrings and memory leaks
Starting a goroutine without knowing when to stop it
Slices and memory leaks
Sleeping in unit tests
Returning interfaces
Returning a nil receiver
Providing a wrong time duration
Propagating an inappropriate context
Project disorganization
Panicking
Overusing getters and setters
Not using the functional options pattern
Not using testing utility packages
Not using test execution modes
Not using table driven tests
Not using notification channels
Not using nil channels
Not using linters
Not using Go diagnostic tools
Not using errgroup
Not understanding the impacts of running Go in Docker and Kubernetes
Not understanding the concurrency impacts of a workload type
Not understanding the concept of a rune
Not understanding stack vs heap
Not understanding slice length and capacity
Not understanding race problems
Not understanding how the GC works
Not understanding floating points
Not understanding CPU caches
Not taking into account instruction-level parallelism
Not relying on inlining
Not properly checking if a slice is empty
Not making slice copies correctly
Not knowing which type of receiver to use
Not knowing how to reduce allocations
Not handling defer errors
Not handling an error
Not exploring all the Go test features
Not enabling the -race flag
Not dealing with the time API efficiently
Not closing transient resources
Not categorizing tests
Not being careful with goroutines and loop variables
Not being aware of the possible problems with type embedding
Not being aware of data alignment
Never using named result parameters
Neglecting integer overflows
Mixing up concurrency and parallelism
Misusing trim functions
Misusing sync.WaitGroup
Misusing init functions
Misunderstanding Go contexts
Missing code documentation
Maps and memory leaks
Making wrong assumptions during map iterations
Interface pollution
Interface on the producer side
Inefficient slice initialization
Inefficient map initialization
Inaccurate string iteration
Ignoring when to wrap an error
Ignoring the impact of using pointer elements in range loops
Ignoring the fact that elements are copied in range loops
Ignoring package name collisions
Ignoring how the break statement works
Ignoring how defer arguments and receivers are evaluated
Ignoring how arguments are evaluated in range loops
Handling an error twice
Forgetting the return after replying to an HTTP request
Forgetting about sync.Cond
Forgetting about possible side effects with string formatting
Expecting deterministic behavior using select and channels
Creating utility packages
Creating data races with append
Creating confusion with octal literals
Copying a sync type
Comparing values incorrectly
Common SQL mistakes
Common JSON handling mistakes
Checking an error value inaccurately
Checking an error type inaccurately
Being puzzled about when to use channels or mutexes
Being puzzled about channel size
Being confused about when to use generics
Being confused about nil and the empty slice
Any says nothing