Skip to main content

golang

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.

来源信息

仓库
MadAppGang/claude-code
最近来源活动
2026年1月20日 08:16
检测到的 SKILL.md 语言
英语
星标
284
分支
25

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
golang
version
2.0.0
description
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.
keywords
["Go","Golang","concurrency","goroutines","channels","error handling","testing","backend","API"]
plugin
dev
updated
2026-01-20T00:00:00.000Z
# 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**: 1. Small interfaces (1-3 methods ideal) 2. Consumer-side interface placement 3. Error values, not exceptions 4. Happy path at left margin 5. Goroutines must have explicit termination --- ## 1. Idiomatic Go Patterns ### 1.1 Naming Conventions **Package Names**: ```go // ✅ 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**: ```go 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**: ```go // 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**: ```go // ✅ 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): ```go // 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**: ```go // ❌ 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): ```go // ✅ 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. ```go // ❌ 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 `else` blocks when `if` returns - Handle errors immediately - Keep normal flow at lowest indentation --- ### 1.4 Composition Over Inheritance **Type Embedding** (Struct Composition): ```go // 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**: ```go 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: ```go // ❌ 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**: ```go 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: ```go // ❌ 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**: ```go // (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 `_`**: ```go // 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**: ```go // 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`. ```go 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**: ```go // 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`: ```go // ❌ 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): ```go 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): ```go type ValidationError struct { Field string Value interface{} Message string } func (e *ValidationError) Error() string { return fmt.Sprintf("validation failed for field '%s': %s (value: %v)", e.Field, e.Message, e.Value) } func validateAge(age int) error { if age < 0 { return &ValidationError{ Field: "age", Value: age, Message: "must be non-negative", } } return nil } // Caller extracts rich information if err := validateAge(-5); err != nil { var valErr *ValidationError if errors.As(err, &valErr) { fmt.Printf("Field: %s, Value: %v\n", valErr.Field, valErr.Value) } } ``` **Decision Guide**: - **Sentinel errors**: Simple, global error conditions - **Custom types**: Errors needing structured data or methods **Important**: Use pointer receivers for error types: ```go // ✅ CORRECT: Pointer receiver func (e *ValidationError) Error() string { ... } // ❌ WRONG: Value receiver (breaks errors.As) func (e ValidationError) Error() string { ... } ``` ---
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看