Good documentation is crucial for maintainable code. Go has excellent built-in documentation tools and conventions. This guide covers Go’s comment conventions, godoc, and best practices for documenting your code. For more context, see Go Installation Guide, Go Ecosystem Overview, Go Best Practices.
Comment Basics
Single-Line Comments
package main
import "fmt"
func main() {
// This is a single-line comment
fmt.Println("Hello, World!")
x := 42 // Inline comment
fmt.Println(x)
}
Multi-Line Comments
package main
import "fmt"
/*
This is a multi-line comment.
It can span multiple lines.
Useful for longer explanations.
*/
func main() {
fmt.Println("Hello, World!")
}
Documentation Comments
Package Documentation
// Package users provides user management functionality.
//
// This package handles user creation, retrieval, and management.
// It includes authentication and authorization features.
package users
import "errors"
var ErrNotFound = errors.New("user not found")
Function Documentation
package users
// NewUser creates a new user with the given name and email.
//
// It returns a pointer to the newly created User.
// If name or email is empty, it returns nil.
func NewUser(name, email string) *User {
if name == "" || email == "" {
return nil
}
return &User{Name: name, Email: email}
}
// Save persists the user to the database.
//
// It returns an error if the user cannot be saved.
// Common errors include database connection failures.
func (u *User) Save() error {
// Implementation
return nil
}
Type Documentation
package users
// User represents a user in the system.
//
// It contains basic user information including ID, name, and email.
// The ID is automatically assigned when the user is created.
type User struct {
ID int // Unique identifier
Name string // User's full name
Email string // User's email address
IsActive bool // Whether the user is active
}
// Status represents the status of a user.
type Status int
const (
StatusActive Status = iota // User is active
StatusInactive // User is inactive
StatusSuspended // User is suspended
)
Constant and Variable Documentation
package config
// DefaultTimeout is the default timeout for operations in seconds.
const DefaultTimeout = 30
// MaxRetries is the maximum number of retries for failed operations.
const MaxRetries = 3
// ErrNotFound is returned when a resource is not found.
var ErrNotFound = errors.New("not found")
// ErrUnauthorized is returned when access is denied.
var ErrUnauthorized = errors.New("unauthorized")
Godoc Format
Godoc Conventions
package math
// Add returns the sum of a and b.
func Add(a, b int) int {
return a + b
}
// Subtract returns the difference of a and b.
func Subtract(a, b int) int {
return a - b
}
// Multiply returns the product of a and b.
func Multiply(a, b int) int {
return a * b
}
// Divide returns the quotient of a and b.
// It returns an error if b is zero.
func Divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
Godoc with Examples
package math
// Add returns the sum of a and b.
//
// Example:
// result := Add(2, 3)
// fmt.Println(result) // Output: 5
func Add(a, b int) int {
return a + b
}
Godoc with Code Blocks
package strings
// Split splits a string by a separator.
//
// Example:
// parts := Split("a,b,c", ",")
// // parts is []string{"a", "b", "c"}
//
// If the separator is not found, it returns a slice with the original string.
func Split(s, sep string) []string {
// Implementation
return nil
}
Documenting Interfaces
package io
// Reader is the interface that wraps the basic Read method.
//
// Read reads up to len(p) bytes into p. It returns the number of bytes
// read (0 <= n <= len(p)) and any error encountered. Even if Read
// returns n < len(p), it may use all of p as scratch space during the call.
// If some data is available but not len(p) bytes, Read conventionally
// returns what is available instead of waiting for more.
type Reader interface {
Read(p []byte) (n int, err error)
}
// Writer is the interface that wraps the basic Write method.
//
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
type Writer interface {
Write(p []byte) (n int, err error)
}
Documenting Methods
package users
// User represents a user in the system.
type User struct {
ID int
Name string
Email string
}
// GetName returns the user's name.
func (u *User) GetName() string {
return u.Name
}
// SetName sets the user's name.
func (u *User) SetName(name string) {
u.Name = name
}
// IsValid checks if the user has valid data.
//
// A user is valid if it has a non-empty name and email.
func (u *User) IsValid() bool {
return u.Name != "" && u.Email != ""
}
// Save persists the user to the database.
//
// It returns an error if the user cannot be saved.
// Possible errors include:
// - ErrInvalidUser: if the user data is invalid
// - ErrDatabaseError: if there's a database error
func (u *User) Save() error {
if !u.IsValid() {
return ErrInvalidUser
}
// Save to database
return nil
}
Using Godoc
Viewing Documentation
# View documentation for a package
go doc fmt
# View documentation for a function
go doc fmt.Println
# View documentation for a type
go doc http.Server
# View documentation for a method
go doc http.Server.ListenAndServe
# View all documentation
go doc -all fmt
# View in browser
go doc -http=:6060
# Then visit http://localhost:6060
Generating HTML Documentation
# Generate HTML documentation
godoc -html fmt > fmt.html
# Generate for entire project
godoc -html ./... > docs.html
Best Practices for Comments
✅ Good Comments
package calculator
// Add returns the sum of two integers.
func Add(a, b int) int {
return a + b
}
// User represents a user in the system.
type User struct {
ID int // Unique identifier
Name string // User's full name
Email string // User's email address
}
// IsValid checks if the user has valid data.
// A user is valid if it has a non-empty name and email.
func (u *User) IsValid() bool {
return u.Name != "" && u.Email != ""
}
// TODO: Add email validation
// FIXME: Handle edge case for empty strings
// NOTE: This function is performance-critical
// DEPRECATED: Use NewUser instead
❌ Bad Comments
// ❌ Bad: Obvious comments
x := 5 // Set x to 5
// ❌ Bad: Unclear comments
// Do the thing
func Process() {
// Implementation
}
// ❌ Bad: Outdated comments
// This function returns the user
// Actually, it returns the product now
func GetUser() *Product {
// Implementation
}
// ❌ Bad: No documentation for exported items
func PublicFunction() {
// Implementation
}
Documentation Examples
Complete Package Documentation
// Package users provides user management functionality.
//
// This package handles user creation, retrieval, updating, and deletion.
// It includes authentication and authorization features.
//
// Basic usage:
// user := users.NewUser("Alice", "[email protected]")
// if err := user.Save(); err != nil {
// log.Fatal(err)
// }
//
// For more information, see the documentation for specific types and functions.
package users
import "errors"
var (
// ErrNotFound is returned when a user is not found.
ErrNotFound = errors.New("user not found")
// ErrInvalidUser is returned when user data is invalid.
ErrInvalidUser = errors.New("invalid user")
)
// User represents a user in the system.
type User struct {
ID int
Name string
Email string
}
// NewUser creates a new user with the given name and email.
func NewUser(name, email string) *User {
return &User{Name: name, Email: email}
}
// Save persists the user to the database.
func (u *User) Save() error {
if u.Name == "" || u.Email == "" {
return ErrInvalidUser
}
// Save to database
return nil
}
// GetByID retrieves a user by ID.
func GetByID(id int) (*User, error) {
// Retrieve from database
return nil, ErrNotFound
}
Complete Type Documentation
package http
// Server defines parameters for running an HTTP server.
//
// The zero value for Server is a valid configuration.
type Server struct {
// Addr optionally specifies the TCP address for the server to listen on,
// in the form "host:port". If empty, ":http" is used.
Addr string
// Handler to invoke, http.DefaultServeMux if nil.
Handler Handler
// ReadTimeout is the maximum duration before timing out
// a read from the client's connection.
ReadTimeout time.Duration
// WriteTimeout is the maximum duration before timing out
// a write to the client's connection.
WriteTimeout time.Duration
}
// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.
//
// If srv.Addr is blank, ":http" is used.
//
// ListenAndServe always returns a non-nil error. After Shutdown or Close,
// the returned error is ErrServerClosed.
func (srv *Server) ListenAndServe() error {
// Implementation
return nil
}
Special Comments
Build Tags
// +build linux darwin
// This code only compiles on Linux and macOS
package main
// Or use the newer syntax
//go:build linux || darwin
package main
Deprecated Functions
// Deprecated: Use NewUser instead.
func CreateUser(name string) *User {
return NewUser(name, "")
}
TODO and FIXME
// TODO: Add email validation
func ValidateEmail(email string) bool {
return true
}
// FIXME: This doesn't handle edge cases
func Process(data string) error {
return nil
}
Documentation Tools
Using go doc
# View package documentation
go doc mypackage
# View function documentation
go doc mypackage.MyFunction
# View type documentation
go doc mypackage.MyType
# View method documentation
go doc mypackage.MyType.MyMethod
# View all documentation
go doc -all mypackage
# View in browser
go doc -http=:6060
Using godoc
# Start godoc server
godoc -http=:6060
# Generate HTML
godoc -html mypackage > mypackage.html
# Generate for all packages
godoc -html ./... > docs.html
Best Practices
✅ Good Practices
- Document all exported items - Functions, types, constants, variables
- Start with the name - “Function does X”
- Be concise - Clear and brief
- Use examples - Show how to use
- Keep comments updated - Sync with code
- Use proper grammar - Professional documentation
- Explain why, not what - The code shows what
- Use godoc format - Follow conventions
❌ Anti-Patterns
// ❌ Bad: No documentation
func Process() {
// Implementation
}
// ❌ Bad: Obvious comments
x := 5 // Set x to 5
// ❌ Bad: Outdated comments
// This returns the user
// (Actually returns the product now)
func GetUser() *Product {
// Implementation
}
// ❌ Bad: Unclear comments
// Do stuff
func DoStuff() {
// Implementation
}
Summary
Good documentation is essential:
- Document all exported items
- Use godoc format
- Start comments with the name
- Provide examples
- Keep comments updated
- Use proper grammar
- Follow Go conventions
Well-documented code is easier to maintain and use.
Comments