golang-patterns
Idiomatic Go patterns, best practices, and conventions for building robust, efficient, and maintainable Go applications. Use when writing or reviewing Go code and idiomatic structure or conventions are in question.
DeepseekModel
官方收录技能
质量 优秀 · 90
v1.0.0
获取
https://deepseekmodel.com/api/download.php?id=affaan-m-ecc-skills-golang-patterns-skill-md&format=skill
下载 .skill
标准格式,含 system_prompt 与 model_config,导入任意 Agent 框架即可使用
.skill 文件中 system_prompt 字段的实际内容。
name golang-patterns description Idiomatic Go patterns, best practices, and conventions for building robust, efficient, and maintainable Go applications. Use when writing or reviewing Go code and idiomatic structure or conventions are in question. metadata {"origin":"ECC"} Go Development Patterns Idiomatic Go patterns and best practices for building robust, efficient, and maintainable applications. When to Activate Writing new Go code Reviewing Go code Refactoring existing Go code Designing Go packages/modules Core Principles 1. Simplicity and Clarity Go favors simplicity over cleverness. Code should be obvious and easy to read. // Good: Clear and direct func GetUser (id string ) (*User, error ) { user, err := db.FindUser(id) if err != nil { return nil , fmt.Errorf( "get user %s: %w" , id, err) } return user, nil } // Bad: Overly clever func GetUser (id string ) (*User, error ) { return func () (*User, error ) { if u, e := db.FindUser(id); e == nil { return u, nil } else { return nil , e } }() } 2. Make the Zero Value Useful Design types so their zero value is immediately usable without initialization. // Good: Zero value is useful type Counter struct { mu sync.Mutex count int // zero value is 0, ready to use } func (c *Counter) Inc() { c.mu.Lock() c.count++ c.mu.Unlock() } // Good: bytes.Buffer works with zero value var buf bytes.Buffer buf.WriteString( "hello" ) // Bad: Requires initialization type BadCounter struct { counts map [ string ] int // nil map will panic } 3. Accept Interfaces, Return Structs Functions should accept interface parameters and return concrete types. // Good: Accepts interface, returns concrete type func ProcessData (r io.Reader) (*Result, error ) { data, err := io.ReadAll(r) if err != nil { return nil , err } return &Result{Data: data}, nil } // Bad: Returns interface (hides implementation details unnecessarily) func ProcessData (r io.Reader) (io.Reader, error ) { // ... } Error Handling Patterns Error Wrapping with Context // Good: Wrap errors with context func LoadConfig (path string ) (*Config, error ) { data, err := os.ReadFile(path) if err != nil { return nil , fmt.Errorf( "load config %s: %w" , path, err) } var cfg Config if err := json.Unmarshal(data, &cfg); err != nil { return nil , fmt.Errorf( "parse config %s: %w" , path, err) } return &cfg, nil } Custom Error Types // Define domain-specific errors type ValidationError struct { Field string Message string } func (e *ValidationError) Error() string { return fmt.Sprintf( "validation failed on %s: %s" , e.Field, e.Message) } // Sentinel errors for common cases var ( ErrNotFound = errors.New( "resource not found" ) ErrUnauthorized = errors.New( "unauthorized" ) ErrInvalidInput = errors.New( "invalid input" ) ) Error Checking with errors.Is and errors.As func HandleError (err error ) { // Check for specific error if errors.Is(err, sql.ErrNoRows) { log.Println( "No records found" ) return } // Check for error type var validationErr *ValidationError if errors.As(err, &validationErr) { log.Printf( "Validation error on field %s: %s" , validationErr.Field, validationErr.Message) return } // Unknown error log.Printf( "Unexpected error: %v" , err) } Never Ignore Errors // Bad: Ignoring error with blank identifier result, _ := doSomething() // Good: Handle or explicitly document why it's safe to ignore result, err := doSomething() if err != nil { return err } // Acceptable: When error truly doesn't matter (rare) _ = writer.Close() // Best-effort cleanup, error logged elsewhere Concurrency Patterns Worker Pool func WorkerPool (jobs <- chan Job, results chan <- Result, numWorkers int ) { var wg sync.WaitGroup for i := 0 ; i < numWorkers; i++ { wg.Add( 1 ) go func () { defer wg.Done() for job := range jobs { results <- process(job) } }() } wg.Wait() close (results) } Context for Cancellation and Timeouts func FetchWithTimeout (ctx context.Context, url string ) ([] byte , error ) { ctx, cancel := context.WithTimeout(ctx, 5 *time.Second) defer cancel() req, err := http.NewRequestWithContext(ctx, "GET" , url, nil ) if err != nil { return nil , fmt.Errorf( "create request: %w" , err) } resp, err := http.DefaultClient.Do(req) if err != nil { return nil , fmt.Errorf( "fetch %s: %w" , url, err) } defer resp.Body.Close() return io.ReadAll(resp.Body) } Graceful Shutdown func GracefulShutdown (server *http.Server) { quit := make ( chan os.Signal, 1 ) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Println( "Shutting down server..." ) ctx, cancel := context.WithTimeout(context.Background(), 30 *time.Second) defer cancel() if err := server.Shutdown(ctx); err != nil { log.Fatalf( "Server forced to shutdown: %v" , err) } log.Println( "Server exited" ) } errgroup for Coordinated Goroutines import "golang.org/x/sync/errgroup" func FetchAll (ctx context.Context, urls [] string ) ([][] byte , error ) { g, ctx := errgroup.WithContext(ctx) results := make ([][] byte , len (urls)) for i, url := range urls { i, url := i, url // Capture loop variables g.Go( func () error { data, err := FetchWithTimeout(ctx, url) if err != nil { return err } results[i] = data return nil }) } if err := g.Wait(); err != nil { return nil , err } return results, nil } Avoiding Goroutine Leaks // Bad: Goroutine leak if context is cancelled func leakyFetch (ctx context.Context, url string ) <- chan [] byte { ch := make ( chan [] byte ) go func () { data, _ := fetch(url) ch <- data // Blocks forever if no receiver }() return ch } // Good: Properly handles cancellation func safeFetch (ctx context.Context, url string ) <- chan [] byte { ch := make ( chan [] byte , 1 ) // Buffered channel go func () { data, err := fetch(url) if err != nil { return } select { case ch <- data: case <-ctx.Done(): } }() return ch } Interface Design Small, Focused Interfaces // Good: Single-method interfaces 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 as needed type ReadWriteCloser interface { Reader Writer Closer } Define Interfaces Where They're Used // In the consumer package, not the provider package service // UserStore defines what this service needs type UserStore interface { GetUser(id string ) (*User, error ) SaveUser(user *User) error } type Service struct { store UserStore } // Concrete implementation can be in another package // It doesn't need to know about this interface Optional Behavior with Type Assertions type Flusher interface { Flush() error } func WriteAndFlush (w io.Writer, data [] byte ) error { if _, err := w.Write(data); err != nil { return err } // Flush if supported if f, ok := w.(Flusher); ok { return f.Flush() } return nil } Package Organization Standard Project Layout myproject/ ├── cmd/ │ └── myapp/ │ └── main.go # Entry point ├── internal/ │ ├── handler/ # HTTP handlers │ ├── service/ # Business logic │ ├── repository/ # Data access │ └── config/ # Configuration ├── pkg/ │ └── client/ # Public API client ├── api/ │ └── v1/ # API definitions (proto, OpenAPI) ├── testdata/ # Test fixtures ├── go.mod ├── go.sum └── Makefile Package Naming // Good: Short, lowercase, no underscores package http package json package user // Bad: Verbose, mixed case, or redundant package httpHandler package json_parser package userService // Redundant 'Service' suffix Avoid Package-Level State // Bad: Global mutable state var db *sql.DB func init () { db, _ = sql.Open( "postgres" , os.Getenv( "DATABASE_URL" )) } // Good: Dependency injection type Server struct { db *sql.DB } func NewServer (db *sql.DB) *Server { return &Server{db: db} } Struct Design Functional Options Pattern type Server struct { addr string timeout time.Duration logger *log.Logger } type Option func (*Server) func WithTimeout (d time.Duration) Option { return func (s *Server) { s.timeout = d } } func WithLogger (l *log.Logger) Option { return func (s *Server) { s.logger = l } } func NewServer (addr string , opts ...Option) *Server { s := &Server{ addr: addr, timeout: 30 * time.Second, // default logger: log.Default(), // default } for _, opt := range opts { opt(s) } return s } // Usage server := NewServer( ":8080" , WithTimeout( 60 *time.Second), WithLogger(customLogger), ) Embedding for Composition type Logger struct { prefix string } func (l *Logger) Log(msg string ) { fmt.Printf( "[%s] %s\n" , l.prefix, msg) } type Server struct { *Logger // Embedding - Server gets Log method addr string } func NewServer (addr string ) *Server { return &Server{ Logger: &Logger{prefix: "SERVER" }, addr: addr, } } // Usage s := NewServer( ":8080" ) s.Log( "Starting..." ) // Calls embedded Logger.Log Memory and Performance Preallocate Slices When Size is Known // Bad: Grows slice multiple times func processItems (items []Item) []Result { var results []Result for _, item := range items { results = append (results, process(item)) } return results } // Good: Single allocation func processItems (items []Item) []Result { results := make ([]Result, 0 , len (items)) for _, item := range items { results = append (results, process(item)) } return results } Use sync.Pool for Frequent Allocations var bufferPool = sync.Pool{ New: func () interface {} { return new (bytes.Buffer) }, } func ProcessRequest (data [] byte ) [] byte { buf := bufferPool.Get().(*bytes.Buffer) defer func () { buf.Reset() bufferPool.Put(buf) }() buf.Write(data) // Process... return buf.Bytes() } Avoid String Concatenation in Loops // Bad: Creates many string allocations func join (parts [] string ) string { var result string for _, p := range parts { result += p + "," } return result } // Good: Single allocation with strings.Builder func join (parts [] string ) string { var sb strings.Builder for i, p := range parts { if i > 0 { sb.WriteString( "," ) } sb.WriteString(p) } return sb.String() }
Agent 识别该技能的关键词,点击任意一个即可复制。
该技能未提供触发词。
下载的 .skill 包内含以下字段。
| 字段 | 说明 |
|---|---|
| format | 格式标识(skill/v1) |
| skill_id | 技能唯一 ID |
| name | 技能名称 |
| version | 版本号 |
| description | 技能描述 |
| category | 所属分类(数组) |
| trigger_words | 触发词列表 |
| tags | 标签列表 |
| source | 来源标识 |
| source_url | 来源链接(本页地址) |
| exported_at | 导出时间(每次下载生成) |
| system_prompt | 系统提示词正文 |
| model_config | 模型参数:provider / model / temperature / max_tokens / top_p |
| examples | 示例 |
| install_guide | 各平台导入说明(Coze / Dify / Claude / 自定义框架) |