← Go EspañolChapter 13 of 13

Mejores Prácticas

## Objetivos de Aprendizaje - Seguir convenciones de formato de Go - Escribir código claro e idiomático - Dominar el manejo efectivo de errores - Escribir y ejecutar pruebas - Documentar código apropiadamente ## Formateo (gofmt) ### Formateo Automático ```bash gofmt -w main.go # Formatear y escribir gofmt -d main.go # Mostrar diff gofmt -e main.go # Mostrar errores de sintaxis ``` ### go fmt ```bash go fmt ./... # Ejecutar gofmt en todos los archivos ``` ### Integración de IDE La mayoría de los IDEs formatean automáticamente al guardar. ## Guía de Estilo ### Longitud de Línea Go no enforces line length. Write for readability. ### Indentación Usar tabs. Dejar que el editor maneje la conversión. ### Llaves ```go // Correcto if x > 0 { fmt.Println("positivo") } // Incorrecto if x > 0 { fmt.Println("positivo") } ``` ### Agrupar Importaciones ```go import ( "fmt" "math" "strings" "github.com/pkg/errors" ) ``` ### Agrupar Variables ```go // Bueno: declaraciones agrupadas var ( count int name string ready bool ) ``` ## Convenciones de Nomenclatura ### Variables - Cortas pero significativas - camelCase para variables locales - PascalCase para exportadas ```go var name string // Local var UserName string // Exportada i, j := 0, 1 // Variables de bucle ctx := context.Background() // Context ``` ### Constantes - PascalCase para exportadas - camelCase para no exportadas ```go const MaxRetries = 3 // Exportada const maxCacheSize = 100 // No exportada ``` ### Funciones - PascalCase para exportadas - camelCase para no exportadas ```go func CalculateArea(r float64) float64 {} // Exportada func calculateInterest() {} // No exportada ``` ### Paquetes - Cortos, minúsculas, sin guiones bajos - Descriptivos pero concisos ```go import "strings" import "context" import "errgroup" ``` ### Nombres de Interfaces - Añadir sufijo `-er` cuando sea apropiado - Describir el comportamiento ```go type Reader interface {} type Writer interface {} type ReadWriter interface {} ``` ## Manejo de Errores ### Manejo de Errores Explícito ```go // Bueno if err != nil { return fmt.Errorf("operación fallida: %w", err) } // Malo: ignorar errores result, _ := riskyCall() ``` ### Envolver Errores con Contexto ```go // Bueno if err := readConfig(path); err != nil { return fmt.Errorf("cargando config desde %s: %w", path, err) } // Malo if err := readConfig(path); err != nil { return err } ``` ### Errores Centinela ```go var ErrNotFound = errors.New("no encontrado") func find(id string) error { if id == "missing" { return ErrNotFound } return nil } ``` ## Comentarios ### Comentarios de Documentación ```go // Package utils proporciona funciones de utilidad para manipulación de cadenas. package utils // Add retorna la suma de dos enteros. func Add(a, b int) int { return a + b } ``` ### Comentarios en Línea ```go // Asegurar capacidad para nuevos elementos if len(slice) >= cap(slice) { slice = append(slice, make(T, 10)...) } ``` ### Patrones de Comentarios ```go // TODO: Añadir validación // FIXME: Manejar caso extremo // NOTE: Esto es intencional porque... ``` ## Estructura del Código ### Mantener Funciones Pequeñas ```go // Bueno: función enfocada func validateEmail(email string) error { if !strings.Contains(email, "@") { return ErrInvalidEmail } return nil } // Bueno: compuesto en función más grande func registerUser(email string) error { if err := validateEmail(email); err != nil { return err } // Continuar registro... } ``` ### Retornos Anticipados ```go // Bueno func process(data []byte) error { if len(data) == 0 { return ErrEmptyData } // Procesar datos... } // Malo func process(data []byte) error { if len(data) > 0 { // Procesar datos... } else { return ErrEmptyData } } ``` ### Evitar Condicionales Anidados ```go // Bueno: retorno anticipado func findUser(id string) (*User, error) { if id == "" { return nil, ErrInvalidID } user, err := db.GetUser(id) if err != nil { return nil, err } return user, nil } ``` ## Pruebas ### Nombres de Archivos de Prueba ```text add.go -> add_test.go utils/helpers.go -> utils/helpers_test.go ``` ### Pruebas Dirigidas por Tablas ```go func TestAdd(t *testing.T) { tests := []struct { name string a, b int want int }{ {"positive", 2, 3, 5}, {"negative", -1, -1, -2}, {"zero", 0, 5, 5}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := Add(tt.a, tt.b) if got != tt.want { t.Errorf("Add(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.want) } }) } } ``` ### Subtests ```go func TestMath(t *testing.T) { t.Run("Add", func(t *testing.T) { if Add(2, 3) != 5 { t.Error("Add falló") } }) t.Run("Multiply", func(t *testing.T) { if Multiply(2, 3) != 6 { t.Error("Multiply falló") } }) } ``` ### Probando Errores ```go func TestDivide(t *testing.T) { _, err := Divide(10, 0) if err == nil { t.Error("Error esperado para división por cero") } if !errors.Is(err, ErrDivisionByZero) { t.Errorf("Esperado ErrDivisionByZero, got %v", err) } } ``` ### Benchmarking ```go func BenchmarkAdd(b *testing.B) { for i := 0; i < b.N; i++ { Add(1, 2) } } ``` ```bash go test -bench=. -benchmem ``` ### Ejecutar Pruebas ```bash go test # Ejecutar pruebas go test -v # Verboso go test -run Pattern # Ejecutar pruebas que coinciden go test -cover # Mostrar cobertura go test -race # Verificar condiciones de carrera go test ./... # Todos los paquetes ``` ## Mejores Prácticas de Concurrencia ### No Filtrar Goroutines ```go // Bueno: goroutine controlada done := make(chan struct{}) go func() { // Trabajo... close(done) }() <-done ``` ### Context para Cancelación ```go ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() go doWork(ctx) ``` ### Evitar Estado Compartido ```go // Bueno: comunicar datos compartidos ch := make(chan int) go func() { ch <- result }() value := <-ch ``` ## Consejos de Rendimiento ### Preasignar Slices ```go // Bueno slice := make([]int, 0, 100) for i := 0; i < 100; i++ { slice = append(slice, i) } // Malo (múltiples reasignaciones) var slice []int for i := 0; i < 100; i++ { slice = append(slice, i) } ``` ### Usar strings.Builder ```go var sb strings.Builder for i := 0; i < 1000; i++ { sb.WriteString("hello") } result := sb.String() ``` ### Evitar Conversiones []byte a string ```go // En rutas calientes data := []byte("hello") s := string(data) // ¡Asignación! // Mejor: mantener como []byte cuando sea posible data := []byte("hello") // Usar data directamente ``` ## Documentación con godoc ### Ejecutar godoc ```bash go doc fmt.Println godoc -http=:8080 ``` ### Exportar para Documentación Todos los identificadores exportados están documentados en godoc. ## Resumen - Usar `gofmt` o `go fmt` para formateo - Seguir convenciones de nomenclatura: camelCase local, PascalCase exportado - Manejar errores explícitamente; no ignorarlos - Escribir comentarios para funciones y paquetes exportados - Escribir pruebas dirigidas por tablas para cobertura completa - Mantener funciones pequeñas y enfocadas - Usar retornos anticipados para reducir anidamiento - No filtrar goroutines; usar context para cancelación - Preasignar slices para evitar reasignaciones - Documentar código exportado para godoc

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →