Módulos: `go.mod`, `go get` y semver
Desde Go 1.11, los módulos son la forma oficial de gestionar dependencias. Un `go.mod` por proyecto, versionado por semver y checksum verificado en `go.sum`.
¿Qué es un módulo?
Un módulo es una colección de paquetes Go versionados como una unidad. Se identifica por una ruta (típicamente la URL del repositorio) y se declara en un archivo go.mod en la raíz del proyecto.
// Inicializar un módulo nuevo
// go mod init github.com/fherrera/mi-proyecto
// Esto crea un go.mod con:
// module github.com/fherrera/mi-proyecto
//
// go 1.23
go: creating new go.mod: module github.com/fherrera/mi-proyectoLa ruta del módulo (github.com/fherrera/mi-proyecto) es importante:
- Debe ser única y, si planeas publicarlo, tiene que coincidir con la URL real del repo.
- Es el prefijo de los imports internos. Si tienes un paquete en
internal/db/, lo importas comogithub.com/fherrera/mi-proyecto/internal/db.
Anatomía de `go.mod`
module github.com/fherrera/mi-proyecto
go 1.23
require (
github.com/google/uuid v1.6.0
github.com/joho/godotenv v1.5.1
)
require (
golang.org/x/sys v0.20.0 // indirect
)
Las directivas principales:
module— la ruta del módulo.go— la versión mínima de Go requerida.require— las dependencias directas y sus versiones.// indirect— marca dependencias que no importas directamente pero que necesita una dep tuya.
Añadir y actualizar dependencias
go get descarga y registra dependencias. Soporta sintaxis flexible para elegir versión.
// Última versión estable
// go get github.com/google/uuid
// Versión específica
// go get github.com/google/uuid@v1.6.0
// Última versión del branch master
// go get github.com/google/uuid@master
// Commit específico (Go resuelve a una pseudo-versión)
// go get github.com/google/uuid@a1b2c3d
// Actualizar a la última versión menor/patch
// go get -u github.com/google/uuid
// Actualizar todas las dependencias
// go get -u ./...
// Quitar una dependencia (úsala y luego corre tidy)
// go get github.com/google/uuid@none
Tras editar imports en tu código, corre go mod tidy. Añade lo que falta, elimina lo que no se usa y mantiene go.mod/go.sum consistentes. Es prácticamente obligatorio antes de commitear.
Semver en Go
Go exige semver estricto: vMAJOR.MINOR.PATCH (con la v minúscula al inicio).
| Componente | Cuándo sube | Compatibilidad |
|---|---|---|
MAJOR (v2.0.0) | Cambios incompatibles | Rompe usuarios existentes |
MINOR (v1.6.0) | Funcionalidad nueva | Retrocompatible |
PATCH (v1.6.3) | Bug fixes | Retrocompatible |
La regla v2+: desde la versión major 2 en adelante, el path del módulo cambia. Una librería en v2.x.x se importa como github.com/foo/bar/v2. Esto permite que v1 y v2 convivan en el mismo binario.
package main
import (
barV1 "github.com/foo/bar"
barV2 "github.com/foo/bar/v2"
)
func main() {
_ = barV1.Hello()
_ = barV2.Hello()
}
`go.sum`, proxy y vendor
go.sum registra los checksums criptográficos de cada versión descargada. Se genera automáticamente y debe commitearse al repo: garantiza que cualquier desarrollador descargue exactamente los mismos bytes.
// github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+L4cM=
// github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
Proxy: go descarga módulos a través de proxy.golang.org por defecto. Eso acelera las descargas y proporciona inmutabilidad. Puedes desactivarlo con GOPROXY=direct o usar uno propio (Athens, Artifactory).
Vendor: copiar todas las dependencias dentro de vendor/ en el repo.
// Crear el directorio vendor con todas las dependencias
// go mod vendor
// A partir de ahora, los builds usan vendor/ en vez de descargar
// go build → detecta vendor/ automáticamente
// Forzar el uso (o no uso) de vendor
// go build -mod=vendor
// go build -mod=mod
Para builds reproducibles sin acceso a internet (CI aislado, auditorías de seguridad, despliegues offline). En equipos pequeños con CI conectado, el caché de proxy.golang.org + go.sum ya cubre la mayoría de necesidades sin inflar el repo.
Flujo típico de trabajo
// 1. Inicializar el proyecto
// mkdir mi-app && cd mi-app
// go mod init github.com/fherrera/mi-app
// 2. Escribir código que importa algo
// echo 'package main
// import (
// "fmt"
// "github.com/google/uuid"
// )
// func main() { fmt.Println(uuid.New()) }
// ' > main.go
// 3. Descargar y resolver dependencias
// go mod tidy
// 4. Compilar y ejecutar
// go run .
// 5. Construir el binario
// go build -o mi-app
a1b2c3d4-e5f6-7890-abcd-ef1234567890Con esto tienes el ciclo completo: inicializar, añadir dependencias, mantener go.mod/go.sum limpios y compilar binarios reproducibles.