Communitygithub.com

golang-patterns

Modèles Go idiomatiques, bonnes pratiques et conventions pour construire des applications Go robustes, efficaces et maintenables. À utiliser lors de l'écriture ou de la révision de code Go et de structures ou conventions idiomatiques.

Qu'est-ce que golang-patterns ?

Cette compétence fournit un guide complet des modèles de développement Go idiomatiques, des bonnes pratiques et des conventions essentielles pour créer des applications Go robustes. Elle explique comment activer des principes fondamentaux comme la simplicité, la clarté, l'utilisation de la valeur zéro, l'acceptation d'interfaces et le retour de structures. Des modèles de gestion d'erreurs (emballage, types personnalisés, utilisation d'errors.Is/As), de concurrence (worker pools, contextes pour annulation et délais d'attente, arrêt gracieux, errgroup), de conception d'interfaces (petites et ciblées, définies là où elles sont utilisées, comportement optionnel avec assertions de type) et d'organisation de packages sont détaillés. La compétence aborde également la conception de structures (modèle d'options fonctionnelles, composition par embedding), l'optimisation mémoire et performance (préallocation de slices, sync.Pool, strings.Builder), l'intégration d'outils Go (commandes essentielles, configuration de linter) et des anti-patrons à éviter. Elle est utile aux développeurs et réviseurs de code Go qui souhaitent produire un code idiomatique, maintenable et performant.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/golang-patterns

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Modèles de développement Go

Modèles Go idiomatiques et bonnes pratiques pour construire des applications robustes, efficaces et maintenables.

Quand activer

  • Écrire du nouveau code Go
  • Réviser du code Go
  • Refactoriser du code Go existant
  • Concevoir des packages/modules Go

Principes fondamentaux

1. Simplicité et clarté

Go privilégie la simplicité plutôt que l'ingéniosité. Le code doit être évident et facile à lire.

// Bien : Clair et 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
}

// Mal : Trop astucieux
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. Rendre la valeur zéro utile

Concevez les types pour que leur valeur zéro soit immédiatement utilisable sans initialisation.

// Bien : La valeur zéro est utile
type Counter struct {
    mu    sync.Mutex
    count int // la valeur zéro est 0, prête à l'emploi
}

func (c *Counter) Inc() {
    c.mu.Lock()
    c.count++
    c.mu.Unlock()
}

// Bien : bytes.Buffer fonctionne avec la valeur zéro
var buf bytes.Buffer
buf.WriteString("hello")

// Mal : Nécessite une initialisation
type BadCounter struct {
    counts map[string]int // une map nil va paniquer
}

3. Accepter des interfaces, retourner des structures

Les fonctions doivent accepter des paramètres d'interface et retourner des types concrets.

// Bien : Accepte une interface, retourne un type concret
func ProcessData(r io.Reader) (*Result, error) {
    data, err := io.ReadAll(r)
    if err != nil {
        return nil, err
    }
    return &Result{Data: data}, nil
}

// Mal : Retourne une interface (cache inutilement les détails d'implémentation)
func ProcessData(r io.Reader) (io.Reader, error) {
    // ...
}

Modèles de gestion d'erreurs

Emballage des erreurs avec contexte

// Bien : Emballer les erreurs avec contexte
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
}

Types d'erreur personnalisés

// Définir des erreurs spécifiques au domaine
type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
}

// Erreurs sentinelles pour les cas courants
var (
    ErrNotFound     = errors.New("resource not found")
    ErrUnauthorized = errors.New("unauthorized")
    ErrInvalidInput = errors.New("invalid input")
)

Vérification des erreurs avec errors.Is et errors.As

func HandleError(err error) {
    // Vérifier une erreur spécifique
    if errors.Is(err, sql.ErrNoRows) {
        log.Println("No records found")
        return
    }

    // Vérifier un type d'erreur
    var validationErr *ValidationError
    if errors.As(err, &validationErr) {
        log.Printf("Validation error on field %s: %s",
            validationErr.Field, validationErr.Message)
        return
    }

    // Erreur inconnue
    log.Printf("Unexpected error: %v", err)
}

Ne jamais ignorer les erreurs

// Mal : Ignorer l'erreur avec l'identifiant vide
result, _ := doSomething()

// Bien : Gérer ou documenter explicitement pourquoi il est sûr d'ignorer
result, err := doSomething()
if err != nil {
    return err
}

// Acceptable : Quand l'erreur n'a vraiment pas d'importance (rare)
_ = writer.Close() // Nettoyage au mieux, erreur journalisée ailleurs

Modèles de concurrence

Pool de workers

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)
}

Contexte pour annulation et délais d'attente

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)
}

Arrêt gracieux

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 pour goroutines coordonnées

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 // Capturer les variables de boucle
        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
}

Éviter les fuites de goroutines

// Mal : Fuite de goroutine si le contexte est annulé
func leakyFetch(ctx context.Context, url string) <-chan []byte {
    ch := make(chan []byte)
    go func() {
        data, _ := fetch(url)
        ch <- data // Bloque pour toujours s'il n'y a pas de récepteur
    }()
    return ch
}

// Bien : Gère correctement l'annulation
func safeFetch(ctx context.Context, url string) <-chan []byte {
    ch := make(chan []byte, 1) // Canal tamponné
    go func() {
        data, err := fetch(url)
        if err != nil {
            return
        }
        select {
        case ch <- data:
        case <-ctx.Done():
        }
    }()
    return ch
}

Conception d'interfaces

Interfaces petites et ciblées

// Bien : Interfaces à méthode unique
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
}

// Composer les interfaces selon les besoins
type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

Définir les interfaces là où elles sont utilisées

// Dans le package consommateur, pas le fournisseur
package service

// UserStore définit ce dont ce service a besoin
type UserStore interface {
    GetUser(id string) (*User, error)
    SaveUser(user *User) error
}

type Service struct {
    store UserStore
}

// L'implémentation concrète peut être dans un autre package
// Elle n'a pas besoin de connaître cette interface

Comportement optionnel avec des assertions de type

type Flusher interface {
    Flush() error
}

func WriteAndFlush(w io.Writer, data []byte) error {
    if _, err := w.Write(data); err != nil {
        return err
    }

    // Vider si supporté
    if f, ok := w.(Flusher); ok {
        return f.Flush()
    }
    return nil
}

Organisation des packages

Structure de projet standard

myproject/
├── cmd/
│   └── myapp/
│       └── main.go           # Point d'entrée
├── internal/
│   ├── handler/              # Gestionnaires HTTP
│   ├── service/              # Logique métier
│   ├── repository/           # Accès aux données
│   └── config/               # Configuration
├── pkg/
│   └── client/               # Client d'API public
├── api/
│   └── v1/                   # Définitions d'API (proto, OpenAPI)
├── testdata/                 # Données de test
├── go.mod
├── go.sum
└── Makefile

Nommage des packages

// Bien : Court, minuscules, sans underscores
package http
package json
package user

// Mal : Verbeux, casse mélangée ou redondant
package httpHandler
package json_parser
package userService // Suffixe 'Service' redondant

Éviter l'état au niveau du package

// Mal : État mutable global
var db *sql.DB

func init() {
    db, _ = sql.Open("postgres", os.Getenv("DATABASE_URL"))
}

// Bien : Injection de dépendances
type Server struct {
    db *sql.DB
}

func NewServer(db *sql.DB) *Server {
    return &Server{db: db}
}

Conception des structures

Modèle d'options fonctionnelles

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, // valeur par défaut
        logger:  log.Default(),    // valeur par défaut
    }
    for _, opt := range opts {
        opt(s)
    }
    return s
}

// Utilisation
server := NewServer(":8080",
    WithTimeout(60*time.Second),
    WithLogger(customLogger),
)

Embedding pour la 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 obtient la méthode Log
    addr    string
}

