The invisible interface
You write a package. You share it with a teammate. They import it and type go doc mypkg in the terminal. The screen is empty. They curse. You spent hours on the logic, but the documentation is invisible.
Go has a built-in documentation engine. It reads your comments and turns them into a clean reference. The tool is called go doc. It lives in the standard library. It extracts comments attached to declarations and formats them for humans. If you write the comments correctly, the tool does the rest. If you skip them, the tool has nothing to show. The community treats documentation as part of the code contract. Good godoc comments make a package usable without reading the source.
How godoc reads your comments
Godoc comments are standard Go comments placed immediately before a declaration. They follow strict rules so the parser can extract the right information. The comment must start with the capitalized name of the entity being documented. The first sentence is the summary. It must be a complete sentence ending with a period. The period is the separator. Everything after the period is detail. The tool uses this structure to generate the summary line and the body.
Here's the minimal pattern: package comment, function comment, summary sentence.
// Package mathutil provides simple math helpers.
// This comment documents the package itself.
// It appears when users run go doc mathutil.
package mathutil
// Add returns the sum of a and b.
// The first sentence is the summary. It ends with a period.
// Subsequent lines provide details about behavior.
func Add(a, b int) (int, error) {
// Implementation details go here.
// Comments inside functions are not godoc.
return a + b, nil
}
The package comment documents the package. It should describe what the package does and how to use it. The function comment documents the function. It starts with Add. The summary is Add returns the sum of a and b. The tool splits on the period. The rest is the body.
Comments inside functions are ignored by go doc. They are for the developer reading the source. Godoc only cares about comments attached to declarations.
The summary sentence rule
The first sentence is the headline. Make it count. The summary appears in search results, in go doc output, and on pkg.go.dev. It should describe what the entity does, not what the name is.
Write // Add returns the sum of a and b. not // This function adds two numbers. The name is already Add. The summary should add value.
The period is critical. If you forget the period, the tool treats the entire comment as the summary. The output becomes a wall of text. If you have a period in an acronym like U.S.A., the tool splits too early. Use USA or rephrase the sentence to avoid internal periods.
Convention aside: gofmt formats comments too. It aligns block comments and trims whitespace. Run gofmt on your code. It ensures comments look consistent. Most editors run it on save. Trust the tool. Argue logic, not formatting.
Realistic package structure
Real packages have types, methods, and constants. Comments apply to everything exported. Unexported names start with a lowercase letter. go doc hides unexported names by default. You don't need godoc comments for private helpers. Inline comments are enough.
Here's a realistic struct with a method and a receiver.
// Counter tracks a running total.
// The zero value is safe to use.
type Counter struct {
// mu protects value from concurrent access.
// Fields can have comments too.
mu sync.Mutex
value int
}
// Increment adds one to the counter.
// It is safe for concurrent use.
func (c *Counter) Increment() {
c.mu.Lock()
// Lock before modifying shared state.
defer c.mu.Unlock()
c.value++
}
// Value returns the current count.
// It returns a copy of the internal value.
func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
The struct comment starts with Counter. The method comments start with Increment and Value. The receiver name is c. It matches the type Counter. This is the convention. Receiver names are usually one or two letters. Use (c *Counter), not (this *Counter) or (self *Counter).
Convention aside: Public names start with a capital letter. Private names start lowercase. There are no keywords like public or private. Capitalization controls visibility. go doc only shows exported names. If you want to document something, export it. If you don't want to document it, keep it unexported.
Examples that run as tests
Go supports example functions in godoc comments. These are special. They document usage and run as tests. The function name must start with Example. It can be Example for the package, ExampleAdd for a function, or ExampleCounter_Increment for a method.
Here's an example function for the Add function.
// ExampleAdd demonstrates how to use Add.
func ExampleAdd() {
result, err := Add(2, 3)
if err != nil {
panic(err)
}
// Print the result for the test to check.
fmt.Println(result)
// Output:
// 5
}
The // Output: comment tells the test runner what to expect. When you run go test, the tool executes the example. It captures the output. It compares it to the Output comment. If they match, the test passes. If they differ, the test fails.
Examples are documentation that runs. If the example breaks, the docs lie. This keeps documentation in sync with the code. Users see working examples. They can copy the code and run it.
Convention aside: go test -run Example runs only examples. Use this to verify docs quickly. Examples should be minimal. Show the happy path. Skip error handling unless the error is the point.
Pitfalls and tooling
Godoc comments are easy to get wrong. The tooling helps catch mistakes. go vet checks for missing docs on exported symbols. If you export a function without a comment, go vet warns you.
The compiler rejects the program with exported function Add should have comment or be unexported if you run go vet on a package with undocumented exports. This is not a compile error. It's a static analysis warning. Fix it by adding a comment or lowering the case of the name.
Common pitfalls include:
- Missing the period in the summary. The summary runs on forever.
- Starting the comment with the wrong name.
// This function...instead of// Add.... - Using periods in acronyms.
U.S.A.breaks the summary split. - Forgetting the package comment. Users run
go doc pkgand see nothing. - Documenting unexported names.
go dochides them. The comment is wasted effort.
Convention aside: if err != nil { return err } is verbose by design. The community accepts the boilerplate because it makes the unhappy path visible. Godoc comments should mention errors if the function returns them. Say // Add returns an error if the sum overflows. This tells users what to expect.
Section closer: Trust go vet. It catches the missing docs.
When to use godoc vs other docs
Documentation lives at different levels. Use the right tool for the job.
Use godoc comments for exported declarations. They document the API. They power go doc and pkg.go.dev. They are part of the code.
Use inline comments for implementation details. They explain complex logic inside functions. They help developers reading the source. They are not visible to users.
Use example functions for usage patterns. They show how to use the API. They run as tests. They keep docs in sync.
Use README files for project overview. They describe the project, installation, and high-level architecture. They live in the repository root. They are not parsed by go doc.
Use unexported names for internal helpers. They don't need godoc comments. Inline comments are enough.
Section closer: Exported names get godoc. Unexported names get inline comments.