Use 1-2 letter abbreviations based on the type name
Be consistent throughout a type’s methods
1
2
3
4
5
6
7
8
9
10
// ✅ Goodfunc(c*Client)CloneRepo(...)func(c*Client)UpdateRepo(...)// ✅ Good for multi-word typesfunc(rhc*RemoteHeadCache)Get(...)// ❌ Bad - inconsistentfunc(client*Client)CloneRepo(...)func(c*Client)UpdateRepo(...)
Configuration Variables
Always suffix with Cfg or Config depending on context:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ✅ Good - struct fields use abbreviated suffixtypeClientstruct{buildCfg*appcfg.BuildConfig}// ✅ Good - function parameters use abbreviated suffixfuncNewGenerator(cfg*config.Config,outputDirstring)*Generator// ✅ Good - package-level uses full name for clarityvarDefaultConfig=&config.Config{...}// ❌ Bad - no suffixtypeClientstruct{build*appcfg.BuildConfig}
// ✅ Public - descriptive, full wordsfunc(c*Client)CloneRepo(repoappcfg.Repository)errorfuncComputeRepoHash(repoPathstring,commitstring)(string,error)// ✅ Private - can use abbreviationsfunc(c*Client)getAuth(authCfg*appcfg.AuthConfig)errorfuncclassifyError(errerror)errorfunc(c*Client)fetchOrigin(repo*git.Repository)error
Getter/Setter Patterns
Go doesn’t use Get/Set prefixes for simple accessors:
// ✅ Good - consistent "classify" patternfuncclassifyFetchError(urlstring,errerror)errorfuncclassifyCloneError(urlstring,errerror)error// ✅ Good - consistent "is" pattern for boolean checksfuncisPermanentGitError(errerror)boolfuncisTransientError(errerror)bool// ❌ Bad - mixing patternsfuncclassifyFetchError(urlstring,errerror)errorfuncisPermanentGitError(errerror)boolfuncclassifyTransientType(errerror)string// Different return type
Constructor Functions
1
2
3
4
5
6
7
// ✅ Standard patternfuncNewClient(workspaceDirstring)*ClientfuncNewRemoteHeadCache(cacheDirstring)(*RemoteHeadCache,error)// ✅ Builder pattern - use "With" prefixfunc(c*Client)WithBuildConfig(cfg*appcfg.BuildConfig)*Clientfunc(c*Client)WithRemoteHeadCache(cache*RemoteHeadCache)*Client
Verb Ordering in Compound Function Names
When function names contain multiple actions, order verbs to reflect execution flow and emphasize the primary operation:
1. Validation/Check Before Action
Place validation verbs first when they guard the main operation:
1
2
3
4
5
6
7
8
// ✅ Good - validation happens firstfuncValidateAndCreate(cfg*Config)errorfuncCheckAndUpdate(repoRepository)errorfuncEnsureAndClone(dirstring)error// ❌ Bad - suggests action happens before validationfuncCreateAndValidate(cfg*Config)errorfuncUpdateAndCheck(repoRepository)error
2. Setup Before Execution
Preparation verbs come before execution verbs:
1
2
3
4
5
6
7
8
// ✅ Good - setup before main operationfuncPrepareAndExecute(cmdCommand)errorfuncInitializeAndRun(serviceService)errorfuncLoadAndProcess(filestring)error// ❌ Bad - execution before preparationfuncExecuteAndPrepare(cmdCommand)errorfuncRunAndInitialize(serviceService)error
3. Primary Action First
When combining a main operation with a side effect, lead with the primary action:
1
2
3
4
5
6
7
8
// ✅ Good - primary action leadsfuncCreateWithNotification(resourceResource)errorfuncUpdateWithLogging(entityEntity)errorfuncDeleteWithCleanup(pathstring)error// ✅ Also acceptable - "And" pattern for equal importancefuncCreateAndNotify(resourceResource)errorfuncUpdateAndLog(entityEntity)error
4. CRUD Operation Ordering
When implementing multiple CRUD operations, follow this conventional order:
1
2
3
4
5
6
7
8
9
10
11
// ✅ Good - conventional CRUD orderfuncCreate(...)funcGet(...)orRead(...)funcUpdate(...)funcDelete(...)// For bulk operationsfuncCreateBatch(...)funcGetAll(...)funcUpdateBatch(...)funcDeleteBatch(...)
5. Method Chaining Order
Builder pattern methods should follow logical construction sequence:
Cleanup operations should be explicit in compound names:
1
2
3
4
5
6
7
// ✅ Good - clear cleanup semanticsfuncCloseAndCleanup()errorfuncStopAndRemove()errorfuncCompleteAndArchive()error// ❌ Bad - ambiguous cleanup timingfuncCleanupAndClose()error// Does cleanup happen before closing?
Type Naming
Struct Types
Use clear, descriptive names without abbreviations:
typeClientstruct{// ✅ Private fields - use abbreviated suffixesworkspaceDirstringbuildCfg*appcfg.BuildConfigremoteHeadCache*RemoteHeadCache// ✅ Exported fields - full words for clarityNamestringDescriptionstringRepositoryappcfg.Repository}
// ✅ Good - descriptive prefiximport(appcfg"git.home.luguber.info/inful/docbuilder/internal/config"ggitcfg"github.com/go-git/go-git/v5/config")// ❌ Bad - unclear abbreviationimport(cfg1"git.home.luguber.info/inful/docbuilder/internal/config"cfg2"github.com/go-git/go-git/v5/config")
Error Handling
Unified Error System
DocBuilder uses internal/foundation/errors package for all error handling. This provides:
Type-safe error categories (ErrorCategory)
Structured error context
Retry semantics
HTTP and CLI adapters
Error Variables
Prefix package-level sentinel errors with Err:
1
2
3
4
5
6
7
8
9
10
11
12
// ✅ Goodvar(ErrNotFound=errors.New(errors.CategoryNotFound,"repository not found").Build()ErrUnauthorized=errors.New(errors.CategoryAuth,"authentication failed").Build()ErrInvalidConfig=errors.ValidationError("invalid configuration").Build())// ❌ Badvar(NotFoundError=errors.New("repository not found")// No categoryUnauthorized=errors.New("authentication failed")// No category)
Error Construction
Use the fluent builder API:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ✅ Good - validation error with contextreturnerrors.ValidationError("invalid forge type").WithContext("input",forgeType).WithContext("valid_values",[]string{"github","gitlab"}).Build()// ✅ Good - wrapping errorsreturnerrors.WrapError(err,errors.CategoryGit,"failed to clone repository").WithContext("url",repo.URL).WithContext("branch",repo.Branch).Build()// ❌ Bad - raw errorsreturnfmt.Errorf("failed to clone: %w",err)// No category or context
Error Messages
Start with lowercase
Be specific and actionable
Add context using WithContext(key, value) instead of string formatting
Error Categories
Available categories in internal/foundation/errors/categories.go:
// ✅ Good - extract classified errorifclassified,ok:=errors.AsClassified(err);ok{ifclassified.Category()==errors.CategoryValidation{// Handle validation error}}// ✅ Good - check category using helperiferrors.HasCategory(err,errors.CategoryAuth){// Handle auth error}// ❌ Bad - old patternvarclassified*foundation.ClassifiedErroriffoundation.AsClassified(err,&classified){// Old API - no longer exists}
Typed Errors
For package-specific errors, use the builder:
1
2
3
4
5
6
7
8
9
10
// ✅ Good - use builder for custom errorsfuncNewAuthError(op,urlstring,causeerror)error{returnerrors.WrapError(cause,errors.CategoryAuth,"authentication failed").WithContext("operation",op).WithContext("url",url).Build()}// UsagereturnNewAuthError("clone",repo.URL,err)
Comments and Documentation
Package Documentation
Every package should have a package comment:
1
2
3
4
// Package git provides a client for performing Git operations such as // clone, update, and authentication handling for DocBuilder's // documentation pipeline.packagegit
Function Documentation
Document all exported functions:
1
2
3
// CloneRepo clones a repository to the workspace directory.// If retry is enabled, it wraps the operation with retry logic.func(c*Client)CloneRepo(repoappcfg.Repository)(string,error)
Comment Style
Use complete sentences with proper punctuation
Start with the name of the thing being documented
Explain why, not just what for complex logic
1
2
3
4
5
6
7
8
// ✅ Good// ComputeRepoHash computes a deterministic hash for a repository tree.// The hash is based on the commit SHA and the tree structure of configured paths,// enabling content-addressable caching where same commit + same paths = same hash.// ❌ Bad// computes hash// This function hashes repos
TODO Comments
Include context and optionally an issue number:
1
2
// TODO(username): Add authentication support for remote.List// TODO: Implement rate limiting (see issue #123)
Code Organization
File Naming
Use lowercase with underscores for multi-word file names
Match primary type or functionality
1
2
3
4
5
6
7
8
9
// ✅ Goodremote_cache.go// Contains RemoteHeadCachetyped_errors.go// Contains error typesclient.go// Contains Client type// ❌ BadRemoteCache.go// Wrong caseremote-cache.go// Use underscore, not hyphenremotecache.go// Hard to read