viccom/patrons-de-conception-go

Patrons Go idiomatiques — options fonctionnelles, constructeurs, gestion d'erreurs et cascade, gestion des ressources et cycle de vie, arrêt gracieux, résilience, architecture, injection de dépendances, gestion des données, streaming, et plus encore.

Qu'est-ce que patrons-de-conception-go ?

Cette compétence est un guide complet pour les développeurs Go qui souhaitent appliquer des patrons de conception idiomatiques et robustes dans des applications prêtes pour la production. Elle couvre un large éventail de sujets, de la création d'APIs avec des options fonctionnelles à la gestion d'erreurs, en passant par la résilience et l'architecture. L'accent est mis sur la simplicité et l'explicite, en privilégiant les solutions les plus petites qui résolvent un problème réel, sans abstraction prématurée. La compétence est conçue pour des agents IA comme Claude Code, Cursor ou Codex, et propose deux modes principaux : le mode conception, pour créer de nouvelles APIs ou structures, et le mode révision, pour auditer du code existant. Elle fournit des règles précises (comme "éviter init()", "utiliser crypto/rand", "toujours définir un timeout") et des exemples de code concrets pour chaque patron, facilitant leur application immédiate dans un projet.

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/viccom/viccom-agent-skills/tree/HEAD/skills/golang-design-patterns

Installed? Explore more Développement et programmation skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Demander à votre IA préférée

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

Documentation

Persona : Vous êtes un architecte Go qui valorise la simplicité et l'explicite. Vous n'appliquez des patrons que lorsqu'ils résolvent un vrai problème — pas pour faire preuve de sophistication — et vous vous opposez à l'abstraction prématurée.

Modes :

  • Mode conception — création de nouvelles API, packages ou structures d'application : demandez au développeur sa préférence architecturale avant de proposer des patrons ; privilégiez le plus petit patron qui satisfait l'exigence.
  • Mode révision — audit du code existant pour des problèmes de conception : recherchez les abus de init(), les ressources non limitées, les timeouts manquants et l'état global implicite ; rapportez les constatations avant de suggérer des refactorisations.

Réglage par défaut de la communauté. Une compétence d'entreprise qui remplace explicitement la compétence samber/cc-skills-golang@golang-design-patterns a la priorité.

Patrons de conception et idiomes Go

Patrons Go idiomatiques pour du code prêt pour la production. Pour les détails sur la gestion des erreurs, voir la compétence samber/cc-skills-golang@golang-error-handling ; pour la propagation de contexte, voir la compétence samber/cc-skills-golang@golang-context ; pour la conception de structures/interfaces, voir la compétence samber/cc-skills-golang@golang-structs-interfaces.

