Go Commands & Documentation — Practical Tasks¶
Task 1: Write a Fully Documented Package (Beginner)¶
Description¶
Create a simple geometry package with complete documentation that would look professional on pkg.go.dev.
Requirements¶
- Package-level doc comment
- All exported types, functions, and constants documented
- At least one example function that can be run as a test
- Pass
go vet ./...with no warnings
Starter Code¶
// TODO: Add package-level documentation here
package geometry
import "math"
// TODO: Add doc comment for Shape
type Shape interface {
// TODO: Add doc comment for Area
Area() float64
// TODO: Add doc comment for Perimeter
Perimeter() float64
}
// TODO: Add doc comment for Circle
type Circle struct {
// TODO: Add doc comment for Radius
Radius float64
}
// TODO: Add doc comment for Area
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
// TODO: Add doc comment for Perimeter
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
// TODO: Add doc comment for Rectangle
type Rectangle struct {
// TODO: Add doc comment
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
Create a geometry_test.go file with at least one Example* function.
Evaluation Criteria¶
- Package doc comment explains what the package does and how to use it
- All exported symbols have doc comments starting with the symbol name
- At least one
ExampleCircle_Area()function in_test.go -
go vet ./...passes without warnings -
go doc -all .shows complete, readable documentation - Example functions have
// Output:comments and passgo test
Task 2: Go Environment Inspector (Beginner)¶
Description¶
Write a Go program that prints a formatted report of the current Go environment, using os/exec to run go env -json and parse the output.
Requirements¶
- Run
go env -jsonand parse the JSON output - Display key environment variables in a formatted table
- Highlight if GOPROXY is set to non-default
- Show whether CGO is enabled
Starter Code¶
package main
import (
"encoding/json"
"fmt"
"os/exec"
)
// EnvReport holds selected Go environment variables
type EnvReport struct {
GOVERSION string
GOROOT string
GOPATH string
GOMODCACHE string
GOCACHE string
GOPROXY string
GOOS string
GOARCH string
CGO_ENABLED string
}
func getGoEnv() (*EnvReport, error) {
// TODO: Run `go env -json` and unmarshal into EnvReport
// Hint: use exec.Command("go", "env", "-json")
// Then: cmd.Output() to get the JSON bytes
// Then: json.Unmarshal to parse
panic("not implemented")
}
func printReport(r *EnvReport) {
// TODO: Print a formatted table like:
// Go Version: go1.21.0
// GOROOT: /usr/local/go
// GOPATH: /home/user/go
// ...
// GOPROXY: [custom] https://proxy.company.com ⚠️
panic("not implemented")
}
func main() {
report, err := getGoEnv()
if err != nil {
fmt.Println("Error:", err)
return
}
printReport(report)
}
Evaluation Criteria¶
- Successfully runs
go env -jsonand parses output - Prints all required variables in a readable format
- Highlights non-default
GOPROXYvalues - Handles errors gracefully
- The program itself has doc comments
Task 3: Documentation Quality Checker (Intermediate)¶
Description¶
Write a tool that checks whether all exported symbols in a Go package have documentation comments, and reports which ones are missing.
Requirements¶
- Use
go/parserandgo/astto parse a Go source file - Report all exported functions, types, and constants without doc comments
- Exit with code 1 if any documentation is missing
- Accept a directory path as command-line argument
Starter Code¶
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"path/filepath"
"strings"
)
type MissingDoc struct {
Name string
Kind string // "func", "type", "const", "var"
Line int
File string
}
// checkFile parses a Go file and returns all exported symbols missing doc comments
func checkFile(filename string) ([]MissingDoc, error) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
if err != nil {
return nil, err
}
var missing []MissingDoc
ast.Inspect(f, func(n ast.Node) bool {
switch decl := n.(type) {
case *ast.FuncDecl:
// TODO: check if exported and has doc comment
// Hint: ast.IsExported(decl.Name.Name) && decl.Doc == nil
_ = decl
case *ast.GenDecl:
// TODO: handle type, const, var declarations
// Hint: iterate decl.Specs for *ast.TypeSpec, *ast.ValueSpec
_ = decl
}
return true
})
return missing, nil
}
func main() {
dir := "."
if len(os.Args) > 1 {
dir = os.Args[1]
}
var allMissing []MissingDoc
// TODO: Walk directory, call checkFile for each .go file
// Skip test files (*_test.go)
// Skip files starting with generated header
_ = filepath.Walk
_ = strings.HasSuffix
if len(allMissing) > 0 {
for _, m := range allMissing {
fmt.Printf("%s:%d: exported %s %q missing doc comment\n",
m.File, m.Line, m.Kind, m.Name)
}
os.Exit(1)
}
fmt.Println("All exported symbols are documented!")
}
Evaluation Criteria¶
- Correctly identifies exported functions without doc comments
- Correctly identifies exported types without doc comments
- Skips test files
- Reports file, line number, symbol name, and kind
- Exits with code 1 when issues are found
- Works on the current directory by default
Task 4: Build Tag Demonstrator (Intermediate)¶
Description¶
Create a multi-platform greeting application that uses build tags to provide platform-specific implementations.
Requirements¶
- Create a
platformpackage with aPlatformName()function - Provide implementations for: linux, darwin, windows
- Provide a fallback for unknown platforms
- Write a main program that uses the platform package
- Document all exported symbols
File Structure¶
platform/
├── platform_linux.go (//go:build linux)
├── platform_darwin.go (//go:build darwin)
├── platform_windows.go (//go:build windows)
├── platform_other.go (//go:build !linux && !darwin && !windows)
└── platform.go (doc.go with package comment)
main.go
Starter Code for platform/platform.go¶
// Package platform provides OS-specific information.
// It automatically selects the correct implementation
// based on the build target operating system.
//
// Usage:
//
// name := platform.PlatformName()
// fmt.Printf("Running on: %s\n", name)
package platform
Starter Code for platform/platform_linux.go¶
//go:build linux
// +build linux
package platform
// PlatformName returns the name of the current operating system.
func PlatformName() string {
// TODO: return "Linux"
panic("not implemented")
}
// PlatformIcon returns an emoji representing the platform.
func PlatformIcon() string {
// TODO: return "🐧"
panic("not implemented")
}
Evaluation Criteria¶
- Build tags are correct (
//go:build linux, etc.) -
go build -tags ...selects the correct implementation - Fallback file compiles when no specific platform tag matches
- All exported functions are documented
-
go vet ./...passes - Main program uses the platform package correctly
Task 5: Generate an Enum with Stringer (Intermediate)¶
Description¶
Use go generate and the stringer tool to automatically generate String() methods for an enum type.
Requirements¶
- Install
stringer:go install golang.org/x/tools/cmd/stringer@latest - Create an enum type with
//go:generatedirective - Run
go generate ./... - Verify the generated
String()method is correct - Write tests that use the generated
String()method
Starter Code¶
// status.go
package order
// OrderStatus represents the current state of an order.
//go:generate stringer -type=OrderStatus -output=order_status_string.go
// OrderStatus values represent the lifecycle of an order.
type OrderStatus int
const (
// StatusPending indicates the order has been received but not yet processed.
StatusPending OrderStatus = iota
// StatusProcessing indicates the order is being prepared.
StatusProcessing
// StatusShipped indicates the order has been dispatched.
StatusShipped
// StatusDelivered indicates the order has been delivered.
StatusDelivered
// StatusCancelled indicates the order was cancelled.
StatusCancelled
)
// status_test.go
package order
import "testing"
func TestOrderStatusString(t *testing.T) {
tests := []struct {
status OrderStatus
want string
}{
{StatusPending, "StatusPending"},
{StatusShipped, "StatusShipped"},
// TODO: add more cases
}
for _, tc := range tests {
if got := tc.status.String(); got != tc.want {
t.Errorf("status %d: got %q, want %q", tc.status, got, tc.want)
}
}
}
Evaluation Criteria¶
-
//go:generatedirective is present and correct -
go generate ./...produces a*_string.gofile - Generated
String()method returns correct names - Tests pass with
go test ./... - Generated file has a comment saying it was generated
Task 6: Module Dependency Reporter (Intermediate)¶
Description¶
Write a tool that uses go list -json to analyze a Go module's dependencies and generate a report.
Requirements¶
- Run
go list -m -json alland parse the output - Count direct vs indirect dependencies
- Identify the oldest and newest dependencies by version
- Check if any dependency is
replaced ingo.mod - Output a formatted summary report
Starter Code¶
package main
import (
"encoding/json"
"fmt"
"os/exec"
"strings"
)
// Module represents a Go module from `go list -m -json all`
type Module struct {
Path string
Version string
Indirect bool
Replace *Module
Error *struct {
Err string
}
}
func listModules() ([]Module, error) {
cmd := exec.Command("go", "list", "-m", "-json", "all")
out, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("go list failed: %w", err)
}
// go list -json outputs multiple JSON objects, not an array
// Use json.Decoder to read them one by one
var modules []Module
dec := json.NewDecoder(strings.NewReader(string(out)))
for dec.More() {
var m Module
if err := dec.Decode(&m); err != nil {
return nil, err
}
modules = append(modules, m)
}
return modules, nil
}
func generateReport(modules []Module) {
// TODO: Print:
// - Total modules: N
// - Direct dependencies: N
// - Indirect dependencies: N
// - Replaced modules: list them
// - Module list (direct only): sorted
panic("not implemented")
}
func main() {
modules, err := listModules()
if err != nil {
fmt.Println("Error:", err)
return
}
generateReport(modules)
}
Evaluation Criteria¶
- Correctly parses multi-object JSON from
go list - Correctly distinguishes direct from indirect dependencies
- Lists replaced modules with their replacements
- Output is clean and readable
- Tool itself has proper doc comments
Task 7: Custom go vet Analyzer (Advanced)¶
Description¶
Write a custom Go analyzer that reports when fmt.Println is used outside of test files. This demonstrates understanding of the go/analysis framework.
Requirements¶
- Use
golang.org/x/tools/go/analysis - Report all calls to
fmt.Printlnin non-test files - Include file and line number in the report
- Make it usable as a standalone tool AND as part of
golangci-lint
Starter Code¶
package nofmtprintln
import (
"go/ast"
"strings"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/singlechecker"
)
// Analyzer reports calls to fmt.Println in non-test files.
var Analyzer = &analysis.Analyzer{
Name: "nofmtprintln",
Doc: "reports uses of fmt.Println in non-test code",
Run: run,
}
func run(pass *analysis.Pass) (interface{}, error) {
for _, file := range pass.Files {
// TODO: Check if this is a test file
filename := pass.Fset.File(file.Pos()).Name()
if strings.HasSuffix(filename, "_test.go") {
continue
}
// TODO: Walk the AST looking for fmt.Println calls
// Hint: use ast.Inspect
// Look for *ast.CallExpr where:
// Fun is *ast.SelectorExpr
// Fun.X is *ast.Ident with Name == "fmt"
// Fun.Sel is *ast.Ident with Name == "Println"
// Then call: pass.Reportf(call.Pos(), "use log package instead of fmt.Println")
_ = ast.Inspect
_ = file
}
return nil, nil
}
// Main makes this a standalone tool
func Main() {
singlechecker.Main(Analyzer)
}
// main.go (in cmd/nofmtprintln/)
package main
import "your/module/nofmtprintln"
func main() {
nofmtprintln.Main()
}
Evaluation Criteria¶
- Analyzer correctly finds
fmt.Printlncalls - Test files are excluded
- Reports include file, line number, and message
- Can be run:
go vet -vettool=./nofmtprintln ./... - Has proper documentation
Task 8: go doc Web Server Wrapper (Advanced)¶
Description¶
Build a simple HTTP server that wraps go doc and serves documentation in a browser-friendly format (without requiring godoc installation).
Requirements¶
- Accept package path as URL parameter
- Run
go docand format the output as HTML - Support linking between packages
- Handle errors gracefully
Starter Code¶
package main
import (
"fmt"
"html"
"net/http"
"os/exec"
"strings"
)
func docHandler(w http.ResponseWriter, r *http.Request) {
pkg := r.URL.Query().Get("pkg")
if pkg == "" {
pkg = "fmt" // default
}
// TODO: Run `go doc -all <pkg>` and get output
cmd := exec.Command("go", "doc", "-all", pkg)
out, err := cmd.Output()
if err != nil {
// TODO: Handle error — show error page
http.Error(w, "Package not found: "+pkg, http.StatusNotFound)
return
}
// TODO: Format the output as HTML
// 1. Escape HTML special characters
// 2. Add heading formatting (lines starting with "func", "type", "const")
// 3. Wrap code blocks in <pre><code>
// 4. Add a simple search box
// 5. Render the response
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(w, `<html>
<head><title>Go Docs: %s</title></head>
<body>
<form><input name="pkg" value="%s"><button>Look up</button></form>
<pre>%s</pre>
</body></html>`,
html.EscapeString(pkg),
html.EscapeString(pkg),
html.EscapeString(string(out)))
_ = strings.Contains
}
func main() {
http.HandleFunc("/", docHandler)
fmt.Println("Go doc server at http://localhost:8080/?pkg=fmt")
http.ListenAndServe(":8080", nil)
}
Evaluation Criteria¶
- Server starts and responds to requests
-
go docoutput is displayed for valid packages - Invalid packages show a helpful error
- HTML is properly escaped (no XSS)
- Form allows looking up different packages
- Server handles concurrent requests safely
Task 9: CI Format Gate (Advanced)¶
Description¶
Write a GitHub Actions workflow AND a local pre-commit hook that enforce: 1. All files are go fmt formatted 2. go vet passes 3. All exported symbols are documented 4. Generated files are up to date
Deliverables¶
File 1: .github/workflows/quality.yml
name: Code Quality
on:
push:
branches: [main]
pull_request:
jobs:
quality:
runs-on: ubuntu-latest
steps:
# TODO: Add steps for:
# 1. Checkout code
# 2. Setup Go
# 3. Check formatting (fail if unformatted)
# 4. Run go vet
# 5. Run go generate and check for diff
# 6. (optional) Run golangci-lint
File 2: scripts/pre-commit.sh
#!/bin/bash
set -e
echo "Running pre-commit checks..."
# TODO: Check 1: formatting
# Run gofmt -l and fail if output is non-empty
# TODO: Check 2: vet
# Run go vet ./...
# TODO: Check 3: documentation
# Run your doc checker from Task 3 if available
# TODO: Check 4: build
# Run go build ./... to catch compile errors
echo "All checks passed!"
File 3: Makefile
.PHONY: fmt vet generate check all
# TODO: Implement all targets:
# fmt: run go fmt
# vet: run go vet
# generate: run go generate and check diff
# check: run all checks without fixing
# all: fmt + vet + generate + test + build
Evaluation Criteria¶
- GitHub Actions workflow is valid YAML
- Workflow correctly fails on unformatted code
- Pre-commit hook is executable and works locally
- Makefile has all required targets
- Targets are documented with comments
Task 10: Package Documentation Site Generator (Advanced)¶
Description¶
Build a tool that uses go/doc and go/parser to generate a static HTML documentation site for a Go package — similar to a simplified pkg.go.dev.
Requirements¶
- Parse a Go package directory
- Extract package doc, function docs, type docs, and examples
- Generate HTML output with:
- Table of contents with anchor links
- Syntax-highlighted code snippets
- Collapsible sections
- Output to a
docs/directory
Starter Code¶
package main
import (
"fmt"
"go/ast"
"go/doc"
"go/parser"
"go/printer"
"go/token"
"html/template"
"os"
"strings"
)
// PackageData holds extracted documentation for a package
type PackageData struct {
Name string
Doc string
Funcs []FuncData
Types []TypeData
Examples []ExampleData
}
// FuncData holds documentation for a single function
type FuncData struct {
Name string
Signature string
Doc string
}
// TypeData holds documentation for a type
type TypeData struct {
Name string
Doc string
Methods []FuncData
}
// ExampleData holds an example function
type ExampleData struct {
Name string
Code string
Doc string
}
// extractPackage extracts documentation from a directory
func extractPackage(dir string) (*PackageData, error) {
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, dir, nil, parser.ParseComments)
if err != nil {
return nil, err
}
// TODO: Use go/doc to extract documentation
// Hint: doc.NewFromFiles(fset, files, importPath)
_ = doc.NewFromFiles
_ = ast.File{}
_ = printer.Fprint
return &PackageData{}, nil
}
const htmlTemplate = `<!DOCTYPE html>
<html>
<head>
<title>{{.Name}} - Go Documentation</title>
<style>
body { font-family: monospace; max-width: 900px; margin: 0 auto; padding: 20px; }
h1 { color: #007d9c; }
pre { background: #f4f4f4; padding: 10px; overflow-x: auto; }
.func-name { color: #007d9c; font-weight: bold; }
.doc { color: #555; margin: 5px 0 15px 0; }
</style>
</head>
<body>
<h1>package {{.Name}}</h1>
<p class="doc">{{.Doc}}</p>
<h2>Functions</h2>
{{range .Funcs}}
<div>
<h3 class="func-name" id="{{.Name}}">{{.Name}}</h3>
<pre>{{.Signature}}</pre>
<p class="doc">{{.Doc}}</p>
</div>
{{end}}
<h2>Types</h2>
{{range .Types}}
<div>
<h3 id="{{.Name}}">{{.Name}}</h3>
<p class="doc">{{.Doc}}</p>
{{range .Methods}}
<h4>func ({{$.Name}}) {{.Name}}</h4>
<pre>{{.Signature}}</pre>
<p class="doc">{{.Doc}}</p>
{{end}}
</div>
{{end}}
</body>
</html>`
func generateHTML(data *PackageData, outDir string) error {
tmpl, err := template.New("docs").Parse(htmlTemplate)
if err != nil {
return err
}
if err := os.MkdirAll(outDir, 0755); err != nil {
return err
}
f, err := os.Create(outDir + "/index.html")
if err != nil {
return err
}
defer f.Close()
return tmpl.Execute(f, data)
}
func main() {
dir := "."
if len(os.Args) > 1 {
dir = os.Args[1]
}
data, err := extractPackage(dir)
if err != nil {
fmt.Fprintln(os.Stderr, "Error:", err)
os.Exit(1)
}
if err := generateHTML(data, "docs"); err != nil {
fmt.Fprintln(os.Stderr, "Error:", err)
os.Exit(1)
}
fmt.Println("Documentation generated in ./docs/index.html")
_ = strings.TrimSpace
}
Evaluation Criteria¶
- Correctly extracts package, function, and type documentation
- Generated HTML renders in a browser
- All exported symbols appear in the output
- Links between types and their methods work
- The tool itself has proper documentation
Tips for All Tasks¶
- Always run
go docon packages you use — check function signatures before using - Run
go vet ./...after completing each task — fix all warnings - Write doc comments as you code — don't leave them for last
- Test your
go generatedirectives — make sure they produce the expected output - Use
go list ./...to verify your package structure is correct