Make Your Go Docs Self‑Testing with Executable Examples

The author discovered that stale Go documentation caused runtime panics, then leveraged Go's hidden feature of executable examples to turn code snippets into live tests that run on pkg.go.dev, integrate into CI, and keep docs and code in sync, dramatically improving developer efficiency.

Golang Shines
Golang Shines
Golang Shines
Make Your Go Docs Self‑Testing with Executable Examples

Step 1: Turn a simple function into an executable example

Assume a basic string‑reversal function:

// reverse/reverse.go
package reverse

func String(s string) string {
    runes := []rune(s)
    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
        runes[i], runes[j] = runes[j], runes[i]
    }
    return string(runes)
}

Write an Example function in the test file:

// reverse/reverse_test.go
func ExampleString() {
    fmt.Println(String("hello"))
    // Output:
    // olleh
}

Key point: the comment // Output: is the expected result; go test compares actual output with this expectation.

If the output mismatches, the test fails, forcing you to update the documentation or the code.

Running go test -v ./reverse shows the example passing:

$ go test -v ./reverse
=== RUN   ExampleString
--- PASS: ExampleString (0.00s)
PASS

The example appears on pkg.go.dev with a "Run" button, letting users try the code in the browser without installing Go.

Step 2: Add "live" documentation to common utility functions

Example 1 – Factorial with boundary handling

// factorial/factorial.go
func Calc(n int) (int, error) {
    if n < 0 {
        return 0, fmt.Errorf("negative number: %d", n)
    }
    if n <= 1 {
        return 1, nil
    }
    result := 1
    for i := 2; i <= n; i++ {
        result *= i
    }
    return result, nil
}
// factorial/factorial_test.go
func ExampleCalc_normal() {
    result, _ := Calc(5)
    fmt.Println(result)
    // Output:
    // 120
}

func ExampleCalc_error() {
    _, err := Calc(-3)
    fmt.Println(err)
    // Output:
    // negative number: -3
}

Example 2 – Handling unordered output

When deduplicating a slice with a map, iteration order is nondeterministic. Use the special comment // Unordered output: to indicate that order does not matter.

// unique/unique.go
func Ints(nums []int) []int {
    seen := make(map[int]bool)
    var result []int
    for _, n := range nums {
        if !seen[n] {
            seen[n] = true
            result = append(result, n)
        }
    }
    return result
}
// unique/unique_test.go
func ExampleInts() {
    nums := []int{3, 1, 2, 1, 3}
    unique := Ints(nums)
    sort.Ints(unique) // ensure deterministic order for the example
    fmt.Println(unique)
    // Output:
    // [1 2 3]
    // Or, to ignore order:
    // Unordered output:
    // [1 2 3]
}

Step 3: Advanced usage – grouping examples

Package‑level example

// mathutil/mathutil_test.go
func Example() {
    a := Add(2, 3) // 5
    b := Multiply(a, 4) // 20
    fmt.Println(IsEven(b)) // true
    // Output:
    // true
}

Method example for a type

// counter/counter.go
type Counter struct { count int }
func (c *Counter) Inc() { c.count++ }
func (c *Counter) Value() int { return c.count }
// counter/counter_test.go
func ExampleCounter_basic() {
    var c Counter
    c.Inc()
    c.Inc()
    fmt.Println(c.Value())
    // Output:
    // 2
}

func ExampleCounter_reset() {
    c := Counter{count: 10}
    c.count = 0 // direct reset for demo
    fmt.Println(c.Value())
    // Output:
    // 0
}

Step 4: Integrate into the development workflow

Local development – examples are tests

# Run only examples
$ go test ./... -run Example
# All examples pass → safe to publish docs

CI/CD – prevent "doc rot"

# .github/workflows/test.yml
- name: Verify examples
  run: go test -run Example ./...
# If an example fails, the PR is blocked, forcing documentation updates.

Online docs – users can "play" code

Visit pkg.go.dev/your‑tool‑library Click "Run" on an example to see the result instantly in the browser.

No need to git clone or go install – zero‑friction experience.

Retrospective – why this feature is a game‑changer

Documentation stays in sync with code, never becomes outdated.

Examples double as tests, so code changes must pass documentation checks first.

Users can try functions directly online, lowering the barrier to adoption.

One write‑once approach boosts efficiency for library authors and maintainers.

Ideal for open‑source projects that need many code demonstrations.

Naming convention: Example + TypeName + _ + MethodName – the doc tool links them automatically.

In a later incident, a colleague asked whether the UniqueInts function returns nil or an empty slice for an empty input. Instead of digging through code and sending screenshots, I pointed them to the live example, changed the input to []int{}, clicked Run, and the output clarified the behavior in seconds.

That moment reinforced the belief that the best documentation is not the prettiest prose, but something the user can interact with directly.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

CI/CDPackage documentationDocumentation testingExample functionsExecutable examples
Golang Shines
Written by

Golang Shines

We share daily the latest Golang technical articles, practical resources, language news, tutorials, and real-world projects to help everyone learn and improve.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.