golang-patterns
Patrones idiomáticos de Go, buenas prácticas y convenciones para construir aplicaciones Go robustas, eficientes y mantenibles.
DeepseekModel
Curated skill
Quality Excellent · 90
v1.0.0
Get
https://deepseekmodel.com/api/download.php?id=affaan-m-ecc-docs-es-skills-golang-patterns-skill-md&format=skill
Download .skill
Standard format with system_prompt and model_config, ready for any agent framework
The actual content of the system_prompt field in the .skill file.
name golang-patterns description Patrones idiomáticos de Go, buenas prácticas y convenciones para construir aplicaciones Go robustas, eficientes y mantenibles. origin ECC 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. // 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. // 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. // 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 // 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 // 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 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 // 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 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 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 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 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 // 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 // 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 // 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 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 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 // 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 // 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 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 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 // 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 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 // 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 {
Keywords that activate this skill. Click one to copy it.
This skill does not provide trigger words.
The downloaded .skill package contains the following fields.
| Field | Description |
|---|---|
| format | Format tag (skill/v1) |
| skill_id | Unique skill ID |
| name | Skill name |
| version | Version |
| description | Description |
| category | Categories (array) |
| trigger_words | Trigger words |
| tags | Tags |
| source | Source |
| source_url | Source URL (this page) |
| exported_at | Exported at (set per download) |
| system_prompt | System prompt body |
| model_config | Model config: provider / model / temperature / max_tokens / top_p |
| examples | Examples |
| install_guide | Import guide for Coze / Dify / Claude / custom frameworks |
The same skill can be exported in different platform formats.