Provides best practices for Go backend development, focusing on clean code, idiomatic error handling, and efficient concurrency.
Install
mkdir -p .claude/skills/golang && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/9102" && unzip -o skill.zip -d .claude/skills/golang && rm skill.zipInstalls to .claude/skills/golang
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Use when building Go backend services, implementing goroutines/channels, handling errors idiomatically, writing tests with testify, or following Go best practices for APIs/CLI tools.Key capabilities
- →Implement idiomatic error handling
- →Manage goroutines and channels safely
- →Structure Go backend services
- →Write unit tests using testify
- →Apply Go naming and interface conventions
How it works
The skill applies community-validated patterns, such as small interface design and explicit error handling, to ensure code readability, maintainability, and concurrency safety.
Inputs & outputs
When to use golang
- →Implementing idiomatic error handling
- →Optimizing goroutines and channels
- →Structuring Go APIs and services
- →Writing unit tests using testify
About this skill
Go Development Best Practices
Version: 2.0.0 Purpose: Comprehensive Go development patterns covering idioms, error handling, concurrency, testing, and quality Scope: Backend development with Go - API services, CLI tools, system software Prerequisites: Basic Go syntax knowledge
Overview
Go (Golang) is designed for simplicity, explicit error handling, and safe concurrent programming. This skill covers production-ready patterns validated by the Go community, official documentation, and industry standards (Uber Engineering, Google).
Core Philosophy:
- Simplicity: "Clear is better than clever" - favor readable code over abstractions
- Explicit over implicit: No exceptions, no hidden control flow, visible errors
- Composition over inheritance: Interfaces and embedding, not class hierarchies
- Built-in concurrency: Goroutines and channels as first-class primitives
- Tooling-first: Format, vet, test, and benchmark built into the language
Key Design Principles:
- Small interfaces (1-3 methods ideal)
- Consumer-side interface placement
- Error values, not exceptions
- Happy path at left margin
- Goroutines must have explicit termination
1. Idiomatic Go Patterns
1.1 Naming Conventions
Package Names:
// ✅ GOOD: Package names are single lowercase identifiers
// Import path: "net/url" → package name: url
// Import path: "encoding/json" → package name: json
package url // from "net/url"
package json // from "encoding/json"
package strings
// ❌ BAD
package urls // No plural
package encodingjson // Don't smash words together
package stringutils // Too verbose
Getters and Setters:
type Account struct {
balance int
}
// ✅ GOOD: No "Get" prefix
func (a *Account) Balance() int {
return a.balance
}
func (a *Account) SetBalance(amount int) {
a.balance = amount
}
// ❌ BAD: Java-style getters
func (a *Account) GetBalance() int {
return a.balance
}
Error Variables:
// Exported sentinel errors (capitalized)
var ErrNotFound = errors.New("not found")
var ErrTimeout = errors.New("timeout")
// Unexported internal errors (lowercase)
var errInternal = errors.New("internal error")
Interface Naming:
// ✅ GOOD: Short, descriptive
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
// ❌ BAD: Verbose or unclear
type DataReader interface { ... }
type IReader interface { ... } // No "I" prefix
1.2 Interface Design - "The Bigger the Interface, the Weaker the Abstraction"
Core Principle: Small, consumer-side interfaces provide maximum flexibility.
Single-Method Interfaces (Ideal):
// Standard library examples
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
// Compose interfaces
type ReadCloser interface {
Reader
Closer
}
Consumer-Side Interface Placement:
// ❌ WRONG: Producer defines interface
package store
type CustomerStorage interface {
StoreCustomer(Customer) error
GetCustomer(string) (Customer, error)
UpdateCustomer(Customer) error
// 10+ methods...
}
type PostgresStore struct {}
func (s *PostgresStore) StoreCustomer(...) { ... }
// ✅ CORRECT: Consumer defines what it needs
package client
type customerGetter interface {
GetCustomer(string) (store.Customer, error)
}
func ProcessCustomer(cg customerGetter) {
customer, _ := cg.GetCustomer("123")
// Only depends on GetCustomer method
}
Return Concrete Types, Accept Interfaces (Postel's Law):
// ✅ GOOD
func NewStore() *PostgresStore {
return &PostgresStore{}
}
func Process(storage CustomerStorage) error {
// Accepts interface
}
// ❌ BAD: Returning interface
func NewStore() CustomerStorage {
return &PostgresStore{}
}
When to Create Interfaces:
- Multiple implementations exist or are planned
- Need for testing (mocking dependencies)
- Decoupling packages
- NOT for: Single implementation with no testing need
1.3 Happy Path Left, Early Returns
Core Principle: Align success path to left margin, handle errors first.
// ❌ BAD: Deep nesting
func join(s1, s2 string, max int) (string, error) {
if s1 == "" {
return "", errors.New("s1 is empty")
} else {
if s2 == "" {
return "", errors.New("s2 is empty")
} else {
concat, err := concatenate(s1, s2)
if err != nil {
return "", err
} else {
if len(concat) > max {
return concat[:max], nil
} else {
return concat, nil
}
}
}
}
}
// ✅ GOOD: Happy path aligned left
func join(s1, s2 string, max int) (string, error) {
if s1 == "" {
return "", errors.New("s1 is empty")
}
if s2 == "" {
return "", errors.New("s2 is empty")
}
concat, err := concatenate(s1, s2)
if err != nil {
return "", err
}
if len(concat) > max {
return concat[:max], nil
}
return concat, nil
}
Guidelines:
- Maximum 3-4 levels of nesting
- Omit
elseblocks whenifreturns - Handle errors immediately
- Keep normal flow at lowest indentation
1.4 Composition Over Inheritance
Type Embedding (Struct Composition):
// Embedding for method promotion
type Logger struct {
*log.Logger
prefix string
}
func NewLogger(prefix string) *Logger {
return &Logger{
Logger: log.New(os.Stdout, "", 0),
prefix: prefix,
}
}
// Logger methods automatically available
logger := NewLogger("APP")
logger.Println("message") // Calls embedded log.Logger.Println
Interface Composition:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
// Compose interfaces
type ReadCloser interface {
Reader
Closer
}
Warning: Avoid embedding in public APIs:
// ❌ BAD: Exposes implementation details
type MyHandler struct {
http.Handler // Leaks all Handler methods
}
// ✅ GOOD: Explicit delegation
type MyHandler struct {
handler http.Handler
}
func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Custom logic
h.handler.ServeHTTP(w, r)
}
1.5 Key Go Idioms
Defer for Cleanup:
func processFile(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // Guaranteed cleanup
// Multiple returns, all close file
if condition {
return nil // File closed
}
return process(f) // File closed
}
// Mutex pattern
func (c *Counter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++ // All paths unlock
}
Critical Rule: Call defer AFTER checking error:
// ❌ WRONG
defer f.Close() // f is nil if Open failed
f, err := os.Open(path)
// ✅ CORRECT
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
Multiple Return Values:
// (value, error) - Standard error handling
func GetUser(id string) (*User, error) {
// ...
}
// (value, bool) - "comma ok" idiom
value, ok := myMap[key]
if !ok {
// key not found
}
result, ok := someValue.(TargetType)
if !ok {
// type assertion failed
}
data, ok := <-channel
if !ok {
// channel closed
}
Blank Identifier _:
// Ignore unwanted values
_, err := os.Open(filename)
// Compile-time interface check
var _ http.Handler = (*MyHandler)(nil)
// Import for side effects
import _ "net/http/pprof"
Useful Zero Values:
// sync.Mutex - ready to use
var mu sync.Mutex
mu.Lock() // Works immediately
// bytes.Buffer - valid empty buffer
var buf bytes.Buffer
buf.WriteString("hello") // No initialization needed
// Slices - safe to read
var s []int
fmt.Println(len(s)) // 0 (safe)
2. Error Handling
2.1 Error Wrapping with %w (Go 1.13+)
Core Pattern: Wrap errors with context using fmt.Errorf and %w.
func processFile(path string) error {
file, err := os.Open(path)
if err != nil {
// Wrap with context using %w
return fmt.Errorf("failed to open file %s: %w", path, err)
}
defer file.Close()
data, err := io.ReadAll(file)
if err != nil {
return fmt.Errorf("failed to read file %s: %w", path, err)
}
return processData(data)
}
// Result when error bubbles up:
// "failed to initialize: failed to open file config.json: open config.json: no such file or directory"
Checking Wrapped Errors:
// errors.Is - Check for specific error in chain
if errors.Is(err, os.ErrNotExist) {
fmt.Println("File doesn't exist")
}
// errors.As - Extract specific error type
var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Printf("Path error on: %s\n", pathErr.Path)
}
Critical: Use %w, NOT %v:
// ❌ WRONG: Breaks error chain
return fmt.Errorf("failed: %v", err)
// ✅ CORRECT: Preserves chain
return fmt.Errorf("failed: %w", err)
2.2 Sentinel Errors vs Custom Error Types
Sentinel Errors (Package-Level Variables):
package db
var (
ErrConnectionFailed = errors.New("database connection failed")
ErrRecordNotFound = errors.New("record not found")
ErrDuplicateKey = errors.New("duplicate key violation")
)
func GetUser(id int) (*User, error) {
// ...
if notFound {
return nil, ErrRecordNotFound
}
return user, nil
}
// Caller checks with errors.Is
user, err := db.GetUser(123)
if errors.Is(err, db.ErrRecordNotFound) {
// Handle not found
}
Custom Error Types (Rich Context):
type ValidationError struct {
Field string
Value interface{}
Message string
}
fun
---
*Content truncated.*
When not to use it
- →When building high-level UI applications requiring complex class hierarchies
- →When performance requirements necessitate manual memory management
Prerequisites
Limitations
- →Requires adherence to Go-specific design philosophy
- →Does not support legacy object-oriented design patterns
How it compares
It enforces Go-specific idioms like composition over inheritance and consumer-side interface definition, contrasting with traditional object-oriented patterns.
Compared to similar skills
golang side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| golang (this skill) | 3 | 6mo | Review | Intermediate |
| rr | 0 | 5mo | No flags | Advanced |
| effective-go | 323 | 9mo | No flags | Beginner |
| architecture-patterns | 55 | 2mo | No flags | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by MadAppGang
View all by MadAppGang →You might also like
rr
west2-online
当用户调用此 skill 时,请按以下步骤执行:
effective-go
openshift
Apply Go best practices, idioms, and conventions from golang.org/doc/effective_go. Use when writing, reviewing, or refactoring Go code to ensure idiomatic, clean, and efficient implementations.
architecture-patterns
wshobson
Implement proven backend architecture patterns including Clean Architecture, Hexagonal Architecture, and Domain-Driven Design. Use when architecting complex backend systems or refactoring existing applications for better maintainability.
workflow-orchestration-patterns
wshobson
Design durable workflows with Temporal for distributed systems. Covers workflow vs activity separation, saga patterns, state management, and determinism constraints. Use when building long-running processes, distributed transactions, or microservice orchestration.
go-dev-guidelines
jumppad-labs
This skill should be used when writing, refactoring, or testing Go code. It provides idiomatic Go development patterns, TDD-based workflows, project structure conventions, and testing best practices using testify/require and mockery. Activate this skill when creating new Go features, services, packages, tests, or when setting up new Go projects.
golang-pro
sickn33
Master Go 1.21+ with modern patterns, advanced concurrency, performance optimization, and production-ready microservices. Expert in the latest Go ecosystem including generics, workspaces, and cutting-edge frameworks. Use PROACTIVELY for Go development, architecture design, or performance optimization.