ワンクリックで
go-idioms
Go stdlib, error wrapping, interfaces, goroutines, table-driven tests, gofumpt.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Go stdlib, error wrapping, interfaces, goroutines, table-driven tests, gofumpt.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
Structured fault tolerance for coordinator agents. 5-level escalation ladder (Retry → Replace → Skip → Redistribute → Degrade), dead-man timers, degraded completion protocol, and cross-level escalation format. Load when orchestrating agents that may fail.
Structured code review protocol for inspecting code quality against the full rule set. Use when auditing code written by yourself or another agent, during the /audit workflow, or when the user asks for a code review.
Reusable convergence protocol for coordinator agents. Defines the BRIEFING → ITERATE → GATE → CONVERGE loop, context hygiene rules, self-succession protocol, turn budget, and handoff compression. Load when orchestrating multi-iteration workflows.
Pre-flight checklist and post-implementation self-review protocol. Use before generating any code (pre-flight) and after writing code but before verification (self-review) to catch issues early.
MECE task decomposition, file ownership enforcement, DAG-based execution, and safe merge protocol for intra-domain parallel dispatch. The safety invariants that prevent merge chaos when multiple agents write in parallel. Applies recursively at every nesting depth.
Shared protocols for all agents in the multi-agent pipeline: recursive nesting, pre-implementation restatement, parallel dispatch format, and agent definition cascade. Load this skill instead of inlining these protocols in every agent file.
| name | go-idioms |
| description | Go stdlib, error wrapping, interfaces, goroutines, table-driven tests, gofumpt. |
| paths | ["**/*.go","**/go.mod"] |
Go favors simplicity, explicitness, and readability. The language is intentionally small — resist the urge to import patterns from other languages. If it looks boring and obvious, it's probably idiomatic Go.
Scope: This file covers Go-specific coding idioms. For file layout, see
references/project-structure.md in this skill. For test naming conventions, seetesting-strategy.md. For logging library choice, see thelogging-implementationskill.
Always return errors — never panic in library or business code
panic is reserved for truly unrecoverable states (programmer errors, nil dereference)recover only at top-level goroutine boundaries (middleware, server startup)Wrap errors with context using %w
// ✅ Preserves the error chain for errors.Is / errors.As
return fmt.Errorf("creating task for user %s: %w", userID, err)
// ❌ Loses the error chain
return fmt.Errorf("creating task: %v", err)
Use sentinel errors for expected branch conditions
// Define in errors.go
var ErrNotFound = errors.New("not found")
var ErrUnauthorized = errors.New("unauthorized")
// Caller checks with errors.Is
if errors.Is(err, ErrNotFound) {
// handle
}
Use typed errors for rich domain errors
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
}
// Caller unwraps with errors.As
var ve *ValidationError
if errors.As(err, &ve) {
// access ve.Field, ve.Message
}
Handle errors at the right level — propagate upward until you have enough context to act on them; don't swallow or re-wrap the same error twice.
Keep interfaces small — one or two methods is ideal
// ✅ Focused, composable
type Reader interface { Read(p []byte) (n int, err error) }
type Writer interface { Write(p []byte) (n int, err error) }
// ❌ Monolithic
type FileManager interface {
Read(...); Write(...); Delete(...); List(...); Stat(...)
}
"Accept interfaces, return structs"
Define interfaces where they are used, not where they are implemented
// ✅ Defined in the consumer package (task feature)
// task/storage.go
type Storage interface {
GetByID(ctx context.Context, id string) (*Task, error)
}
// postgres.go implements Storage — it does NOT define it
Implicit satisfaction is a feature — don't use embedding to "implement" interfaces
implements keyword needed or wantedFor general concurrency principles (race conditions, deadlocks, message passing), see
concurrency-and-threading-principles.md. This section covers Go-specific mechanics.
Always pass context.Context as the first parameter
// ✅
func (s *Service) GetTask(ctx context.Context, id string) (*Task, error)
// ❌ — no way to cancel or propagate deadlines
func (s *Service) GetTask(id string) (*Task, error)
Never start a goroutine without knowing how it will stop
// ✅ Goroutine is bounded by context cancellation
go func() {
for {
select {
case <-ctx.Done():
return
case item := <-ch:
process(item)
}
}
}()
Use errgroup for concurrent fan-out with error collection
g, ctx := errgroup.WithContext(ctx)
g.Go(func() error { return fetchUsers(ctx) })
g.Go(func() error { return fetchOrders(ctx) })
if err := g.Wait(); err != nil { ... }
Prefer channels for ownership transfer; mutexes for shared state
Close channels from the sender, never the receiver
Receiver names: short, consistent, and the first letter of the type
func (s *Service) Create(...) {} // ✅
func (svc *Service) Create(...) {} // ❌ — too verbose
func (self *Service) Create(...) {} // ❌ — not Go
Package names: short, lowercase, no underscores, no plurals
package task // ✅
package tasks // ❌ plural
package task_service // ❌ underscore
Acronyms follow Go conventions (all caps or all lowercase)
userID // ✅
userId // ❌
HTTPClient // ✅
HttpClient // ❌
Unexported identifiers omit the type name — if it's private, keep it terse
Don't stutter — task.Task is fine; task.TaskService is not
Functional options for optional configuration
type Option func(*Service)
func WithTimeout(d time.Duration) Option {
return func(s *Service) { s.timeout = d }
}
func NewService(store Storage, opts ...Option) *Service {
s := &Service{store: store, timeout: 30 * time.Second}
for _, o := range opts { o(s) }
return s
}
defer for cleanup — always use error-checked closures
Every deferred cleanup call that returns an error MUST check and log the error.
Never use bare defer X.Close() — the discarded error hides resource leak failures.
// ❌ NEVER: Error silently discarded
defer rows.Close()
// ✅ ALWAYS: Error-checked closure with structured logging
rows, err := db.QueryContext(ctx, query)
if err != nil { return fmt.Errorf("querying tasks: %w", err) }
defer func() {
if err := rows.Close(); err != nil {
slog.Warn("failed to close rows", "error", err, "operation", "ListTasks")
}
}()
Transaction rollback:
// ❌ NEVER
defer tx.Rollback()
// ✅ ALWAYS: Guard against sql.ErrTxDone (already committed)
defer func() {
if err := tx.Rollback(); err != nil && !errors.Is(err, sql.ErrTxDone) {
slog.Error("failed to rollback transaction", "error", err, "operation", "CreateOrder")
}
}()
HTTP response body:
// ❌ NEVER
defer resp.Body.Close()
// ✅ ALWAYS: Drain then close (prevents connection reuse issues)
defer func() {
if _, err := io.Copy(io.Discard, resp.Body); err != nil {
slog.Warn("failed to drain response body", "error", err)
}
if err := resp.Body.Close(); err != nil {
slog.Warn("failed to close response body", "error", err)
}
}()
Avoid init() functions — they run implicitly and make testing harder; prefer explicit initialization in main or constructors
Prefer struct embedding over inheritance for code reuse, but only when the embedded type truly represents an "is-a" relationship
Use named return values only for documentation or defer-based cleanup — never rely on naked returns in non-trivial functions
Test file naming and pyramid proportions are defined in
testing-strategy.md. This section covers Go-specific tooling only.
Table-driven tests are the default pattern
func TestCalculateDiscount(t *testing.T) {
tests := []struct {
name string
input float64
expected float64
wantErr bool
}{
{"zero items", 0, 0, false},
{"negative input", -1, 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := calculateDiscount(tt.input)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tt.expected, got)
})
}
}
Use testify for assertions (require for fatal assertions, assert for non-fatal)
Run with the race detector in CI — go test -race ./...
Use httptest.NewRecorder() for HTTP handler tests — no live server needed
Test behaviour, not implementation — assert on outputs and side effects, not internal field values
All of the following must pass with zero warnings/errors before any commit. See code-idioms-and-conventions.md for the full checklist.
| Tool | Purpose | Command |
|---|---|---|
gofumpt / goimports | Canonical formatting | gofumpt -l -w . |
go vet | Correctness checks | go vet ./... |
staticcheck | Advanced static analysis | staticcheck ./... |
gosec | Security scanning | gosec -quiet ./... |
golangci-lint | Aggregated linter (CI) | golangci-lint run |
govulncheck | Dependency CVE scanning | govulncheck ./... |
//nolint:errcheck is NEVER acceptable. If a function returns an error, handle it — even in defer. Use an error-checked closure (see § Idiomatic Patterns above). This is the #1 source of audit findings.//nolint: directives require a // NOLINT: rationale comment AND must be approved during code reviewgo vet ./... type-checks and catches correctness issues without producing binaries (analogous to cargo check) — reserve golangci-lint for pre-commitLogging: Never use
fmt.Printlnorlog.Printfin production service code — these produce unstructured output. Uselog/slog(stdlib, Go 1.21+) or the project's chosen adapter. See@.agents/skills/logging-implementation/SKILL.mdfor the required library and patterns.