Skip to content
SHC Docs

Coding style

  • gofmt for all Go formatting — the canonical tool, no per-project width override (gofmt has no width setting on purpose).
  • No manual formatting — let gofmt handle it.
  • Run make format before committing; make lint rejects diffs.

Go’s own conventions are the floor. SHC project additions:

TypeConventionExample
Packageslowercase, no _backup, internalrpc
Exported typesPascalCaseBackupService, StackModel
Exported functionsPascalCaseNewService, CreateBackup
Unexported types/funcscamelCasestackRow, parseMoveEntry
VariablescamelCasebackupPath
ConstantsPascalCase / camelCase per visibilityDefaultTimeout, defaultTickInterval
Coded error factoriesX######CCCCCCX404908STKNFD
Coded errors specssame code, X######CCCCCC{Code: "X404908STKNFD", ...}
Type parameterssingle capitalT, K, V

Coded error factories use the verbatim X######CCCCCC form (6 digits + 6+ uppercase letters). No ErrXxx aliases, no dual naming. Validate at module-init() time via coded.Register, which panics on bad shape.

Every feature module under modules/{module}/ (ce module; ee-only modules under modules/) follows the same skeleton:

modules/{module}/
├── service.go # business logic — *Service struct + methods
├── router.go # HTTP routes (chi)
├── types.go # request/response shapes (Go structs + validate tags)
├── errors.go # X######CCCCCC factories + spec slice + init()
├── events.go # event handler registrations (if applicable)
├── register.go # init() side-effects (router builder, command builder, runtime singleton)
└── commands/ # cobra subcommand surface (if module exposes CLI verbs)
// 1. Package + comment
// Package backup implements the snapshot orchestrator.
package backup
// 2. Imports (stdlib first, then third-party, then internal — each
// group separated by a blank line; gofmt enforces this)
import (
"context"
"fmt"
"github.com/go-chi/chi/v5"
"gitlab.com/bitspur/selfhosted-cloud/ce/core/coded"
)
// 3. Constants (typed when not implicit)
const (
DefaultBackupTimeout = 30 * time.Minute
backupModeIncremental = "incremental"
)
// 4. Types
type Service struct{ /* ... */ }
// 5. Constructors
func NewService(opts ServiceOptions) *Service { /* ... */ }
// 6. Methods (group by receiver, public before private)
func (s *Service) CreateBackup(ctx context.Context, req BackupCreate) (*BackupModel, error) { /* ... */ }

User-visible text (errors, logs, CLI, API descriptions)

Section titled “User-visible text (errors, logs, CLI, API descriptions)”

Use the same rules as error message formatting for any string a human reads in normal operation:

  • Lowercase start — imperative or factual phrase, not Title Case or sentence case with a leading capital (except proper nouns, acronyms, and literal codes like X404908STKNFD).
  • One line where practical.
  • No colons — prefer a comma to separate clauses (same as errors).
  • Applies to: coded.New template strings, event Message, corelog/logger Debug / Info message arguments (WARN+ carries a *coded.CodedError, so its text lives in the factory template), cobra Short: / Long: help, and validator-derived OpenAPI descriptions.

Code identifiers and structured keys are unchanged; this rule is for natural-language fragments.

  • Small functions — single responsibility, easy to test.
  • Errors are values — return *coded.CodedError from internal surfaces; errors.New and fmt.Errorf are forbidden by forbidigo. Wrap external errors with coded.FromExternal(err).
  • No dead codegolangci-lint runs unused and will refuse the build. Delete unused imports, functions, comments.
  • DRY — extract common logic into reusable functions, but only after two concrete call sites demand it (§2.7 of the engineering standards).
  • No backward-compatibility code unless explicitly required.