Introduction
Effective Go provides guidance for writing clear, idiomatic Go code. This document demonstrates best practices and common patterns that have emerged from the Go community and the language designers.Go is not just about getting the syntax right. It’s about writing code that is clear, idiomatic, and maintainable. This guide will help you write code that feels natural to other Go programmers.
Formatting
Formatting is handled automatically by thegofmt tool (also available as go fmt). This eliminates debates about style and ensures consistency across all Go code.
Key formatting conventions:
- Use tabs for indentation
- No line length limit (but be reasonable)
- Less parentheses than C/Java - operator precedence is clearer
- Comments are formatted by
gofmtalong with code
Commentary
Go provides C-style block comments/* */ and C++-style line comments //. Line comments are the norm; block comments appear mostly as package comments.
Package Comments
Every package should have a package comment, a block comment preceding the package clause:Doc Comments
Every exported name should have a doc comment. The first sentence should be a summary that starts with the name being declared:Doc comments work best as complete sentences, which allow for a wide variety of automated presentations. The first sentence should be a one-sentence summary that starts with the name being declared.
Names
Names are as important in Go as in any other language. They even have semantic effect: the visibility of a name outside a package is determined by whether its first character is uppercase.Package Names
Short and clear
Package names should be short, concise, and evocative. By convention, packages are given lowercase, single-word names.
No underscores
Don’t use underscores or mixedCaps. The package name is part of the type name when using it.
Interface Names
By convention, one-method interfaces are named by the method name plus an -er suffix:Reader, Writer, Formatter, CloseNotifier, etc.
MixedCaps
The convention in Go is to use MixedCaps or mixedCaps rather than underscores for multi-word names.Control Structures
If
In Go, a simpleif can include an initialization statement:
For
Go has only one looping construct: thefor loop. It has three forms:
- Classic for
- While-style
- Infinite loop
- Range
Switch
Go’sswitch is more flexible than C’s. The expressions need not be constants or even integers:
Unlike C, Go’s switch cases don’t fall through by default. Use
fallthrough explicitly if you need that behavior.Functions
Multiple Return Values
Go’s unusual feature of multiple return values is used extensively, especially for returning both a result and an error:Named Result Parameters
The return parameters of a Go function can be named and used as regular variables. When named, they are initialized to the zero values for their types when the function begins:Defer
Go’sdefer statement schedules a function call to be run after the function completes:
Data
Allocation with new
Go has two allocation primitives:new and make. new(T) allocates zeroed storage for a new item of type T and returns its address:
Allocation with make
make(T, args) creates slices, maps, and channels only, and returns an initialized (not zeroed) value of type T (not *T):
Remember:
make returns initialized values, while new returns pointers to zeroed values. Use make for slices, maps, and channels; use new for other types when you need a pointer.Slices
Slices hold references to an underlying array. If you pass a slice to a function, changes to the slice’s elements will be visible to the caller:Maps
Maps are a convenient and powerful built-in data structure that associates values of one type (the key) with values of another type (the element or value):Initialization
Constants
Constants in Go are just that—constant. They are created at compile time and can only be numbers, characters, strings, or booleans:The init function
Each source file can define its owninit function to set up state. init is called after all variable declarations have been evaluated:
Methods
Pointers vs. Values
The rule about pointers vs. values for receivers is that value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers:Interfaces and Other Types
Interfaces
Interfaces in Go provide a way to specify the behavior of an object. A type implements an interface by implementing its methods—no explicit declaration required:Type Assertions
A type assertion provides access to an interface value’s underlying concrete value:Type Switches
A type switch is like a regular switch but compares types instead of values:Concurrency
Share by Communicating
Do not communicate by sharing memory; instead, share memory by communicating. This philosophy is embodied in Go’s channels:Goroutines
Goroutines are lightweight threads managed by the Go runtime:Goroutines are very cheap. It’s practical to have thousands or even hundreds of thousands of goroutines in a single program.
Channels
Channels provide synchronization and communication:Errors
Error Handling
Go programs express error conditions with error values:Panic
The built-inpanic function stops ordinary control flow and begins panicking. Use it for unrecoverable errors:
Recover
Therecover function regains control of a panicking goroutine:
Best Practices Summary
Format with gofmt
Always run gofmt on your code. Consistent formatting is non-negotiable.
Write clear comments
Every exported name needs a doc comment. Make them complete sentences.
Keep it simple
Go is designed for simplicity. Don’t over-engineer solutions.
Handle errors
Check every error. Don’t ignore error return values.
Use concurrency wisely
Goroutines and channels are powerful, but use them when they add clarity.
Follow conventions
MixedCaps for names, -er suffix for interfaces, short package names.
Related Resources
Language Specification
Complete Go language reference
Memory Model
Understanding concurrency guarantees
GODEBUG
Backwards compatibility settings