title: Go Conventions weight: 90
Go Conventions
Language & Deps
- Modern Go — use current language features (
any, generics where they reduce noise) - Minimise external deps; stdlib first
- Allowed:
github.com/spf13/cobrafor CLI,gopkg.in/yaml.v3for config - No ORM, no logging framework, no DI container
Project Layout
- Small: root
main.gowith features split by concern (apply.go,config.go,status.go) - Med: root
main.gowithinternal/sub-packages; nopkg/ - Large/Multi: tools in
cmd/<name>/main.gowith features in split files orinternal/ - Embed static assets with
//go:embed - Never commit binaries —
go builddrops binaries in the repo root. Add it to.gitignoreat project setup time:
Use the# ignore Go binaries /mybinaryBINARYvariable from the Makefile as the canonical name so the.gitignoreentry and the build output always match.
Spec-driven Apps (Optional)
- Use this concept only on demand or when you see a need for it. Example: We are changing strings and layout a lot, and the user wants to have finegrained control.
- Add
spec/<feature>.jsonto drive compile-time features:spec/strings.jsondefines labels, titles, messagesspec/layout.jsondefines app layout, ordering, and morespec/screen-help.jsonhome screen contentspec/screen-home.jsonhelp screen content- add more as needed and create structs for parsing
CLI
- Use Cobra; one
*cobra.Commandper verb, flags defined on that command RunEnotRun— return errors, don’tos.Exitinside commandsSilenceUsage: trueon commands where error is not a usage mistake
Error Handling
- Wrap with context:
fmt.Errorf("settings: %w", err) - No
panicexcept truly unrecoverable init failures - Return errors up; print only at the top level
State Management
- No package-level mutable variables.
Pass state explicitly via function parameters or a named struct.
Package-level vars create hidden coupling and break concurrent use.
// bad var globalClient *http.Client // good type App struct { client *http.Client } init()only for truly static, side-effect-free registration (e.g.flag.Var). Never useinit()to connect to services or load files.
Types & Style
- Unexported types for internal results; exported only when crossing package boundary
- Pointer fields (
*bool,*int) for optional struct values; addboolPtr/intPtrhelpers - Section banners:
// ── Section name ──────────────────────────────────────────── - Doc comments on all exported symbols
Output Discipline
- Functions return results; callers own printing
- Print changed items with two-space indent:
fmt.Printf(" wrote %s\n", path) - Sub-details indented four spaces
Tests
- Table-driven tests with
t.Run - Test files in same package (
package main) - Helpers:
t.Helper(),t.Fatalffor setup failures,t.Errorffor assertion failures - No test frameworks — stdlib
testingonly