← 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
// Every Go file belongs to a package; main builds an executable
package main

// Import the packages this program uses
import (
	"fmt"
	"strconv"
)

// Define the function parseAge
func parseAge(input string) (int, error) {
	// Short declaration: create age, err and infer the type
	age, err := strconv.Atoi(input)
	// Handle the error if one occurred
	if err != nil {
		return 0, fmt.Errorf("invalid age %q: %w", input, err)
	}
	// Send age, nil back to the caller
	return age, nil
}

// main is where the program starts running
func main() {
	// Short declaration: create age, err and infer the type
	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
// Every Go file belongs to a package; main builds an executable
package main

// Import the fmt package
import "fmt"

// Define the function classify
func classify(age int) string {
	// Check whether age < 0
	if age < 0 {
		// Send "invalid" back to the caller
		return "invalid"
	}
	// Check whether age < 13
	if age < 13 {
		// Send "child" back to the caller
		return "child"
	}
	if age < 20 {
		return "teen"
	}
	return "adult"
}

// main is where the program starts running
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
// Every Go file belongs to a package; main builds an executable
package main

// Import the packages this program uses
import (
	"fmt"
	"strings"
)

// Define the function wordCount
func wordCount(r *strings.Reader) int {
	// Short declaration: create buf and infer the type
	buf := make([]byte, r.Len())
	r.Read(buf)
	return len(strings.Fields(string(buf)))
}

// main is where the program starts running
func main() {
	// Short declaration: create r and infer the type
	r := strings.NewReader("Go favors small interfaces")
	// Print the values followed by a newline
	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 in your own terminal or Node.js environment.

Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
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.