go-guidelines

📁 peixotorms/odinlayer-skills 📅 4 days ago
1
总安装量
1
周安装量
#49916
全站排名
安装命令
npx skills add https://github.com/peixotorms/odinlayer-skills --skill go-guidelines

Agent 安装分布

amp 1
opencode 1
kimi-cli 1
codex 1
github-copilot 1
claude-code 1

Skill 文档

Go Guidelines

Overview

Idiomatic Go guidelines based on Effective Go and Google Go Style Guide. Go has its own conventions — writing good Go means embracing them, not porting patterns from other languages.

Core Proverbs

  • Clear is better than clever — optimize for readability, not cleverness
  • A little copying is better than a little dependency — duplicate small functions rather than import heavy packages
  • The zero value should be useful — design types so var x T works without initialization
  • Don’t communicate by sharing memory; share memory by communicating — use channels
  • Errors are values — program with them, don’t just check them
  • Don’t just check errors, handle them gracefully — add context, decide action
  • Reflection is never clear — avoid reflect unless building frameworks

Formatting

Rule Detail
Use gofmt No exceptions — all Go code is gofmt’d
Indentation Tabs, not spaces
Line length No hard limit; break long lines naturally
Braces Opening brace on same line (mandatory — semicolon insertion)

Import Grouping

import (
    // 1. Standard library
    "context"
    "fmt"
    "net/http"

    // 2. Third-party packages
    "github.com/gorilla/mux"
    "go.uber.org/zap"

    // 3. Internal packages
    "myproject/internal/auth"
    "myproject/user"
)

Naming Conventions

What Convention Example
Packages Lowercase, single-word, no underscores bufio, strconv
Exported names MixedCaps (upper first letter) ReadWriter
Unexported mixedCaps (lower first letter) readBuf
Getters Owner() not GetOwner() func (o *Obj) Name() string
Setters SetOwner() func (o *Obj) SetName(n string)
One-method interfaces Method name + -er suffix Reader, Writer, Stringer
Acronyms All caps throughout URL, HTTP, ID (not Url, Http, Id)
Constants MixedCaps (not ALL_CAPS) MaxRetries, not MAX_RETRIES
Receivers 1-2 letters, consistent func (s *Server), not func (server *Server)
Local variables Length proportional to scope size 1-7 lines: i, buf; larger scopes: descriptive

Doc Comments

// Package sort provides primitives for sorting slices.
package sort

