100 Go Mistakes (2022)

Teiva Harsanyi
Jun 23 · 4 min read

In 2019, I published a post called The Top 10 Most Common Mistakes I’ve Seen in Go Projects. As the name states, it was a way to explain some of the most common mistakes made by Go developers.

This post was pretty much popular (at least for me), and I started to think that writing a book about common mistakes might be a good idea. Hence, until late 2020, I kept collecting ideas throughout my experiences, be it my own errors (yes, I’m a provider of mistakes!) or some I observed in my job or on open-source projects. I also kept investigating posts, studies, books, etc.

End of 2020, I had collected enough content to reach a symbolic number of 100 errors. Right after, I contacted Manning, and we’ve started working together on a future book called 100 Go Mistakes: How to Avoid Them.

The book’s main idea is to list 100 of the most common mistakes made by Go developers in various aspects of the language. Each mistake will be illustrated as far as possible with real-life examples. It should help Go developers in working smarter, not harder.

Today, I’d like to announce that my book has reached the MEAP state (Manning Early Access Program):

There’s still a long way to go (the book is planned for Spring 2022), but I wanted to share two things as part of this post.

First of all, a 50% off discount code, valid on all formats through June 29: mlharsanyi.

Second of all, I’d like to share the current state of the table of contents (ToC). As it’s not yet set in stone, I was very much interested in your feedback. Here is the current ToC:

Basics:

  • Unintended variable shadowing
  • Ignoring logging side effects
  • Comparing values incorrectly
  • JSON handling mistakes
  • Formatting network addresses for IPv4 solely
  • Handling enums incorrectly
  • Not using defer
  • Ignoring how defer arguments and receivers are evaluated
  • Not closing resources
  • Creating confusion with octal literals
  • Neglecting integer overflows
  • Not understanding floating-points
  • Not using linters

Code organization:

  • Writing nested code
  • Misusing init functions
  • Always using getters and setters
  • Interface pollution
  • Interface on producer-side
  • Returning interfaces
  • interface{} says nothing
  • Not using the functional options pattern
  • Project misorganization
  • Creating utility packages
  • Ignoring package name collisions
  • Missing code documentation

Data structures:

  • Not understanding slice length and capacity
  • Inaccurate slice initialization
  • Creating conflicts using slice append
  • Not making slice copy correctly
  • Slice and memory leaks
  • Being confused about nil vs. empty slice
  • Not properly checking if a slice is empty
  • Inaccurate map initialization

Control structures:

  • Ignoring that elements are copied in range loops
  • Ignoring how arguments are evaluated in range loops
  • Ignoring the impacts of using element pointers in range loops
  • Making wrong assumptions during map iterations
  • Ignoring how the break statement work
  • Using defer inside a loop
  • Forgetting about the switch fall through behavior

String:

  • Not understanding the concept of rune
  • Inaccurate string iteration
  • Misusing trim functions
  • Under-optimized strings concatenation
  • Useless string conversion
  • Substring and memory leaks

Functions and methods:

  • Not knowing which type of receiver to use
  • Not using named result parameters
  • Unintended side-effects with named result parameters
  • Returning a nil receiver
  • Using a filename as a function input

Error management:

  • Panicking
  • Ignoring when to wrap an error
  • Comparing an error type inefficiently
  • Comparing an error value inefficiently
  • Handling an error twice
  • Ignoring an error
  • Not handling defer errors

Concurrency:

  • Mixing concurrency and parallelism
  • Concurrency isn’t always faster
  • Misunderstanding Go contexts
  • Not understanding what a race condition is
  • Append is not always race free
  • Goroutines and loop variables
  • Channel receive/send and context
  • Not using directional channels
  • Not using notification channels
  • Closing channels inaccurately
  • Buffered channel capacity and magic number
  • Not using nil channels
  • Passing a struct containing a sync field
  • Exporting concurrency primitives
  • Not using sync.RWMutex
  • Misusing sync.WaitGroup
  • Forgetting about sync.Cond
  • Not using errgroup package
  • Not using runtime.NumCPU()
  • time.After and memory leak
  • Ignoring false sharing

Testing:

  • Forgetting to fail a test
  • Not using table-driven tests
  • Not using test build flags
  • Not using the race option
  • Not checking goroutines leaks
  • Writing inaccurate benchmarks

Optimization:

  • Byte slice and map key
  • Pointers everywhere
  • Expecting that a pointer address is constant
  • Structure alignment
  • Inlining

Production:

  • Using the default http client and server
  • Go and Docker
  • Not exposing a pprof endpoint
  • Generating random numbers in security-sensitive applications

Anything you would have added/changed? Please let me know!

Thanks for reading.

solvingalgo

Solving Algo