Golang Patterns logo

Golang Patterns

CommunityPopular
affaan-m
golang-patterns

Patrones idiomáticos de Go, buenas prácticas y convenciones para construir aplicaciones Go robustas, eficientes y mantenibles.

Overview

Publisheraffaan-m
RepositoryECC
Skill namegolang-patterns
Stars
261.1K
Forks
39.1K
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by affaan-m on GitHub. Read the source before you install it.

Installation

Install the Golang Patterns AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/affaan-m/ECC.git /tmp/ECC
mkdir -p .claude/skills
cp -r /tmp/ECC/docs/es/skills/golang-patterns .claude/skills/golang-patterns
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Golang Patterns in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Golang Patterns on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Golang Patterns is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Patrones de Desarrollo Go

Patrones idiomáticos de Go y buenas prácticas para construir aplicaciones robustas, eficientes y mantenibles.

Cuándo Activar

  • Escribir nuevo código Go
  • Revisar código Go
  • Refactorizar código Go existente
  • Diseñar paquetes/módulos Go

Principios Fundamentales

1. Simplicidad y Claridad

Go favorece la simplicidad sobre la astucia. El código debe ser obvio y fácil de leer.

go
// Bien: Claro y directo
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: Demasiado ingenioso
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. Hacer que el Valor Cero Sea Útil

Diseñar tipos para que su valor cero sea inmediatamente usable sin inicialización.

go
// Bien: El valor cero es útil
type Counter struct {
    mu    sync.Mutex
    count int // el valor cero es 0, listo para usar
}

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

// Bien: bytes.Buffer funciona con el valor cero
var buf bytes.Buffer
buf.WriteString("hello")

// Mal: Requiere inicialización
type BadCounter struct {
    counts map[string]int // el mapa nil causará panic
}

3. Aceptar Interfaces, Retornar Structs

Las funciones deben aceptar parámetros de interfaz y retornar tipos concretos.

go
// Bien: Acepta interfaz, retorna tipo concreto
func ProcessData(r io.Reader) (*Result, error) {
    data, err := io.ReadAll(r)
    if err != nil {
        return nil, err
    }
    return &Result{Data: data}, nil
}

// Mal: Retorna interfaz (oculta detalles de implementación innecesariamente)
func ProcessData(r io.Reader) (io.Reader, error) {
    // ...
}

Patrones de Manejo de Errores

Wrapping de Errores con Contexto

go
// Bien: Envolver errores con contexto
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
}

Tipos de Error Personalizados

go
// Definir errores específicos del dominio
type ValidationError struct {
    Field   string
    Message string
}

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

// Errores centinela para casos comunes
var (
    ErrNotFound     = errors.New("resource not found")
    ErrUnauthorized = errors.New("unauthorized")
    ErrInvalidInput = errors.New("invalid input")
)

Verificación de Errores con errors.Is y errors.As

go
func HandleError(err error) {
    // Verificar error específico
    if errors.Is(err, sql.ErrNoRows) {
        log.Println("No records found")
        return
    }

    // Verificar tipo de error
    var validationErr *ValidationError
    if errors.As(err, &validationErr) {
        log.Printf("Validation error on field %s: %s",
            validationErr.Field, validationErr.Message)
        return
    }

    // Error desconocido
    log.Printf("Unexpected error: %v", err)
}

Nunca Ignorar Errores

go
// Mal: Ignorar error con identificador en blanco
result, _ := doSomething()

// Bien: Manejar o documentar explícitamente por qué es seguro ignorar
result, err := doSomething()
if err != nil {
    return err
}

// Aceptable: Cuando el error realmente no importa (raro)
_ = writer.Close() // Limpieza de mejor esfuerzo, error registrado en otro lugar

Patrones de Concurrencia

Worker Pool

go
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 para Cancelación y Timeouts

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

Apagado Graceful

go
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 para Goroutines Coordinadas

go
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 // Capturar variables del bucle
        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
}

Evitar Goroutine Leaks

go
// Mal: Goroutine leak si el context es cancelado
func leakyFetch(ctx context.Context, url string) <-chan []byte {
    ch := make(chan []byte)
    go func() {
        data, _ := fetch(url)
        ch <- data // Bloquea para siempre si no hay receptor
    }()
    return ch
}

// Bien: Maneja correctamente la cancelación
func safeFetch(ctx context.Context, url string) <-chan []byte {
    ch := make(chan []byte, 1) // Canal con buffer
    go func() {
        data, err := fetch(url)
        if err != nil {
            return
        }
        select {
        case ch <- data:
        case <-ctx.Done():
        }
    }()
    return ch
}

Diseño de Interfaces

Interfaces Pequeñas y Enfocadas

go
// Bien: Interfaces de un solo método
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
}

// Componer interfaces según se necesite
type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

Definir Interfaces Donde Se Usan

go
// En el paquete consumidor, no en el proveedor
package service

// UserStore define lo que este servicio necesita
type UserStore interface {
    GetUser(id string) (*User, error)
    SaveUser(user *User) error
}

type Service struct {
    store UserStore
}

// La implementación concreta puede estar en otro paquete
// No necesita conocer esta interfaz

Comportamiento Opcional con Type Assertions

go
type Flusher interface {
    Flush() error
}

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

    // Hacer flush si está soportado
    if f, ok := w.(Flusher); ok {
        return f.Flush()
    }
    return nil
}

