Code Lib
Updated a month ago

golang-agent-skill

Ssaisudhir14
0.0k
saisudhir14/golang-agent-skill
84
Agent Score

💡 Summary

This repository provides best practices for writing production-ready Go code, focusing on clarity, simplicity, and maintainability.

🎯 Target Audience

Go developers seeking to improve code qualitySoftware engineers reviewing Go codeTeams refactoring legacy Go applicationsTechnical leads enforcing coding standardsStudents learning Go programming best practices

🤖 AI Roast:Powerful, but the setup might scare off the impatient.

Security AnalysisCritical Risk

Risk: Critical. Review: shell/CLI command execution; outbound network access (SSRF, data egress); filesystem read/write scope and path traversal. Run with least privilege and audit before enabling in production.


name: golang description: Best practices for writing production Go code. Use when writing, reviewing, or refactoring Go code. Covers error handling, concurrency, naming conventions, testing patterns, performance optimization, generics, and common pitfalls. Based on Google Go Style Guide, Uber Go Style Guide, Effective Go, and Go Code Review Comments. Updated for Go 1.25.

Go Best Practices

Battle-tested patterns from Google, Uber, and the Go team. These are practices proven in large-scale production systems, updated for modern Go (1.25).

Core Principles

Readable code prioritizes these attributes in order:

  1. Clarity: purpose and rationale are obvious to the reader
  2. Simplicity: accomplishes the goal in the simplest way
  3. Concision: high signal to noise ratio
  4. Maintainability: easy to modify correctly
  5. Consistency: matches surrounding codebase

Error Handling

Return Errors, Do Not Panic

Production code must avoid panics. Return errors and let callers decide how to handle them.

// Wrong func run(args []string) { if len(args) == 0 { panic("an argument is required") } } // Correct func run(args []string) error { if len(args) == 0 { return errors.New("an argument is required") } return nil } func main() { if err := run(os.Args[1:]); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }

Error Wrapping

Use %w when callers need to inspect the underlying error with errors.Is or errors.As. Use %v when you want to hide implementation details or at system boundaries.

// Preserve error chain for programmatic inspection if err != nil { return fmt.Errorf("load config: %w", err) } // Hide internal details at API boundaries if err != nil { return fmt.Errorf("database unavailable: %v", err) }

Keep context succinct. Avoid phrases like "failed to" that pile up as errors propagate.

// Wrong: produces "failed to x: failed to y: failed to create store: the error" return fmt.Errorf("failed to create new store: %w", err) // Correct: produces "x: y: new store: the error" return fmt.Errorf("new store: %w", err)

Joining Multiple Errors (Go 1.20+)

Use errors.Join when multiple operations can fail independently.

func validateUser(u User) error { var errs []error if u.Name == "" { errs = append(errs, errors.New("name required")) } if u.Email == "" { errs = append(errs, errors.New("email required")) } return errors.Join(errs...) } // Checking joined errors if err := validateUser(u); err != nil { if errors.Is(err, ErrNameRequired) { // handles even when joined with other errors } }

Error Types

Choose based on caller needs:

| Caller needs to match? | Message type | Approach | |------------------------|--------------|----------| | No | Static | errors.New("something bad") | | No | Dynamic | fmt.Errorf("file %q not found", file) | | Yes | Static | Exported var ErrNotFound = errors.New("not found") | | Yes | Dynamic | Custom error type with Error() method |

Sentinel Errors and errors.Is

Define sentinel errors for conditions callers need to check.

var ( ErrNotFound = errors.New("not found") ErrInvalidUser = errors.New("invalid user") ) // Checking wrapped errors if errors.Is(err, ErrNotFound) { // handles ErrNotFound even when wrapped } // Custom error types use errors.As var pathErr *os.PathError if errors.As(err, &pathErr) { fmt.Println("failed path:", pathErr.Path) }

Error Naming

Exported error variables use Err prefix. Custom error types use Error suffix.

var ( ErrNotFound = errors.New("not found") ErrInvalidUser = errors.New("invalid user") ) type NotFoundError struct { Resource string } func (e *NotFoundError) Error() string { return fmt.Sprintf("%s not found", e.Resource) }

Handle Errors Once

Do not log an error and also return it. The caller will likely log it again.

// Wrong: logs and returns, causing duplicate logs if err != nil { log.Printf("could not get user %q: %v", id, err) return err } // Correct: wrap and return, let caller decide if err != nil { return fmt.Errorf("get user %q: %w", id, err) } // Also correct: log and degrade gracefully without returning error if err := emitMetrics(); err != nil { log.Printf("could not emit metrics: %v", err) }

Error Strings

Do not capitalize error strings or end with punctuation. They often appear mid-sentence in logs.

// Wrong fmt.Errorf("Something bad happened.") // Correct fmt.Errorf("something bad happened")

Indent Error Flow

Keep the happy path at minimal indentation. Handle errors first.

// Wrong if err != nil { // error handling } else { // normal code } // Correct if err != nil { return err } // normal code continues

Concurrency

Channel Size

Channels should have size zero (unbuffered) or one. Any other size requires justification about what prevents filling under load.

// Wrong: arbitrary buffer c := make(chan int, 64) // Correct c := make(chan int) // unbuffered: synchronous handoff c := make(chan int, 1) // buffered: allows one pending send

