← Back to Go Course | Chapter 14: Standard Library & HTTP | Lesson 10 of 10

Go Best Practices

Best practices are the community's collected wisdom on writing Go that's clear, safe, and easy for the next person (including future you) to understand.

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

markup
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)
}

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

markup
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))
}

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

markup
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))
}

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

bash
gofmt -l .
go vet ./...

⚠️ Run this command in your terminal.

Common Mistakes
  1. Ignoring errors by discarding them with _ instead of handling or explicitly propagating them.
  2. Writing large, deeply nested functions instead of favoring small, focused functions with early returns.
  3. Skipping gofmt/'go vet' before committing, letting avoidable style and correctness issues slip into the codebase.
Chapter Summary
  • 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.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.