Documents
Documentation Standards
Documentation Standards
Type
Topic
Status
Published
Created
Feb 27, 2026
Updated
Feb 28, 2026
Created by
Dosu Bot
Updated by
Dosu Bot

Documentation Standards#

The opnDossier project implements comprehensive documentation standards based on the Google Go Style Guide to ensure code clarity, maintainability, and consistency across the codebase. These standards encompass mandatory GoDoc comments for all exported packages and functions, strict naming conventions distinguishing exported from unexported identifiers, and automated enforcement through 50+ linters configured in golangci-lint.

The project treats documentation as a fundamental engineering requirement rather than an afterthought, integrating quality checks at commit-time through pre-commit hooks, during development through justfile commands, and in CI/CD pipelines where linting failures block all downstream jobs. Unlike many projects, opnDossier requires documentation not just for exported APIs but also expects clear comments for unexported functions, variable blocks, constant blocks, and type definitions, ensuring internal implementation details remain comprehensible to contributors.

These standards are enforced through a multi-layered approach combining tooling configuration (.golangci.yml, .pre-commit-config.yaml), developer workflow integration (justfile commands), and continuous integration gates, creating a comprehensive documentation culture that aligns with Go community best practices.

Core Documentation Requirements#

Package-Level Documentation#

All packages require documentation starting with "Package namename" followed by a concise description of the package's purpose. Package comments appear directly before the package declaration in the primary file.

Examples from the codebase:

// Package main is the entry point for the opnDossier CLI tool.
package main
// Package cmd provides the command-line interface for opnDossier.
package cmd
// Package audit provides security audit functionality for OPNsense configurations 
// against industry-standard compliance frameworks through a plugin-based architecture.
package audit

Multi-line package documentation is used when additional context is needed:

// Package model defines the data structures for OPNsense configurations.
//
// This package re-exports types from internal/schema for backward compatibility.
// New code should import internal/schema directly.
package model

Function Documentation#

Exported Functions#

Exported function documentation follows the pattern "FuncName does X", starting with the function name in PascalCase and using complete sentences with proper punctuation:

// GetRootCmd returns the root Cobra command for the opnDossier CLI application.
// This provides access to the application's main command and its subcommands 
// for integration or extension.
func GetRootCmd() *cobra.Command {
    return rootCmd
}
// NewMarkdownConverter creates and returns a new MarkdownConverter for converting 
// device configuration data to markdown format.
func NewMarkdownConverter() *MarkdownConverter {
    return &MarkdownConverter{}
}

Unexported Functions#

Unexported function documentation follows the same pattern but with camelCase names:

// isLightweightCommand checks if the command or any of its parents is a 
// lightweight command.
func isLightweightCommand(cmd *cobra.Command) bool {
    // Implementation...
}
// setupLightweightContext creates a minimal context for lightweight commands.
// This skips config file loading and uses minimal defaults for fast startup.
func setupLightweightContext(cmd *cobra.Command) error {
    // Implementation...
}
// handleXMLError processes XML syntax errors.
func handleXMLError(err error, dec *xml.Decoder) error {
    // Implementation...
}

Type and Struct Documentation#

All exported types require documentation explaining their purpose and usage:

// DisplayConfig holds display-related settings.
type DisplayConfig struct {
    Width int `mapstructure:"width"` // Terminal width (-1 = auto-detect)
    Pager bool `mapstructure:"pager"` // Enable pager for output
    SyntaxHighlighting bool `mapstructure:"syntax_highlighting"` // Enable syntax highlighting
}

Struct field comments are placed inline for brief explanations, or above the field for longer descriptions.

Complex types receive detailed multi-line documentation:

// CommandContext encapsulates shared state for all CLI commands.
// It is set on the cobra.Command context during PersistentPreRunE
// and provides explicit dependency injection for configuration and logging.
//
// This pattern replaces direct access to package-level globals,
// making dependencies explicit and testing easier.
type CommandContext struct {
    // Config holds the application's configuration loaded from file, 
    // environment, or flags.
    Config *config.Config

    // Logger is the application's structured logger instance.
    Logger *logging.Logger
}