Goroutine Lifetimes

Document when and how goroutines exit. Goroutines blocked on channels will not be garbage collected even if the channel is unreachable.

// Document exit conditions func (w *Worker) Run(ctx context.Context) error { for { select { case <-ctx.Done(): return ctx.Err() case job := <-w.jobs: w.process(job) } } }

Use errgroup for Concurrent Operations

Prefer errgroup.Group over manual sync.WaitGroup for error-returning goroutines.

import "golang.org/x/sync/errgroup" func processItems(ctx context.Context, items []Item) error { g, ctx := errgroup.WithContext(ctx) for _, item := range items { g.Go(func() error { return process(ctx, item) }) } return g.Wait() // returns first error, cancels others via ctx } // With concurrency limit func processItemsLimited(ctx context.Context, items []Item) error { g, ctx := errgroup.WithContext(ctx) g.SetLimit(10) // max 10 concurrent goroutines for _, item := range items { g.Go(func() error { return process(ctx, item) }) } return g.Wait() }

Prefer Synchronous Functions

Synchronous functions are easier to reason about and test. Let callers add concurrency when needed.

// Wrong: forces concurrency on caller func Fetch(url string) <-chan Result // Correct: caller can wrap in goroutine if needed func Fetch(url string) (Result, error)

Zero Value Mutexes

The zero value of sync.Mutex is valid. Do not use pointers to mutexes or embed them in exported structs.

// Wrong mu := new(sync.Mutex) // Wrong: exposes Lock/Unlock in API type SMap struct { sync.Mutex data map[string]string } // Correct type SMap struct { mu sync.Mutex data map[string]string }

Atomic Operations (Go 1.19+)

Use the standard library's typed atomics. External packages are no longer necessary.

import "sync/atomic" type Counter struct { value atomic.Int64 } func (c *Counter) Inc() { c.value.Add(1) } func (c *Counter) Value() int64 { return c.value.Load() } // Also available: atomic.Bool, atomic.Pointer[T], atomic.Uint32, etc.

sync.Map Performance (Go 1.24+)

The sync.Map implementation was significantly improved in Go 1.24. Modifications of disjoint sets of keys are much less likely to contend on larger maps, and there is no longer any ramp-up time required to achieve low-contention loads.


Naming

MixedCaps Always

Go uses MixedCaps, never underscores. This applies even when it breaks other language conventions.

// Wrong MAX_LENGTH, max_length, HTTP_Server // Correct MaxLength, maxLength, HTTPServer

Initialisms

Initialisms maintain consistent case: URL not Url, ID not Id, HTTP not Http.

// Wrong xmlHttpRequest, serverId, apiUrl // Correct xmlHTTPRequest, serverID, apiURL

Short Variable Names

Variables should be short, especially with limited scope. The further from declaration a name is used, the more descriptive it needs to be.

// Good for local scope for i, v := range items { } r := bufio.NewReader(f) // Global or struct fields need more context var DefaultTimeout = 30 * time.Second

Receiver Names

Use one or two letter abbreviations of the type. Be consistent across methods. Do not use generic names like this, self, or me.

// Wrong func (this *Client) Get() {} func (c *Client) Get() {} func (cl *Client) Post() {} // inconsistent // Correct func (c *Client) Get() {} func (c *Client) Post() {}

Pointer vs Value Receivers

| Use pointer receiver when | Use value receiver when | |---------------------------|-------------------------| | Method modifies the receiver | Struct is small and immutable | | Struct is large (avoid copying) | Method doesn't modify state | | Consistency with other methods | Receiver is a map, func, or chan | | Struct contains sync.Mutex | Basic types (int, string, etc.) |

// Pointer: modifies state func (s *Server) Shutdown() error { s.running = false return s.listener.Close() } // Value: small, read-only func (p Point) Distance(q Point) float64 { return math.Hypot(p.X-q.X, p.Y-q.Y) }

Package Names

Package names are lowercase, single words. Avoid util, common, misc, api, types. The package name becomes part of the identifier at call sites.

// Wrong package chubby type ChubbyFile struct{} // chubby.ChubbyFile is redundant // Correct package chubby type File struct{} // chubby.File reads well

Avoid Repetition in Names

Do not repea

5-Dim Analysis
Clarity9/10
Novelty7/10
Utility9/10
Completeness8/10
Maintainability9/10
Pros & Cons

Pros

  • Comprehensive coverage of Go best practices
  • Focus on error handling and concurrency
  • Guidelines based on industry standards

Cons

  • May be overwhelming for beginners
  • Lacks examples for every guideline
  • Not all practices may fit every project

Related Skills

pytorch

S
toolCode Lib
92/ 100

“It's the Swiss Army knife of deep learning, but good luck figuring out which of the 47 installation methods is the one that won't break your system.”

agno

S
toolCode Lib
90/ 100

“It promises to be the Kubernetes for agents, but let's see if developers have the patience to learn yet another orchestration layer.”

nuxt-skills

S
toolCo-Pilot
90/ 100

“It's essentially a well-organized cheat sheet that turns your AI assistant into a Nuxt framework parrot.”

Disclaimer: This content is sourced from GitHub open source projects for display and rating purposes only.

Copyright belongs to the original author saisudhir14.