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 →