Document everything at the smallest scope where it can be accurately documented.
- Interface-level contracts → document on the interface
- Function-level behavior → document on the function
- Package-level patterns → document in doc.go
- Cross-package concerns → document at the package that directly embeds all related packages or in project wide documentation where directory distance between packages is greater than 1.
Before adding anything to doc.go, ask: "Does this apply to the package as a whole, or to a specific type/function?" If it's specific, document it on that type/function instead.
When adding documentation, distinguish between facts and interpretations:
Safe to document:
- Structural relationships (X combines Y + Z, A calls B)
- Observable patterns visible in the codebase (versioned formats, backward compatibility)
- Explicit constraints from existing code comments or validation code
DO NOT document:
- Inferred behaviors (atomic, thread-safe, protected context) unless explicitly stated in code
- Implementation mechanisms you cannot verify (internal locking, goroutine usage)
- Safety guarantees or contracts you deduce but aren't explicitly documented
- TODO comments or "should" statements from code comments
- Discoveries of security vulnerabilities or weaknesses in security design. This is above your pay-grade -- only divulge these if you have been directly asked by the user.
When in doubt, omit the detail. Better to under-document than to document hallucinated contracts.
Package-level patterns that span multiple types/functions:
- How multiple interfaces/types relate to each other
- Package-wide patterns (e.g., callback patterns, lifecycle patterns, state machines)
- File format specifications
- Cross-cutting constraints that affect multiple functions
- Package-wide initialization requirements
These belong on the types/functions themselves:
- Specific interface method contracts
- Individual function parameters and return values
- Type-specific constraints
- Method ordering requirements for a single type
Every doc.go file follows this 3-paragraph structure:
State what the package does in one line:
// Package X manages [topic].
Define what the topic is and what it includes. Use inline definitions (parenthetical or em-dash interpolation):
// Agents are Juju processes that run on behalf of specific entities (machines,
// units, applications, models, or controllers). Agent configuration provides
// persistent identity, credentials, and connection details -- API credentials,
// controller addresses, CA certificates, directory paths, and operational
// settings like logging configuration -- that allow them to authenticate to the
// controller and perform their work.
Point readers to related packages (zoom-out) and sections below (zoom-in). Use generic references to sections:
// See github.com/juju/juju/api for establishing API connections using agent
// configuration. See github.com/juju/juju/controller for controller-specific
// settings that agents must handle. See the sections below for package-level
// concerns that span multiple interfaces.
Add sections only for package-level patterns. In doc.go files, use ASCII diagrams for state machines or workflows.
For provider packages, keep the same 3-paragraph opening, then add explicit provider sections that document package-wide behavior boundaries.
Keep provider explanations concise:
- Prefer short, factual statements over long narrative paragraphs.
- Document behavior boundaries and invariants, not step-by-step walkthroughs.
- Keep each bullet focused on one provider-wide concern.
- Omit repeated context when a section heading already scopes the topic.
- Do NOT document provider version numbers or model-config version details. Version specifics belong on the types or upgrade steps that manage them, not in the package-level doc.
Use internal/provider/oci/doc.go as a concrete pattern:
- Include registry and interface context in paragraph 3 (for example links to
internal/provider/common,internal/provider, andenvirons). - Add a section describing how the provider differs from other providers. Keep these differences as observable, provider-wide facts.
- Add focused sections for domain-wide behavior that spans multiple files, such as configuration, networking, instances/images, storage, and regions/availability zones.
- In
# Configuration, document provider-specific config as a bullet list of keys with concise descriptions (for examplecompartment-id: ...). When known, include REQUIRED/OPTIONAL, defaults, and key validation constraints. Include anAuth types supported:line where applicable (as ininternal/provider/openstack/doc.go). Followinternal/provider/oci/doc.goas the formatting pattern. - Add maintainer invariants for changes that can have broad provider impact.
Recommended heading outline:
// # How the <provider> provider differs from other providers
// # Configuration
// # Networking
// # Instances and images
// # Storage
// # Regions and Availability Zones
// # Maintainer notes
When documenting provider networking, describe provider-owned resource creation when it is package-wide behavior (for example Juju creating a VCN in OCI).
-
Maintain the red thread: Explicitly repeat the main topic (e.g., "agent configuration") rather than using pronouns like "it" or "this".
-
Document contracts, not implementation: State what callers can rely on and what constraints they must respect. Never describe internal mechanisms (locks, goroutines, data structures).
-
Use " -- " for em dashes: Not "-" or "—", but " -- " with spaces.
-
Use " -> " for right arrows: Not "→" or "⮕" or "➡" or "⇨" or "🡒" or "⟶", but " -> " with spaces.
-
Use ASCII diagrams for workflows: In doc.go files, when documenting state transitions, sequences, or data flow, add ASCII diagrams.
Example:
// New Agent First Connect After Connect // +-----------------+ +-----------------+ +-----------------+ // | old: set | | old: set | | current: NEW | // | current: empty | ----> | current: empty | ----> | old: set | // +-----------------+ | | +-----------------+
Before finalizing a doc.go file:
-
Check locality: Every sentence describes a package-level pattern, not a type-specific or function-specific detail.
-
Verify claims against code: For each factual claim (backwards compatibility, state transitions, file formats, behavior patterns):
- State transitions/behaviors: Use
grep_searchto find code comments, implementations, or tests that confirm the pattern - File formats: Read format-related files (format.go, parse.go) to confirm version support and compatibility
- Type relationships: Read the actual interface/type definitions to confirm structure
- Constraints: Search for validation code, error messages, or explicit code comments
- If unsupported: Remove the claim or add qualifying language ("typically", "generally")
- State transitions/behaviors: Use
-
Check sections: Each section describes patterns spanning multiple types/functions, not individual type behavior.
-
Consider ASCII diagrams: For state transitions, sequences, or workflows, add ASCII diagrams.