Résumé des bonnes pratiques

  1. Les constructeurs DEVRAIENT utiliser des options fonctionnelles — elles évoluent mieux au fil de l'évolution des API (une fonction par option, pas de changements cassants)
  2. Les options fonctionnelles DOIVENT retourner une erreur si la validation peut échouer — détectez une configuration erronée à la construction, pas à l'exécution
  3. Évitez init() — s'exécute implicitement, ne peut pas retourner d'erreurs, rend les tests imprévisibles. Utilisez des constructeurs explicites
  4. Les énumérations DEVRAIENT commencer à 1 (ou un sentinelle Inconnu à 0) — la valeur zéro de Go passe silencieusement comme premier membre de l'énumération
  5. Les cas d'erreur DOIVENT être traités en premier avec un retour anticipé — gardez le chemin heureux plat
  6. Panic est pour les bugs, pas pour les erreurs attendues — les appelants peuvent gérer les erreurs retournées ; les panics plantent le processus
  7. defer Close() immédiatement après l'ouverture — des modifications ultérieures du code peuvent accidentellement ignorer le nettoyage
  8. runtime.AddCleanup plutôt que runtime.SetFinalizer — les finaliseurs sont imprévisibles et peuvent ressusciter des objets
  9. Chaque appel externe DEVRAIT avoir un timeout — un upstream lent bloque votre goroutine indéfiniment
  10. Limitez tout (tailles de pools, profondeurs de files d'attente, tampons) — les ressources non limitées croissent jusqu'à planter
  11. La logique de nouvelle tentative DOIT vérifier l'annulation du contexte entre les tentatives
  12. Utilisez strings.Builder pour la concaténation dans les boucles → voir samber/cc-skills-golang@golang-code-style
  13. string vs []byte : utilisez []byte pour la mutation et les E/S, string pour l'affichage et les clés — les conversions allouent
  14. Itérateurs (Go 1.23+) : utilisez pour l'évaluation paresseuse — évitez de tout charger en mémoire
  15. Diffusez les gros transferts — charger des millions de lignes provoque un OOM ; le streaming maintient la mémoire constante
  16. //go:embed pour les ressources statiques — intègre à la compilation, élimine les erreurs d'E/S de fichier à l'exécution
  17. Utilisez crypto/rand pour les clés/tokens — math/rand est prévisible → voir samber/cc-skills-golang@golang-security
  18. Les regexp DOIVENT être compilées une fois au niveau du package — la compilation est O(n) et alloue
  19. Vérifications d'interface à la compilation : var _ Interface = (*Type)(nil)
  20. Un peu de recodage > une grosse dépendance — chaque dépendance ajoute une surface d'attaque et une charge de maintenance
  21. Concevez pour la testabilité — acceptez les interfaces, injectez les dépendances

Patrons de constructeurs : Options fonctionnelles vs Builder

Options fonctionnelles (Préféré)

type Server struct {
    addr         string
    readTimeout  time.Duration
    writeTimeout time.Duration
    maxConns     int
}

type Option func(*Server)

func WithReadTimeout(d time.Duration) Option {
    return func(s *Server) { s.readTimeout = d }
}

func WithWriteTimeout(d time.Duration) Option {
    return func(s *Server) { s.writeTimeout = d }
}

func WithMaxConns(n int) Option {
    return func(s *Server) { s.maxConns = n }
}

func NewServer(addr string, opts ...Option) *Server {
    // Options par défaut
    s := &Server{
        addr:         addr,
        readTimeout:  5 * time.Second,
        writeTimeout: 10 * time.Second,
        maxConns:     100,
    }
    for _, opt := range opts {
        opt(s)
    }
    return s
}

// Utilisation
srv := NewServer(":8080",
    WithReadTimeout(30*time.Second),
    WithMaxConns(500),
)

Les constructeurs DEVRAIENT utiliser les options fonctionnelles — elles évoluent mieux avec l'évolution de l'API et nécessitent moins de code. Utilisez le patron builder uniquement si vous avez besoin d'une validation complexe entre les étapes de configuration.

Constructeurs et initialisation

Évitez init() et les variables globales mutables

init() s'exécute implicitement, rend les tests plus difficiles et crée des dépendances cachées :

  • Plusieurs fonctions init() s'exécutent dans l'ordre de déclaration, entre les fichiers dans l'ordre alphabétique des noms de fichiers — fragile
  • Ne peut pas retourner d'erreurs — les échecs doivent panic ou log.Fatal
  • S'exécute avant main() et les tests — les effets de bord rendent les tests imprévisibles
// Mauvais — état global caché
var db *sql.DB

func init() {
    var err error
    db, err = sql.Open("postgres", os.Getenv("DATABASE_URL"))
    if err != nil {
        log.Fatal(err)
    }
}

// Bon — initialisation explicite, injectable
func NewUserRepository(db *sql.DB) *UserRepository {
    return &UserRepository{db: db}
}

Énumérations : Commencez à 1

Les valeurs zéro devraient représenter un état invalide/non défini :

type Status int

const (
    StatusUnknown Status = iota // 0 = invalide/non défini
    StatusActive                // 1
    StatusInactive              // 2
    StatusSuspended             // 3
)

Compilez les regex une fois

// Bon — compilé une fois au niveau du package
var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)

func ValidateEmail(email string) bool {
    return emailRegex.MatchString(email)
}

Utilisez //go:embed pour les ressources statiques

import "embed"

//go:embed templates/*
var templateFS embed.FS

//go:embed version.txt
var version string

Vérifications d'interface à la compilation

→ Voir la compétence samber/cc-skills-golang@golang-structs-interfaces pour le patron var _ Interface = (*Type)(nil).

Patrons de flux d'erreurs

Les cas d'erreur DOIVENT être traités en premier avec un retour anticipé — gardez le chemin heureux avec une indentation minimale. → Voir la compétence samber/cc-skills-golang@golang-code-style pour le patron complet et des exemples.

Quand panic vs retourner une erreur

  • Retourner une erreur : pannes réseau, fichier non trouvé, entrée invalide — tout ce qu'un appelant peut gérer
  • Panic : pointeur nul dans un endroit qui devrait être impossible, invariant violé, constructeurs Must* utilisés au moment de l'initialisation
  • Erreurs .Close() / Flush() : le nettoyage en lecture seule peut souvent utiliser defer f.Close(), mais les ressources d'écriture/Flush doivent signaler les erreurs de fermeture ou de flush lorsque la durabilité est importante

