# Regla: Estilo de Código Go

Go tiene una sola forma correcta de formatear código: `gofmt`. Esta regla
complementa el estilo mecánico con las convenciones idiomáticas de Go que
no puede verificar una herramienta automática. Seguirlas hace que el código
sea reconocible para cualquier desarrollador Go, no solo para el equipo.

---

## gofmt es la ley — sin alternativa ni discusión

```bash
# Obligatorio antes de cada commit
gofmt -w .

# O con goimports (superset de gofmt que también ordena imports)
goimports -w .
```

Un PR que no pasa `gofmt -l .` (que lista archivos mal formateados) no pasa CI.
No se debate el formato — gofmt es la autoridad. Si el equipo no está de acuerdo
con alguna decisión de gofmt, el equipo está equivocado.

---

## goimports para organización de imports

`goimports` aplica gofmt y además organiza imports en grupos separados por línea en blanco:

```go
import (
    // Paquetes de la standard library
    "context"
    "fmt"
    "time"

    // Dependencias externas
    "github.com/google/uuid"
    "go.uber.org/zap"

    // Paquetes internos del proyecto
    "github.com/miempresa/miapp/internal/domain"
    "github.com/miempresa/miapp/internal/repository"
)
```

---

## Nombres: cortos en scope corto, descriptivos en exportados

Effective Go es la referencia. Las convenciones de naming en Go son distintas a otros lenguajes.

```go
// BIEN — nombres cortos para variables de scope corto (loops, funciones pequeñas)
for i, v := range facturas {
    total += v.Total
}

if err := repo.Guardar(ctx, factura); err != nil {
    return fmt.Errorf("guardar factura: %w", err)
}

// BIEN — nombres descriptivos para exportados y variables de scope amplio
type FacturaRepository interface {
    BuscarPorID(ctx context.Context, id uuid.UUID) (*Factura, error)
    ListarPorCliente(ctx context.Context, clienteID uuid.UUID) ([]*Factura, error)
}

// MAL — nombre largo para variable de loop (innecesariamente verbose)
for indiceDeIteracion, facturaActual := range facturas { ... }

// MAL — nombre críptico para exportado
func Proc(c context.Context, f *Fct) (*Res, error) { ... }
```

Reglas específicas:
- Acrónimos exportados en MAYÚSCULAS: `URL`, `HTTP`, `ID`, no `Url`, `Http`, `Id`.
- Interfaces de un solo método: nombre del método + `-er`: `Reader`, `Writer`, `Stringer`.
- No repetir el nombre del paquete: `factura.Factura` → `factura.Nueva()` retorna `*Factura`.

---

## "Accept interfaces, return structs"

Las funciones aceptan interfaces (para desacoplamiento y testabilidad) y retornan
tipos concretos (para claridad del llamador).

```go
// MAL — retorna interface: el llamador no sabe qué métodos tiene disponibles
func NuevoServicio(repo FacturaRepository) FacturaServiceInterface { ... }

// BIEN — retorna struct: el llamador sabe exactamente qué recibe
func NuevoServicio(repo FacturaRepository) *FacturaService { ... }

// BIEN — acepta interface: se puede inyectar cualquier implementación
type FacturaService struct {
    repo FacturaRepository  // interface, no *FacturaRepositoryPostgres
}
```

---

## Errores se manejan SIEMPRE — nunca `_ = f()`

En Go, ignorar un error es una decisión explícita que requiere justificación.
`_ = f()` sin comentario es código incorrecto.

```go
// MAL — error ignorado silenciosamente
archivo, _ := os.Open(ruta)
defer archivo.Close()

// MAL — error ignorado con blank identifier sin justificación
_ = repositorio.Guardar(ctx, factura)

// BIEN — error manejado
archivo, err := os.Open(ruta)
if err != nil {
    return fmt.Errorf("abrir archivo %s: %w", ruta, err)
}
defer archivo.Close()

// BIEN — si realmente se ignora, documentar POR QUÉ
_ = cache.Invalidar(key)  // Error no crítico: la caché se reconstruye automáticamente
```

---

## Múltiples return values: (resultado, error)

El patrón estándar de Go para operaciones que pueden fallar. Nunca usar
excepciones ni panics para flujo de control normal.

```go
// Siempre: (valor, error) — el error va último
func BuscarFactura(ctx context.Context, id uuid.UUID) (*Factura, error) {
    factura, err := repo.FindByID(ctx, id)
    if err != nil {
        return nil, fmt.Errorf("buscar factura %s: %w", id, err)
    }
    if factura == nil {
        return nil, ErrFacturaNoEncontrada
    }
    return factura, nil
}

// Consumo idiomático
factura, err := BuscarFactura(ctx, id)
if err != nil {
    // manejar error
}
// usar factura — garantizado no-nil aquí
```

---

## Comentarios en funciones exportadas obligatorios

Toda función, tipo o constante exportada DEBE tener comentario en formato godoc.
El comentario empieza con el nombre del símbolo.

```go
// MAL — sin comentario
func (s *FacturaService) Crear(ctx context.Context, req CrearRequest) (*Factura, error) { ... }

// MAL — comentario que no sigue convención godoc
// Esta función crea una factura
func (s *FacturaService) Crear(...) { ... }

// BIEN — empieza con el nombre del símbolo
// Crear genera una nueva factura a partir de la solicitud dada.
// Retorna ErrClienteInactivo si el cliente no está activo en el momento de la creación.
func (s *FacturaService) Crear(ctx context.Context, req CrearRequest) (*Factura, error) { ... }
```

---

## Límites de tamaño

- Máximo **50 líneas** por función (sin contar comentarios y líneas en blanco).
- Archivos grandes son señal de baja cohesión. Si un archivo supera 300 líneas,
  evaluar si el paquete tiene una sola responsabilidad.
- Paquetes con muchos archivos de <50 líneas son paquetes superficiales — considerar consolidar.

---

## Checklist de estilo Go antes de abrir PR

- [ ] `gofmt -l .` retorna vacío (ningún archivo mal formateado)
- [ ] `goimports -l .` retorna vacío (imports organizados)
- [ ] Funciones y tipos exportados tienen comentarios godoc
- [ ] Sin `_ = f()` sin justificación en comentario
- [ ] Acrónimos exportados en MAYÚSCULAS (URL, ID, HTTP)
- [ ] Funciones <= 50 líneas
- [ ] Interfaces aceptadas como parámetros, structs retornados
