Skip to main content
โšก Calmops

Comments, Docstrings, and Documentation

Comments, Docstrings, and Documentation

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.

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.

Comments