вариант

Идиоматические шаблоны, передовые практики и соглашения в языке Go для создания надёжных, эффективных и удобных в обслуживании приложений на Go.

...Расширить все
41
Обновлено время 29 июня 2026 г.

О golang-patterns

golang-patterns — это многоразовый набор навыков искусственного интеллекта, ориентированный на конкретный рабочий процесс. Название: golang-patterns

Этот навык объединяет инструкции, соглашения и рекомендации по конкретным задачам, чтобы агент мог выполнять работу более последовательно. Описание: Идиоматические шаблоны Go, лучшие практики и соглашения для создания надёжных, эффективных и удобных в обслуживании приложений на Go. Идиоматические шаблоны Go и лучшие практики для создания надёжных, эффективных и удобных в обслуживании приложений. - Написание нового кода на Go

На практике этот навык лучше всего подходит для пользователей, которым требуется повторяемое выполнение с меньшим количеством шагов настройки и меньшей неоднозначностью. - Рефакторинг существующего кода на Go - Проектирование пакетов/модулей на Go В Go простота ценится выше изобретательности. Код должен быть понятным и легко читаемым. // Хорошо: Четко и прямо

Часто задаваемые вопросы

В чём помогает « golang-patterns »?

golang-patterns Помогает агентам следовать четко сфокусированному рабочему процессу, описанному в исходной документации, что снижает неоднозначность и обеспечивает соответствие выполнения задачи ее первоначальному назначению.

Когда следует использовать этот навык?

Используйте его, когда задача соответствует рабочему процессу, предметной области или правилам работы, описанным в документации к навыку, особенно когда важна последовательность выполнения.

Каковы основные ограничения?

Возможности навыка ограничены качеством и объемом исходных инструкций. Если исходная документация неполна, оператору может по-прежнему потребоваться дополнительный контекст или ручная проверка.

Посмотреть на GitHub

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 directfunc 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 cleverfunc 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 usefultype 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 valuevar buf bytes.Bufferbuf.WriteString("hello")// Bad: Requires initializationtype 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 typefunc 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 contextfunc 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 errorstype 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 casesvar (    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 identifierresult, _ := doSomething()// Good: Handle or explicitly document why it's safe to ignoreresult, 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 cancelledfunc 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 cancellationfunc 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 interfacestype 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 neededtype ReadWriteCloser interface {    Reader    Writer    Closer}

Define Interfaces Where They're Used

// In the consumer package, not the providerpackage service// UserStore defines what this service needstype 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 underscorespackage httppackage jsonpackage user// Bad: Verbose, mixed case, or redundantpackage httpHandlerpackage json_parserpackage userService // Redundant 'Service' suffix

Avoid Package-Level State

// Bad: Global mutable statevar db *sql.DBfunc init() {    db, _ = sql.Open("postgres", os.Getenv("DATABASE_URL"))}// Good: Dependency injectiontype 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}// Usageserver := 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", 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,    }}// Usages := NewServer(":8080")s.Log("Starting...") // Calls embedded Logger.Log

Memory and Performance

Preallocate Slices When Size is Known

// Bad: Grows slice multiple timesfunc processItems(items []Item) []Result {    var results []Result    for _, item := range items {        results = append(results, process(item))    }    return results}// Good: Single allocationfunc 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 allocationsfunc join(parts []string) string {    var result string    for _, p := range parts {        result += p + ","    }    return result}// Good: Single allocation with strings.Builderfunc join(parts []string) string {    var sb strings.Builder    for i, p := range parts {        if i > 0 {            sb.WriteString(",")        }        sb.WriteString(p)    }    return sb.String()}// Best: Use standard libraryfunc join(parts []string) string {    return strings.Join(parts, ",")}

Go Tooling Integration

Essential Commands

# Build and rungo build ./...go run ./cmd/myapp# Testinggo test ./...go test -race ./...go test -cover ./...# Static analysisgo vet ./...staticcheck ./...golangci-lint run# Module managementgo mod tidygo mod verify# Formattinggofmt -w .goimports -w .

Recommended Linter Configuration (.golangci.yml)

linters:  enable:    - errcheck    - gosimple    - govet    - ineffassign    - staticcheck    - unused    - gofmt    - goimports    - misspell    - unconvert    - unparamlinters-settings:  errcheck:    check-type-assertions: true  govet:    check-shadowing: trueissues:  exclude-use-default: false

Quick Reference: Go Idioms

IdiomDescription
Accept interfaces, return structsFunctions accept interface params, return concrete types
Errors are valuesTreat errors as first-class values, not exceptions
Don't communicate by sharing memoryUse channels for coordination between goroutines
Make the zero value usefulTypes should work without explicit initialization
A little copying is better than a little dependencyAvoid unnecessary external dependencies
Clear is better than cleverPrioritize readability over cleverness
gofmt is no one's favorite but everyone's friendAlways format with gofmt/goimports
Return earlyHandle errors first, keep happy path unindented

Anti-Patterns to Avoid

// Bad: Naked returns in long functionsfunc process() (result int, err error) {    // ... 50 lines ...    return // What is being returned?}// Bad: Using panic for control flowfunc GetUser(id string) *User {    user, err := db.Find(id)    if err != nil {        panic(err) // Don't do this    }    return user}// Bad: Passing context in structtype Request struct {    ctx context.Context // Context should be first param    ID  string}// Good: Context as first parameterfunc ProcessRequest(ctx context.Context, id string) error {    // ...}// Bad: Mixing value and pointer receiverstype Counter struct{ n int }func (c Counter) Value() int { return c.n }    // Value receiverfunc (c *Counter) Increment() { c.n++ }        // Pointer receiver// Pick one style and be consistent

Remember: Go code should be boring in the best way - predictable, consistent, and easy to understand. When in doubt, keep it simple.

Все файлы

1 файлов

Установить golang-patterns

Скачайте файлы навыков и распакуйте их в каталог .claude/skills/.

Скачать ZIP

Клонируйте репозиторий и скопируйте файлы навыка в свой проект.

git clone https://github.com/affaan-m/ECC/blob/main/skills/golang-patterns/SKILL.md # Copy SKILL.md to your .claude/skills/ directory

Копировать Копировать
Быстрая настройка: Скопируйте папку со скиллом в каталог .claude/skills/ — Claude автоматически обнаружит и начнет использовать этот скилл
Репозиторий affaan-m/everything-claude-code

Похожие навыки

golang-structs-interfaces
Обновлено время 29 июня 2026 г.
OR