Organización de Paquetes

Layout Estándar del Proyecto

text
myproject/
├── cmd/
│   └── myapp/
│       └── main.go           # Punto de entrada
├── internal/
│   ├── handler/              # Handlers HTTP
│   ├── service/              # Lógica de negocio
│   ├── repository/           # Acceso a datos
│   └── config/               # Configuración
├── pkg/
│   └── client/               # Cliente de API pública
├── api/
│   └── v1/                   # Definiciones de API (proto, OpenAPI)
├── testdata/                 # Fixtures de prueba
├── go.mod
├── go.sum
└── Makefile

Nomenclatura de Paquetes

go
// Bien: Corto, minúsculas, sin guiones bajos
package http
package json
package user

// Mal: Verboso, mayúsculas mixtas, o redundante
package httpHandler
package json_parser
package userService // Sufijo 'Service' redundante

Evitar Estado a Nivel de Paquete

go
// Mal: Estado global mutable
var db *sql.DB

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

// Bien: Inyección de dependencias
type Server struct {
    db *sql.DB
}

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

Diseño de Structs

Patrón de Opciones Funcionales

go
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, // por defecto
        logger:  log.Default(),    // por defecto
    }
    for _, opt := range opts {
        opt(s)
    }
    return s
}

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

Embedding para Composición

go
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 obtiene el método Log
    addr    string
}

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

// Uso
s := NewServer(":8080")
s.Log("Starting...") // Llama al Logger.Log embebido

Memoria y Rendimiento

Pre-asignar Slices Cuando Se Conoce el Tamaño

go
// Mal: El slice crece múltiples veces
func processItems(items []Item) []Result {
    var results []Result
    for _, item := range items {
        results = append(results, process(item))
    }
    return results
}

// Bien: Asignación única
func processItems(items []Item) []Result {
    results := make([]Result, 0, len(items))
    for _, item := range items {
        results = append(results, process(item))
    }
    return results
}

Usar sync.Pool para Asignaciones Frecuentes

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

Evitar Concatenación de Strings en Bucles

go
// Mal: Crea muchas asignaciones de string
func join(parts []string) string {
    var result string
    for _, p := range parts {
        result += p + ","
    }
    return result
}

// Bien: Asignación única con 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()
}

// Mejor: Usar la librería estándar
func join(parts []string) string {
    return strings.Join(parts, ",")
}

Integración con Herramientas Go

Comandos Esenciales

bash
# Build y ejecutar
go build ./...
go run ./cmd/myapp

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

# Análisis estático
go vet ./...
staticcheck ./...
golangci-lint run

# Gestión de módulos
go mod tidy
go mod verify

# Formato
gofmt -w .
goimports -w .

Configuración Recomendada de Linter (.golangci.yml)

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

linters-settings:
  errcheck:
    check-type-assertions: true
  govet:
    check-shadowing: true

issues:
  exclude-use-default: false

Referencia Rápida: Modismos de Go

ModismoDescripción
Aceptar interfaces, retornar structsLas funciones aceptan parámetros de interfaz, retornan tipos concretos
Los errores son valoresTratar errores como valores de primera clase, no como excepciones
No comunicarse compartiendo memoriaUsar canales para coordinación entre goroutines
Hacer que el valor cero sea útilLos tipos deben funcionar sin inicialización explícita
Un poco de copia es mejor que una pequeña dependenciaEvitar dependencias externas innecesarias
Claro es mejor que inteligentePriorizar legibilidad sobre astucia
gofmt no es favorito de nadie pero sí amigo de todosSiempre formatear con gofmt/goimports
Retornar tempranoManejar errores primero, mantener el camino feliz sin indentar

Anti-Patrones a Evitar

go
// Mal: Retornos naked en funciones largas
func process() (result int, err error) {
    // ... 50 líneas ...
    return // ¿Qué se está retornando?
}

// Mal: Usar panic para control de flujo
func GetUser(id string) *User {
    user, err := db.Find(id)
    if err != nil {
        panic(err) // No hacer esto
    }
    return user
}

// Mal: Pasar context en struct
type Request struct {
    ctx context.Context // El context debería ser el primer parámetro
    ID  string
}

// Bien: Context como primer parámetro
func ProcessRequest(ctx context.Context, id string) error {
    // ...
}

// Mal: Mezclar receptores de valor y puntero
type Counter struct{ n int }
func (c Counter) Value() int { return c.n }    // Receptor de valor
func (c *Counter) Increment() { c.n++ }        // Receptor de puntero
// Elegir un estilo y ser consistente

Recuerda: El código Go debe ser aburrido de la mejor manera — predecible, consistente y fácil de entender. Ante la duda, mantenerlo simple.

Frequently asked questions

What does the Golang Patterns AI skill do?

Patrones idiomáticos de Go, buenas prácticas y convenciones para construir aplicaciones Go robustas, eficientes y mantenibles.

Why use Golang Patterns on TypingMind?

Because you install it once and use it with any model. Golang Patterns is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Golang Patterns in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/affaan-m/ECC/tree/main/docs/es/skills/golang-patterns. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Golang Patterns?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Golang Patterns?

As many as you like. As long as a model supports skills, you can use Golang Patterns with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Golang Patterns AI skill free?

Yes. It is published on GitHub by affaan-m under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