Gestion des données

string vs []byte vs []rune

TypeDéfaut pourUtilisation quand
stringToutImmuable, sûr, UTF-8
[]byteE/SÉcrire dans io.Writer, construire des chaînes, mutations
[]runeOpérations Unicodelen() doit signifier des caractères, pas des octets

Évitez les conversions répétées — chacune alloue. Restez dans un type jusqu'à ce que vous ayez besoin de l'autre.

Itérateurs et streaming pour les grandes données

Utilisez des itérateurs (Go 1.23+) et des patrons de streaming pour traiter de grands ensembles de données sans tout charger en mémoire. Pour les gros transferts entre services (par exemple, 1 million de lignes de DB vers HTTP), diffusez pour éviter l'OOM.

Pour des exemples de code, voir Patrons de gestion des données.

Gestion des ressources

defer Close() immédiatement après l'ouverture — n'attendez pas, n'oubliez pas :

f, err := os.Open(path)
if err != nil {
    return err
}
defer f.Close() // ici, pas 50 lignes plus tard

rows, err := db.QueryContext(ctx, query)
if err != nil {
    return err
}
defer rows.Close()

Pour l'arrêt gracieux, les pools de ressources et runtime.AddCleanup, voir Gestion des ressources.

Résilience et limites

Timeout sur chaque appel externe

ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

resp, err := httpClient.Do(req.WithContext(ctx))

Nouvelles tentatives et vérifications du contexte

La logique de nouvelle tentative DOIT vérifier ctx.Err() entre les tentatives et utiliser un backoff exponentiel/linéaire via select sur ctx.Done(). Les longues boucles DOIVENT vérifier ctx.Err() périodiquement. → Voir la compétence samber/cc-skills-golang@golang-context.

Patrons de base de données

→ Voir la compétence samber/cc-skills-golang@golang-database pour sqlx/pgx, transactions, colonnes nullables, pools de connexions, interfaces de repository, tests.

Architecture

Demandez au développeur quelle architecture il préfère : architecture propre, hexagonale, DDD ou disposition plate. N'imposez pas une architecture complexe sur un petit projet.

Principes fondamentaux quelle que soit l'architecture :

  • Gardez le domaine pur — pas de dépendances de framework dans la couche domaine
  • Échouez rapidement — validez aux limites, faites confiance au code interne
  • Rendez les états illégaux non représentables — utilisez les types pour appliquer les invariants
  • Respectez les principes de l'application 12 facteurs — → voir samber/cc-skills-golang@golang-project-layout

Guides détaillés

GuidePérimètre
Patrons d'architecturePrincipes de haut niveau, quand chaque architecture convient
Architecture propreCas d'utilisation, règle de dépendance, adaptateurs en couches
Architecture hexagonalePorts et adaptateurs, isolation du cœur de domaine
Domain-Driven DesignAgrégats, objets valeur, contextes délimités

Philosophie du code

  • Évitez le code répétitif — mais n'abstrayez pas prématurément
  • Minimisez les dépendances — un peu de recodage > une grosse dépendance
  • Concevez pour la testabilité — acceptez les interfaces, injectez les dépendances, gardez les fonctions pures

Références croisées

  • → Voir la compétence samber/cc-skills-golang@golang-data-structures pour la sélection de structures de données, les internes et les packages container/
  • → Voir la compétence samber/cc-skills-golang@golang-error-handling pour l'encapsulation des erreurs, les erreurs sentinelles et la règle de gestion unique
  • → Voir la compétence samber/cc-skills-golang@golang-structs-interfaces pour la conception d'interfaces et la composition
  • → Voir la compétence samber/cc-skills-golang@golang-concurrency pour le cycle de vie des goroutines et l'arrêt gracieux
  • → Voir la compétence samber/cc-skills-golang@golang-context pour les patrons de timeout et d'annulation
  • → Voir la compétence samber/cc-skills-golang@golang-project-layout pour l'architecture et la structure des répertoires
  • → Voir la compétence samber/cc-skills-golang@golang-refactoring pour organiser en toute sécurité une migration vers l'un de ces patrons (struct d'options, DI, interfaces côté consommateur) dans une base de code existante

Individual skills in this repo

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

Skills associés