Writing idiomatic, effective, and clean Go code
Writing idiomatic, effective, and clean Go code involves adhering to a set of principles and practices that leverage the language’s unique design.
Here are some key guidelines, often considered “commandments” in the Go community:
The Ten Commandments of Idiomatic Go:
-
Thou Shalt Format Thy Code with golangci-lint:
- Guideline: Use the
golangci-lint tool religiously. It enforces a standard, opinionated style for indentation, spacing, and alignment.
We support the tool directly in our Makefile. You can run:
and
- This consistency makes Go code highly readable and reduces time spent on style debates. Many editors and IDEs integrate
gofmt automatically on save. The goimports tool is a superset of gofmt that also manages imports.
- References:
-
Thou Shalt Handle Errors Explicitly:
- Guideline: Go treats errors as values. Functions that can fail should return an error as the last return value.
Always check error returns and handle them gracefully. Avoid ignoring errors using the blank identifier
_.
Propagate errors back up the call stack or handle them appropriately (e.g., logging, retrying).
Use errors.Is and errors.As for checking error types or values, especially in Go 1.13+. Error strings should be lowercase and not end with punctuation.
- References:
-
Thou Shalt Favor Composition Over Inheritance:
- Guideline: Go does not have traditional class inheritance.
Achieve code reuse and flexibility through composition (embedding structs) and interfaces.
Design small, focused interfaces that define behavior.
- References:
-
Thou Shalt Design Small, Focused Interfaces:
- Guideline: Go’s interfaces are implicitly implemented.
Define interfaces on the consumer side, specifying only the methods a client needs.
This promotes decoupling and testability.
Name interfaces with an “-er” suffix (e.g.,
Reader, Writer) when they define a single method, though this is a convention, not a strict rule for all interfaces.
- References:
-
Thou Shalt Write Concurrent Code Using Goroutines and Channels:
- Guideline: Embrace Go’s built-in concurrency primitives.
Use goroutines for concurrent execution and channels for safe communication and synchronization between them.
Understand the Go memory model and use tools like the race detector to avoid race conditions.
- References:
-
Thou Shalt Not Use Global Variables Extensively:
- Guideline: Limit the use of global variables to avoid side effects and improve testability and maintainability.
Pass data explicitly through function parameters and return values, or use struct fields.
- References:
-
Thou Shalt Keep Functions Small and Single-Purpose:
- Guideline: Design functions that do one thing well.
Short, focused functions are easier to understand, test, and maintain.
Avoid excessive nesting and complex logic within a single function.
- References:
-
Thou Shalt Write Tests:
- Guideline: Go has a built-in testing framework.
Write unit tests for your code to ensure correctness and provide examples of how to use your functions and types.
Table-driven tests are a common and effective pattern in Go.
- References:
-
Thou Shalt Document Exported Symbols:
- Guideline: Provide clear and concise documentation for all exported functions, types, variables, and constants.
Comments should explain what the code does and why, especially for non-obvious parts.
Use Godoc conventions for easily generated documentation.
- References:
-
Thou Shalt Be Mindful of Performance and Allocations:
- Guideline: While Go is performant, be aware of potential bottlenecks.
Understand how slices, maps, and pointers work to minimize unnecessary allocations and garbage collection pressure, especially in performance-critical code.
Use tools like the pprof profiler to identify performance issues.
Additional Recommended Reading and Style Guides:
These resources provide more detailed explanations and examples for each of the guidelines mentioned above, helping you to write more effective and idiomatic Go code.