| name | go |
| description | Language-specific super-code guidelines for go. |
| risk | safe |
| source | community |
| date_added | 2026-06-16 |
Go: Idiomatic Efficiency Reference
Table of Contents
- Error Handling
- Slices & Maps
- Goroutines & Channels
- Structs & Interfaces
- Functions & Closures
- Anti-patterns specific to Go
1. Error Handling {#errors}
result, _ := os.Open(path)
result, err := os.Open(path)
if err != nil {
return fmt.Errorf("open %s: %w", path, err)
}
err := doA()
if err != nil { return err }
err = doB()
if err != nil { return err }
if err := doA(); err != nil { return err }
if err := doB(); err != nil { return err }
type MyError struct{ msg string }
func (e MyError) Error() string { return e.msg }
var 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}
var result []string
for _, item := range items {
result = append(result, item.Name)
}
result := make([]string, 0, len(items))
for _, item := range items {
result = append(result, item.Name)
}
if _, ok := m[key]; !ok {
m[key] = []string{}
}
m[key] = append(m[key], value)
m[key] = append(m[key], value)
copy := original
copy := make(map[K]V, len(original))
for k, v := range original { copy[k] = v }
3. Goroutines & Channels {#concurrency}
go doWork()
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
doWork()
}()
wg.Wait()
ch := make(chan Result)
go func() { ch <- compute() }()
result := <-ch
ch := make(chan Result, 1)
go func() { ch <- compute() }()
result := <-ch
for {
select {
case v := <-ch:
process(v)
default:
}
}
for v := range ch {
process(v)
}
Use golang.org/x/sync/errgroup for fan-out with error collection.
4. Structs & Interfaces {#structs}
type Storage interface {
Get(key string) ([]byte, error)
Set(key string, val []byte) error
Delete(key string) error
List(prefix string) ([]string, error)
}
type Getter interface { Get(key string) ([]byte, error) }
type Setter interface { Set(key string, val []byte) error }
type Storage interface { Getter; Setter }
func NewStore() *RedisStore { ... }
func NewStore() Storage { return &RedisStore{...} }
func (p *Point) X() float64 { return p.x }
func (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}
func divide(a, b float64) (result float64, err error) {
result = a / b
return
}
func divide(a, b float64) (float64, error) {
if b == 0 { return 0, errors.New("division by zero") }
return a / b, nil
}
for i := 0; i < n; i++ {
go func() { use(i) }()
}
for i := 0; i < n; i++ {
go func(i int) { use(i) }(i)
}
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.