Constants and Variables#

Constant and variable blocks require documentation explaining their purpose and usage context:

// Version is the current application version, injected at build time by 
// GoReleaser via ldflags.
var Version = "dev"

// Application constants.
const (
    // AppName is the application name used in CLI output and configuration.
    AppName = "opnDossier"

    // DefaultFormat is the default output format for configuration reports.
    DefaultFormat = "markdown"

    // NetworkAny represents the "any" network in firewall rules.
    NetworkAny = "any"
)

Sentinel errors are documented with their conditions:

// Error definitions for validation.
var (
    // ErrInvalidLogLevel is returned when an unrecognized log level is specified.
    ErrInvalidLogLevel = errors.New("invalid log level")

    // ErrInvalidLogFormat is returned when an unrecognized log format is specified.
    ErrInvalidLogFormat = errors.New("invalid log format")
)

Package-level variables with special purposes receive detailed explanations:

var (
    cfgFile string //nolint:gochecknoglobals // CLI config file path
    cfg *config.Config //nolint:gochecknoglobals // Application configuration (internal)
    logger *logging.Logger //nolint:gochecknoglobals // Application logger (internal)
)

// lightweightCommands lists command names that don't need full initialization.
// These commands skip config file loading and heavy logger setup for faster startup.
var lightweightCommands = map[string]bool{ //nolint:gochecknoglobals // Static command list
    "version": true,
    "help": true,
    "completion": true,
}

Go-Specific Comment Conventions#

The project follows the Google Go Style Guide for comment formatting and conventions:

Comment Formatting Rules#

  1. Start with the identifier name: Comments begin with the name of the thing being described
  2. Use complete sentences: All documentation comments are complete sentences with proper grammar and punctuation
  3. Explain "why" over "what": Comments should clarify intent and rationale rather than restating obvious code
  4. Avoid stating the obvious: Don't document trivial behavior that's clear from function signatures
  5. No emojis: Do not use emojis in code comments or documentation unless the code specifically processes emoji data

Comment Placement#

  • Package comments: Before the package declaration in the main file
  • Function comments: Immediately preceding the function declaration
  • Type comments: Immediately preceding the type declaration
  • Field comments: Inline for brief descriptions, above for detailed explanations
  • Const/var blocks: Above the block or above individual items for specific context

End-of-Line Punctuation#

The godot linter enforces proper punctuation at the end of comments.

Naming Conventions#

Identifier Capitalization#

The project follows standard Go naming conventions:

Receiver Names#

Receivers use single-letter or short names:

func (c *MarkdownConverter) ToMarkdown(ctx context.Context, data *common.CommonDevice) (string, error)
func (b *MarkdownBuilder) BuildSystemSection(data *common.CommonDevice) string

Package Names#

Packages use lowercase, single-word names when possible. Multi-word names use lowercase without underscores:

  • cmd
  • converter
  • cfgparser
  • compliance

Import Organization#

Imports are organized using goimports into three groups separated by blank lines:

  1. Standard library packages
  2. Third-party packages
  3. Local/internal packages

Example from cmd/root.go:

import (
    // Standard library
    "context"
    "fmt"
    "os"
    "strings"

    // Third-party packages
    "github.com/charmbracelet/log"
    "github.com/spf13/cobra"
    "github.com/spf13/pflag"

    // Internal packages
    "github.com/EvilBit-Labs/opnDossier/internal/config"
    "github.com/EvilBit-Labs/opnDossier/internal/constants"
    "github.com/EvilBit-Labs/opnDossier/internal/logging"
)

The gci and goimports formatters automatically enforce this structure.

Automated Enforcement#

Linting Tools#

The project uses golangci-lint with 50+ enabled linters to enforce documentation standards:

Documentation-Specific Linters#

Code Formatters#

Automatic code formatting is enforced through:

Configuration Thresholds#

Key configuration values from .golangci.yml:

  • Timeout: 30 minutes
  • Concurrency: 8 parallel jobs
  • Exit code: 1 (fail build on issues)
  • Tests included: Yes
  • Max issues per linter: 0 (unlimited reporting)
  • Cyclomatic complexity: Max 50, package average 10.0
  • Line length: 120 characters

Pre-Commit Hooks#

The .pre-commit-config.yaml enforces standards before commits:

File Format Validation#

Markdown Configuration#

.markdownlint-cli2.jsonc and .mdformat.toml configure markdown standards:

  • Auto-fix enabled
  • Dash-style lists
  • Fenced code blocks with backticks
  • No line length limits (MD013 disabled)
  • LF line endings
  • Ignores CHANGELOG.md, .golden.md test files, and THIRD_PARTY_NOTICES (auto-generated file)

CI/CD Integration#

The CI workflow (.github/workflows/ci.yml) enforces quality standards:

Lint Job (Hard Blocker)#

The lint job runs golangci-lint and all downstream jobs depend on it passing. Linting failures block the entire pipeline.

Test Coverage Job#

Tests run with coverage reporting uploaded to Codecov, but coverage failures are non-blocking (fail_ci_if_error: false).

Documentation Deployment#

The docs workflow (.github/workflows/docs.yml) builds and deploys MkDocs documentation to GitHub Pages on every push to main.

Developer Workflow Integration#

The justfile provides local commands mirroring CI checks:

Format Commands#

Linting Commands#

Documentation Commands#

Error Handling Standards#

Errors must be wrapped with context using fmt.Errorf with %w:

// Good: Wrap errors with context
file, err := os.Open(path)
if err != nil {
    return fmt.Errorf("failed to open file %s: %w", path, err)
}

Sentinel errors are defined at package level with clear documentation:

// Static errors for the plugin package.
var (
    // ErrPluginNotFound is returned when a requested plugin cannot be found 
    // in the registry.
    ErrPluginNotFound = errors.New("plugin not found")

    // ErrControlNotFound is returned when a requested control cannot be 
    // found in a plugin.
    ErrControlNotFound = errors.New("control not found")
)

Testing and Coverage Standards#

While the focus is on documentation standards, the project also maintains >80% test coverage across all packages with 100% coverage for critical paths:

Note: The 80% threshold mentioned in standards documentation refers to test coverage, not documentation coverage. No automated documentation coverage metrics are enforced.

Commit Message Standards#

All commits must follow Conventional Commits format: <type>(<scope>): <description>

Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore

Scopes: (parser), (converter), (audit), (cli), (model), (plugin), (builder), (schema)

All commits require DCO sign-off:

git commit -s -m "feat(parser): add support for new XML element"
  • Google Go Style Guide: Foundation for opnDossier's documentation standards
  • Effective Go: Commentary and examples for Go code
  • GoDoc: Documentation conventions for Go packages
  • Conventional Commits: Commit message standard used by the project
  • Testing Standards: Test coverage requirements and table-driven test patterns
  • Logging Standards: Structured logging with charmbracelet/log
  • Security Standards: Gosec integration and security-focused linting

Relevant Code Files#

FileDescriptionLines
.golangci.ymlgolangci-lint configuration with 50+ linters247
.pre-commit-config.yamlPre-commit hooks for file validation and formatting47
justfileTask runner with lint, format, and CI commands456
cmd/root.goExample of comprehensive function and variable documentation376
internal/config/config.goExample of struct and field documentation384
internal/constants/constants.goExample of const and var block documentation124
internal/compliance/errors.goExample of sentinel error documentation23
.github/workflows/ci.ymlCI pipeline enforcing linting standards169
.markdownlint-cli2.jsoncMarkdown linting configuration52
.mdformat.tomlMarkdown formatting configuration19