50-Mistake-Golang

50 Common Mistakes in Go Development

Go is a powerful and concise language, but its simplicity can lead to subtle mistakes, especially for developers new to its paradigms. This guide outlines 50 common pitfalls in Go development, with explanations and examples to help you write robust and idiomatic Go code. These mistakes are derived from common errors observed in the Go community, translated and adapted from the referenced source.

1. Left Curly Brace on a New Line

Go’s syntax requires opening curly braces ({) to be on the same line as the statement. A newline before { causes a compilation error.

// Wrong
func main()
{
    fmt.Println("Hello")
}

// Correct
func main() {
    fmt.Println("Hello")
}

2. Unused Variables

Go disallows unused variables to enforce clean code. Declared variables must be used, or the code won’t compile.

// Wrong
func main() {
    x := 10 // Error: x declared but not used
}

// Correct
func main() {
    x := 10
    fmt.Println(x)
}

Use _ for variables you intentionally ignore, like loop indices or return values.

3. Unused Imports

Unused import statements cause compilation errors. Remove or use imported packages.

Use goimports to automatically manage imports.

4. Short Variable Declarations Only in Functions

The := operator for short variable declarations is only valid inside functions, not at package level.

5. Redeclaring Variables with :=

Short declarations (:=) cannot redeclare variables in the same block if they’re already declared, unless at least one new variable is introduced.

6. Accidental Variable Shadowing

Short declarations in inner scopes can shadow outer variables, leading to unexpected behavior.

Use = to assign to the outer variable or avoid redeclaring.

7. Misusing nil Slices

A nil slice has len=0, cap=0, and can be appended to, but developers may assume it’s invalid.

Check for nil explicitly only when necessary, as len(s) == 0 is usually sufficient.

8. Appending to a Slice Without Checking Capacity

Appending to a slice may reallocate the underlying array if capacity is exceeded, affecting performance.

Preallocate capacity with make when the size is predictable.

9. Modifying Slices Sharing the Same Array

Slices referencing the same underlying array share modifications, which can lead to bugs.

Use copy to create independent slices.

10. Incorrect Slice Capacity in Reslicing

Reslicing with s[i:j:k] sets cap = k-i. Misjudging k can limit future appends.

11. Ignoring Return Values

Go functions often return multiple values (e.g., result and error). Ignoring them can hide issues.

12. Misusing defer

The defer statement delays execution until the surrounding function returns, but arguments are evaluated immediately.

Use a closure to capture the current state.

13. Multiple defer Order

Deferred functions execute in LIFO (last-in, first-out) order.

14. Panic in Deferred Functions

A panic in a deferred function can overwrite an earlier panic, complicating debugging.

Use recover carefully to handle panics.

15. Incorrect recover Usage

recover only works in deferred functions and captures panics in the current goroutine.

16. Goroutines Without Synchronization

Goroutines run concurrently, and unsynchronized access to shared variables causes race conditions.

Use sync.Mutex or channels for synchronization.

17. Goroutines Leaking

Goroutines that never exit (e.g., blocked on channels) can leak memory.

Use context cancellation or close channels to signal exit.

18. Closing Unbuffered Channels

Closing an unbuffered channel before a receiver is ready causes a panic.

Ensure receivers are ready or use buffered channels.

19. Sending to a Closed Channel

Sending to a closed channel causes a panic.

Check if a channel is closed using a select or boolean flag.

20. Misusing Buffered Channels

Buffered channels can block if full, leading to deadlocks if not handled.

Monitor buffer size or use non-blocking sends with select.

21. Incorrect Range Over Channels

Ranging over a channel continues until the channel is closed.

Always close channels when done sending.

22. Nil Channels

Sending or receiving on a nil channel blocks forever.

Initialize channels with make before use.

23. Incorrect Interface Assertions

Type assertions on interfaces can panic if the type doesn’t match.

Use the comma-ok idiom to check safely.

24. Empty Interface Misuse

Using interface{} sacrifices type safety and can lead to runtime errors.

Prefer specific interfaces or generics (Go 1.18+).

25. String Conversion Errors

Converting non-numeric strings to integers with strconv.Atoi returns an error that must be checked.

Always handle errors.

26. Incorrect String Concatenation

Repeated string concatenation in a loop is inefficient due to immutability.

Use strings.Builder for efficiency.

27. Map Initialization

Using an uninitialized map (var m map[K]V) causes a panic on write.

Initialize with make or a literal.

28. Map Iteration Order

Map iteration order is random by design, which can surprise developers expecting consistency.

Sort keys explicitly if order matters.

29. Concurrent Map Access

Maps are not safe for concurrent writes.

Use sync.RWMutex or sync.Map for concurrency.

30. Incorrect Map Deletion

Deleting from a map with a non-existent key is safe but has no effect.

Check existence with the comma-ok idiom if needed.

31. Incorrect Struct Field Access

Accessing unexported fields (lowercase) from another package causes compilation errors.

Use exported fields (uppercase).

32. Struct Copy Semantics

Structs are copied by value, which can lead to unexpected behavior.

Use pointers for shared modifications.

33. JSON Marshalling Unexported Fields

Unexported struct fields are ignored during JSON marshalling.

Use exported fields for JSON.

34. Incorrect Time Comparison

Comparing time.Time values with == checks both time and location.

Use Equal for time comparison.

35. Time Parsing Errors

Parsing invalid time strings with time.Parse returns an error that must be checked.

Always handle errors.

36. Incorrect Error Wrapping

Using fmt.Errorf without wrapping the original error loses context.

Use %w to wrap errors (Go 1.13+).

37. Ignoring Context Cancellation

Ignoring context.Context cancellation can lead to resource leaks.

Check ctx.Done().

38. Incorrect HTTP Handler Registration

Registering HTTP handlers with the same path overwrites earlier handlers.

Use a router like http.ServeMux carefully.

39. HTTP Server Shutdown

Not shutting down HTTP servers gracefully can drop connections.

Use http.Server.Shutdown.

40. Incorrect File Closing

Not closing files can leak resources.

Use defer to close.

41. Incorrect Loop Variable Capture

Loop variables in closures (e.g., goroutines) are reused, causing unexpected behavior.

Copy the loop variable.

42. Incorrect Array vs. Slice

Arrays ([n]T) and slices ([]T) have different behaviors. Arrays are fixed-size and copied by value.

43. Incorrect Pointer Receiver

Using value receivers in methods prevents modifying the receiver.

Use pointer receivers.

44. Nil Pointer Dereference

Dereferencing a nil pointer causes a panic.

Check for nil before dereferencing.

45. Incorrect Method Expression

Using method expressions requires explicit receiver passing.

46. Incorrect Interface Implementation

A type must implement all methods of an interface, including pointer vs. value receiver nuances.

47. Incorrect Slice Initialization

Slices must be initialized before use, but make syntax varies.

Use correct make arguments.

48. Incorrect Range Over Strings

Ranging over a string iterates over bytes, not runes, which can break UTF-8 handling.

Use []rune for rune iteration.

49. Incorrect Signal Handling

Not handling OS signals can prevent graceful shutdown.

Use os/signal.

50. Ignoring Go’s Simplicity

Overcomplicating solutions with unnecessary abstractions violates Go’s philosophy of simplicity.

Embrace straightforward, idiomatic Go.

Conclusion

These 50 mistakes cover syntax, concurrency, memory management, and idiomatic practices critical for Go development. By understanding these pitfalls and their solutions, you can write cleaner, more efficient, and maintainable Go code. Refer to the Go documentation and tools like go vet, golint, and golangci-lint to catch these issues early.

Last updated