Skip to main content

Comments, Docstrings, and Documentation

Created: May 8, 2026 9 min read

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

  1. Document all exported items - Functions, types, constants, variables
  2. Start with the name - “Function does X”
  3. Be concise - Clear and brief
  4. Use examples - Show how to use
  5. Keep comments updated - Sync with code
  6. Use proper grammar - Professional documentation
  7. Explain why, not what - The code shows what
  8. 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.

Resources

Comments

Share this article

Scan to read on mobile