Go Best Practices
In this page:
Always Handle Errors
Idiomatic Go treats every returned error as something to actively check, not an optional detail -- either handle it immediately, or explicitly wrap and propagate it up to a caller that can.
Example: Always Handle Errors
package main
import (
"fmt"
"strconv"
)
func parseAge(input string) (int, error) {
age, err := strconv.Atoi(input)
if err != nil {
return 0, fmt.Errorf("invalid age %q: %w", input, err)
}
return age, nil
}
func main() {
age, err := parseAge("thirty")
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("age:", age)
}
Login to try C/C++/Java/PHP code in the editor
Favor Early Returns Over Deep Nesting
Handling error and edge cases with an early return at the top of a function keeps the main logic at a single indentation level, which is generally easier to read than deeply nested if/else chains.
Example: Favor Early Returns Over Deep Nesting
package main
import "fmt"
func classify(age int) string {
if age < 0 {
return "invalid"
}
if age < 13 {
return "child"
}
if age < 20 {
return "teen"
}
return "adult"
}
func main() {
fmt.Println(classify(15))
fmt.Println(classify(-1))
}
Login to try C/C++/Java/PHP code in the editor
Accept Interfaces, Return Concrete Types
A common Go guideline is for functions to accept the smallest interface that satisfies their needs (making them flexible for callers), while returning a concrete type (making the result easy to work with directly).
Example: Accept Interfaces, Return Concrete Types
package main
import (
"fmt"
"strings"
)
func wordCount(r *strings.Reader) int {
buf := make([]byte, r.Len())
r.Read(buf)
return len(strings.Fields(string(buf)))
}
func main() {
r := strings.NewReader("Go favors small interfaces")
fmt.Println(wordCount(r))
}
Login to try C/C++/Java/PHP code in the editor
Keep Formatting Consistent
Running gofmt and go vet before committing catches formatting inconsistencies and likely bugs automatically, keeping the whole codebase in one consistent, machine-verified style rather than relying on manual review.
Note: Add 'gofmt -l .' and 'go vet ./...' to a pre-commit hook or CI pipeline to enforce this automatically.
Example: Keep Formatting Consistent
gofmt -l .
go vet ./...
⚠️ Run this command in your terminal.
- Ignoring errors by discarding them with _ instead of handling or explicitly propagating them.
- Writing large, deeply nested functions instead of favoring small, focused functions with early returns.
- Skipping gofmt/'go vet' before committing, letting avoidable style and correctness issues slip into the codebase.
- Always check and handle errors -- never silently discard them with a blank identifier.
- Keep functions small and focused; use early returns to avoid deep nesting.
- Run gofmt and go vet before every commit to catch style and correctness issues automatically.
- Favor small, composable interfaces over large ones, and accept interfaces while returning concrete types.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: