<< All versions
Skill v1.0.0
currentAutomated scan100/100sickn33/agentic-awesome-skills/go
──Details
PublishedOctober 14, 2026 at 05:22 PM
Content Hashsha256:f7ccb16fccc535eb...
Git SHAd38c29b8765d
──Files
Files (1 file, 5.9 KB)
SKILL.md5.9 KBactive
SKILL.md · 239 lines · 5.9 KB
version: "1.0.0" name: go description: "Language-specific super-code guidelines for go." risk: safe source: community date_added: "2026-06-16"
Go: Idiomatic Efficiency Reference
When to Use
- Use this skill when the task matches this description: Language-specific super-code guidelines for go.
Table of Contents
- Error Handling
- Slices & Maps
- Goroutines & Channels
- Structs & Interfaces
- Functions & Closures
- Anti-patterns specific to Go
1. Error Handling {#errors}
go
// ❌ Ignoring errorsresult, _ := os.Open(path)// ✅ — always handle; only use _ when error is provably irrelevantresult, err := os.Open(path)if err != nil {return fmt.Errorf("open %s: %w", path, err)}
go
// ❌ Redundant error variableerr := doA()if err != nil { return err }err = doB()if err != nil { return err }// ✅ — each :=/: is fine; this is idiomatic Go. Don't try to "fix" it.// What you CAN simplify: collapsing to one-liners where the if body is a single returnif err := doA(); err != nil { return err }if err := doB(); err != nil { return err }
go
// ❌ Custom error type with no added valuetype MyError struct{ msg string }func (e MyError) Error() string { return e.msg }// ✅ — use errors.New or fmt.Errorf unless callers need to inspect typevar ErrNotFound = errors.New("not found")return fmt.Errorf("lookup %q: %w", key, ErrNotFound)
Wrap errors with `%w` (not `%v`) so callers can use `errors.Is` / `errors.As`.
2. Slices & Maps {#slices}
go
// ❌ Growing a slice without pre-allocation when size is knownvar result []stringfor _, item := range items {result = append(result, item.Name)}// ✅result := make([]string, 0, len(items))for _, item := range items {result = append(result, item.Name)}
go
// ❌ Manual existence check before map writeif _, ok := m[key]; !ok {m[key] = []string{}}m[key] = append(m[key], value)// ✅ — append to nil slice is valid Gom[key] = append(m[key], value)
go
// ❌ Copying a map by assignment (copies reference)copy := original// ✅copy := make(map[K]V, len(original))for k, v := range original { copy[k] = v }
3. Goroutines & Channels {#concurrency}
go
// ❌ Fire-and-forget goroutine with no lifecyclego doWork()// ✅ — use errgroup or WaitGroup to track completionvar wg sync.WaitGroupwg.Add(1)go func() {defer wg.Done()doWork()}()wg.Wait()
go
// ❌ Unbuffered channel causing unnecessary goroutine blockch := make(chan Result)go func() { ch <- compute() }()result := <-ch// ✅ — for single-result, buffered channel avoids goroutine leak if receiver exits earlych := make(chan Result, 1)go func() { ch <- compute() }()result := <-ch
go
// ❌ select with a busy-wait defaultfor {select {case v := <-ch:process(v)default:// spin}}// ✅ — blocking select unless you genuinely need non-blockingfor v := range ch {process(v)}
Use `golang.org/x/sync/errgroup` for fan-out with error collection.
4. Structs & Interfaces {#structs}
go
// ❌ Large interfacetype Storage interface {Get(key string) ([]byte, error)Set(key string, val []byte) errorDelete(key string) errorList(prefix string) ([]string, error)// ... 10 more methods}// ✅ — small, composable interfacestype Getter interface { Get(key string) ([]byte, error) }type Setter interface { Set(key string, val []byte) error }type Storage interface { Getter; Setter }
go
// ❌ Returning concrete struct from constructor (ties callers to implementation)func NewStore() *RedisStore { ... }// ✅ — return interface when you have or anticipate multiple implementationsfunc NewStore() Storage { return &RedisStore{...} }
go
// ❌ Pointer receiver for tiny value typesfunc (p *Point) X() float64 { return p.x }// ✅ — value receiver for small immutable typesfunc (p Point) X() float64 { return p.x }
Rule: pointer receiver when method mutates state OR struct is large (>3 fields of non-trivial size). Value receiver otherwise.
5. Functions & Closures {#functions}
go
// ❌ Named return values used just to avoid a variable declarationfunc divide(a, b float64) (result float64, err error) {result = a / breturn}// ✅ — named returns are worth it only for deferred mutation or documentationfunc divide(a, b float64) (float64, error) {if b == 0 { return 0, errors.New("division by zero") }return a / b, nil}
go
// ❌ Closure capturing loop variable (classic Go bug, fixed in Go 1.22+)// Pre-1.22: each goroutine captures the same ifor i := 0; i < n; i++ {go func() { use(i) }()}// ✅ (Go <1.22 — pass as parameter)for i := 0; i < n; i++ {go func(i int) { use(i) }(i)}// Go 1.22+: loop variable scoped per iteration, so the original is safe
6. Anti-patterns specific to Go {#antipatterns}
| Anti-pattern | Preferred | |
|---|---|---|
if err != nil { return err } repeated 5+ times | acceptable — it's idiomatic Go | |
panic for expected errors | return err | |
init() with side effects | explicit initialization in main or constructors | |
interface{} / any without generics | use generics (Go 1.18+) or typed interfaces | |
| Mutex field not adjacent to the data it protects | put mu directly above the field it guards | |
| Channel of channels | usually a sign of over-engineering; redesign | |
time.Sleep in tests | use testing hooks or channels for synchronization | |
| Exported types with unexported fields (when fields are the whole point) | record-style structs with all-exported fields | |
log.Fatal outside main | return errors up the stack |
Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.