// Fprint formats using the default formats and writes to w.
// It returns the number of bytes written and any write error.
func Fprint(w io.Writer, a ...any) (n int, err error) {
Doc comment rule Detail
Start with the name of the thing // Fprint formats... not // This function formats...
Full sentences Capitalized, ending with period
Package comment in any file One // Package name ... per package

Naming Pitfalls

// BAD: Java/C# naming
func GetUserName() string { ... }
type IReader interface { ... }

// GOOD: Go naming
func UserName() string { ... }
type Reader interface { ... }

// BAD: stuttering in constructors
widget.NewWidget()
user.NewUser()

// GOOD: package name provides context
widget.New()
user.New()

// BAD: ALL_CAPS constants
const MAX_RETRIES = 3

// GOOD: MixedCaps — name by role, not value
const MaxRetries = 3

Control Structures

Rule Detail
Use if init statements if err := f(); err != nil { } — keeps scope tight
Early return Avoid else after return
for has three forms C-style, while, infinite
range is idiomatic Prefer for k, v := range over index-based loops
Range on string Iterates runes (UTF-8), not bytes
Switch has no fall-through Unlike C; use comma-separated cases for multiple matches
Switch on true Replaces if-else chains cleanly
Type switch switch v := value.(type) for interface type dispatch

Full code examples: resources/types-functions.md

Functions

Rule Detail
Return (value, error) The Go pattern for fallible operations
Named return values Document meaning in godoc; naked returns OK in short functions only
defer runs on any return path Including panics; arguments evaluated immediately
defer is LIFO Last deferred runs first
Use defer for cleanup Files, locks, connections

Full code examples: resources/types-functions.md

Data Types

new vs make

Function Returns Use for Initializes
new(T) *T Any type Zeroed memory
make(T, ...) T Slices, maps, channels only Internal data structures

Slices

Slice rule Detail
Always use return value of append Underlying array may move
Pre-allocate with make([]T, 0, cap) When size is known or estimable
len(s) vs cap(s) Length is current size, capacity is maximum before reallocation
Nil slice is valid len(nil) == 0, append(nil, x) works
Prefer nil for empty slices var s []T not s := []T{} — nil encodes to null in JSON, empty literal to []

Maps

Map rule Detail
Zero value for missing key m["absent"] returns zero, not error
Always use comma-ok for presence check v, ok := m[key]
Not safe for concurrent access Use sync.Map or mutex
Iteration order is random Don’t rely on order

Full code examples: resources/types-functions.md

Design Principles

Zero Values Should Be Useful

Design types so var x T works without initialization. Example: var buf bytes.Buffer is ready to use with no New() call. Nil fields should fall back to sensible defaults.

Don’t Copy Types with Locks

Copying a sync.Mutex copies its state — undefined behavior. Always use pointer receivers on types containing locks.

A Little Copying > A Little Dependency

Duplicate small utility functions rather than importing a heavy package for one helper.

Security

Rule Detail
crypto/rand not math/rand For keys, tokens, secrets — math/rand is predictable
Build tags for syscall //go:build linux when using platform-specific syscalls
Build tags for cgo //go:build cgo — cgo sacrifices memory safety and cross-compilation
Avoid unsafe package Voids all type safety and compatibility guarantees
Avoid reflect Obscures intent, removes compile-time safety

Methods

Pointer vs Value Receivers

Receiver Can modify? Called on Use when
func (t T) Method() No (copy) Values and pointers Read-only, small types
func (t *T) Method() Yes Pointers only* Mutation, large structs

* Go auto-takes address for addressable values.

  • Don’t pass pointers to small basic types — pass values
  • Consistency: if any method needs pointer receiver, all should use pointer

Full code examples: resources/patterns.md

Interfaces

Design Principles

Interface rule Detail
Keep interfaces small 1-2 methods ideal
Accept interfaces, return concrete types Maximum flexibility for callers
Don’t export implementation types when interface suffices Return interface from constructors
Interfaces are satisfied implicitly No implements keyword
Name one-method interfaces with -er Reader, Writer, Closer, Stringer
Define in consuming package Not in implementing package
Don’t define before use Wait until you have realistic usage
Don’t export unused interfaces If only used internally, keep unexported
Prefer synchronous functions Let callers add concurrency — easier to test and reason about

Type Assertions

// Safe form — comma-ok
str, ok := value.(string)
if !ok {
    // value is not a string
}

// Type switch — multiple type checks
switch v := value.(type) {
case string:
    return v
case fmt.Stringer:
    return v.String()
default:
    return fmt.Sprintf("%v", value)
}

Embedding

// Interface embedding — compose interfaces
type ReadWriter interface {
    Reader
    Writer
}

// Struct embedding — promote methods
type Job struct {
    Command string
    *log.Logger  // promoted: job.Println() works
}

Initialization

Init rule Detail
init() called after all variable declarations Automatic
All imports initialized first Dependency order
Multiple init() per file allowed Run in order
Use for verification Not complex logic

Full code examples: resources/patterns.md

Functional Options Pattern

Use for 3+ optional configuration fields, library APIs, or when defaults should work out of the box. Define type Option func(*T), create WithX constructors, and apply in NewT(opts ...Option).

When to use When NOT to use
3+ optional configuration fields 1-2 required parameters — just use arguments
Library APIs (callers shouldn’t know internals) Internal structs with few fields — use literal
Defaults should work out of the box Config loaded from file — use a config struct

Full code examples: resources/patterns.md

Constructor & Validation

Pattern When
NewX() *X Simple construction, always succeeds
NewX() (*X, error) Validation needed, may fail
MustX() *X Panics on error — only for compile-time constants or tests

Full code examples: resources/patterns.md

Generics (Go 1.18+)

Use generics for Don’t use generics for
Type-safe collections/containers Simple functions that work with interface{}
Algorithm reuse across types When only one type is ever used
Reducing code duplication When it makes code harder to read
Options/builder patterns Premature abstraction

Full code examples: resources/patterns.md

Package Organization

// Domain-based (preferred for applications)
myapp/
├── user/           # User domain
│   ├── user.go
│   ├── store.go
│   └── handler.go
├── order/          # Order domain
│   ├── order.go
│   └── service.go
├── internal/       # Private packages
│   └── db/
└── cmd/
    └── server/main.go

// Flat (for libraries and small services)
mylib/
├── mylib.go        # Primary types and functions
├── option.go       # Options pattern
├── mylib_test.go
└── internal/       # Implementation details
Rule Detail
cmd/ Entry points — main packages
internal/ Private — compiler-enforced, cannot be imported outside module
pkg/ Optional — public library code (some projects skip this)
Avoid models/, utils/, helpers/ Too generic — organize by domain
One package = one purpose If you can’t name it in one word, split it

Testing

Testing rule Detail
_test.go suffix Test files, excluded from production builds
Test prefix Functions must start with TestXxx(t *testing.T)
t.Run for subtests Enables selective running: go test -run TestAdd/positive
t.Helper() Marks helper functions for better error locations
t.Cleanup() Deferred cleanup that runs after test completes
t.Parallel() Opt-in parallel execution within a test
testdata/ directory Test fixtures, ignored by Go tooling
Got before want t.Errorf("Func(%v) = %v, want %v", input, got, want)
t.Error over t.Fatal Keep going — report all failures in one run
t.Fatal only for setup When test literally cannot proceed
No t.Fatal from goroutines Only call from test function’s goroutine

Full code examples: resources/testing-verification.md

Static Verification

Tool Purpose
go vet ./... Built-in static analysis
golangci-lint run ./... Comprehensive linter aggregator
go test -race ./... Detect data races at runtime
go test -count=1 ./... Disable test caching
go test -cover ./... Coverage report

Full config and examples: resources/testing-verification.md


Anti-Pattern Quick Reference

Anti-Pattern Better Alternative
GetName() getter Name()
Stuttering: user.UserName user.Name
interface{} everywhere Specific types or generics
Large interfaces (10+ methods) Small, composed interfaces
Returning error and ignoring it Always handle or explicitly _ =
else after return Early return pattern
Naked goroutines (fire-and-forget) Track with sync.WaitGroup or errgroup
init() with complex logic Explicit initialization in main()
Mutable package-level vars Pass config explicitly
Value receiver on large struct Pointer receiver
Checking == nil on interface Check concrete value (interfaces have type+value)
panic for expected errors Return error
Underscore imports without comment Document why: import _ "pkg" // register driver
new(T) when make is needed make for slices/maps/channels
Ignoring append return value Always s = append(s, ...)
Config struct with 10+ fields Functional options pattern
No validation in constructor NewX() (*X, error) with validate()
utils/ or helpers/ package Organize by domain, not by kind
models/ package for all types Put types where they’re used
Not using t.Helper() Test helpers report wrong line numbers
Not using t.Cleanup() Cleanup may not run on t.Fatal()
go test without -race Data races go undetected
Copying struct with sync.Mutex Use pointer receiver, never copy
math/rand for secrets crypto/rand for tokens, keys, secrets
In-band errors (-1, empty string) Return (value, error) or (value, bool)
Defining interface in implementing package Define where consumed
ALL_CAPS constants MixedCaps — Go convention
import . "pkg" (dot import) Makes code origin unclear
Context in struct field Always pass context.Context as first parameter
Clever one-liners Clear, readable multi-line code
reflect for simple tasks Generics or type assertions

Build & Deploy

Always Use Makefile

Before running go build or any build command, check if a Makefile exists. If it does, use it — the Makefile encodes project-specific build steps (ldflags, embedding, code generation) that raw commands miss.

Situation Action
Makefile exists with relevant target make deploy, make build, make test, etc.
Makefile exists, no matching target List targets, pick closest match
No Makefile Fall back to go build, go test, etc.

Permissions & Ownership

Before building or deploying, check file permissions and ownership. Fix to match the project majority:

# Identify majority owner:group
stat -c '%U:%G' * | sort | uniq -c | sort -rn | head -5
# Fix if needed
chown -R <user>:<group> .

Temporary Files

File type Location
Build intermediates, scratch files /tmp — never the project directory
Final build artifacts Project output dir or $GOBIN
Downloaded dependencies Standard location (~/go/pkg/)

Exception: If project instructions (CLAUDE.md, Makefile) specify a different temp location, follow those.

Test File Placement

Test type Location
Temporary (debugging, one-off) /tmp
Permanent, Go convention *_test.go beside the source file
Permanent, integration/e2e tests/ directory (create if it doesn’t exist)
Specific instructions exist Follow those

Resources

Detailed code examples are organized into resource files for progressive disclosure:

  • Types, Functions & Control Flow — Control structure patterns (if/for/switch), function signatures, defer, new vs make, composite literals, slices, maps, and printing
  • Patterns — Methods (pointer vs value receivers), initialization (iota, init), functional options, constructor validation, and generics
  • Testing & Verification — Table-driven tests, parallel tests, test helpers, testify, golangci-lint config, go vet, race detector, and coverage