func NewServer(addr string) *Server {
    return &Server{
        Logger: &Logger{prefix: "SERVER"},
        addr:   addr,
    }
}

// Utilisation
s := NewServer(":8080")
s.Log("Starting...") // Appelle Logger.Log embedded

Mémoire et performance

Préallouer les slices quand la taille est connue

// Mal : Agrandit la slice plusieurs fois
func processItems(items []Item) []Result {
    var results []Result
    for _, item := range items {
        results = append(results, process(item))
    }
    return results
}

// Bien : Allocation unique
func processItems(items []Item) []Result {
    results := make([]Result, 0, len(items))
    for _, item := range items {
        results = append(results, process(item))
    }
    return results
}

Utiliser sync.Pool pour les allocations fréquentes

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)
    // Traitement...
    return buf.Bytes()
}

Éviter la concaténation de chaînes dans les boucles

// Mal : Crée de nombreuses allocations de chaînes
func join(parts []string) string {
    var result string
    for _, p := range parts {
        result += p + ","
    }
    return result
}

// Bien : Allocation unique avec 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()
}

// Meilleur : Utiliser la bibliothèque standard
func join(parts []string) string {
    return strings.Join(parts, ",")
}

Intégration des outils Go

Commandes essentielles

# Build et exécution
go build ./...
go run ./cmd/myapp

# Tests
go test ./...
go test -race ./...
go test -cover ./...

# Analyse statique
go vet ./...
staticcheck ./...
golangci-lint run

# Gestion des modules
go mod tidy
go mod verify

# Formatage
gofmt -w .
goimports -w .

Configuration de linter recommandée (.golangci.yml)

linters:
  enable:
    - errcheck
    - gosimple
    - govet
    - ineffassign
    - staticcheck
    - unused
    - gofmt
    - goimports
    - misspell
    - unconvert
    - unparam

linters-settings:
  errcheck:
    check-type-assertions: true
  govet:
    enable:
      - shadow

issues:
  exclude-use-default: false

Référence rapide : Idiomes Go

IdiomeDescription
Accepter des interfaces, retourner des structuresLes fonctions acceptent des paramètres d'interface, retournent des types concrets
Les erreurs sont des valeursTraiter les erreurs comme des valeurs de première classe, pas des exceptions
Ne communiquez pas en partageant la mémoireUtiliser les canaux pour la coordination entre goroutines
Rendre la valeur zéro utileLes types doivent fonctionner sans initialisation explicite
Une petite copie vaut mieux qu'une petite dépendanceÉviter les dépendances externes inutiles
Clair est mieux qu'astucieuxPrioriser la lisibilité plutôt que l'ingéniosité
gofmt n'est le favori de personne mais l'ami de tout le mondeToujours formater avec gofmt/goimports
Retourner tôtGérer les erreurs d'abord, garder le chemin heureux non indenté

Anti-patrons à éviter

// Mal : Retours nus dans des fonctions longues
func process() (result int, err error) {
    // ... 50 lignes ...
    return // Qu'est-ce qui est retourné ?
}

// Mal : Utiliser panic pour le flux de contrôle
func GetUser(id string) *User {
    user, err := db.Find(id)
    if err != nil {
        panic(err) // Ne faites pas ça
    }
    return user
}

// Mal : Passer le contexte dans une structure
type Request struct {
    ctx context.Context // Le contexte doit être le premier paramètre
    ID  string
}

// Bien : Contexte comme premier paramètre
func ProcessRequest(ctx context.Context, id string) error {
    // ...
}

// Mal : Mélanger les récepteurs valeur et pointeur
type Counter struct{ n int }
func (c Counter) Value() int { return c.n }    // Récepteur valeur
func (c *Counter) Increment() { c.n++ }        // Récepteur pointeur
// Choisissez un style et soyez cohérent

Rappelez-vous : Le code Go devrait être ennuyeux de la meilleure façon possible - prévisible, cohérent et facile à comprendre. Dans le doute, gardez-le simple.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Skills associés