Paquetes, Modulos y Herramientas en Go — Curso Go #8
Introduccion: el sistema de paquetes de Go

Bienvenido a la parte 8 de nuestro curso de Go para principiantes. En este articulo aprenderemos como Go organiza el codigo en paquetes, como gestionar dependencias con modulos, y como aprovechar las herramientas que Go incluye de forma nativa.
En Go, todo archivo pertenece a un paquete. Un paquete es simplemente un directorio que contiene uno o mas archivos .go con la misma declaracion package en la primera linea. Los paquetes son la unidad fundamental de organizacion y reutilizacion de codigo.
1// Archivo: main.go
2package main // Este archivo pertenece al paquete "main"
3
4import "fmt" // Importar el paquete "fmt" de la biblioteca estandar
5
6func main() {
7 fmt.Println("Hola desde el paquete main")
8}
main es especial. Es el punto de entrada de tu programa y debe contener una funcion main(). Cuando ejecutas go run o go build, Go busca este paquete para crear el ejecutable.
Nombres exportados: la convencion de mayusculas
En Go, la visibilidad de un nombre se determina por su primera letra. Esta es una de las convenciones mas simples y elegantes del lenguaje:
- Mayuscula inicial = exportado (publico): accesible desde otros paquetes. Ejemplo:
fmt.Println,math.Pi - Minuscula inicial = no exportado (privado): solo accesible dentro del mismo paquete. Ejemplo:
internal.helper
1package calculadora
2
3// Sumar es exportada - otros paquetes pueden usarla
4func Sumar(a, b int) int {
5 return a + b
6}
7
8// validar NO es exportada - solo se usa dentro de este paquete
9func validar(n int) bool {
10 return n >= 0
11}
12
13// Pi es una constante exportada
14const Pi = 3.14159
15
16// maxIntentos es una constante privada
17const maxIntentos = 5
public, private o protected como Java o C#. La primera letra lo dice todo. Es simple, consistente y no hay ambiguedades.
Go Modules: go.mod, go.sum y gestion de dependencias
Los Go Modules son el sistema oficial de gestion de dependencias desde Go 1.11. Cada proyecto Go es un modulo, definido por un archivo go.mod en la raiz del proyecto.
Inicializar un modulo
1# Crear un directorio para tu proyecto
2mkdir mi-proyecto
3cd mi-proyecto
4
5# Inicializar el modulo
6go mod init github.com/tu-usuario/mi-proyecto
7
8# Esto crea el archivo go.mod
El archivo go.mod generado se ve asi:
1module github.com/tu-usuario/mi-proyecto
2
3go 1.22
Agregar y limpiar dependencias
1# Descargar una dependencia
2go get github.com/gin-gonic/gin
3
4# Limpiar dependencias no usadas y descargar las que faltan
5go mod tidy
6
7# Ver todas las dependencias
8go list -m all
9
10# Actualizar una dependencia
11go get -u github.com/gin-gonic/gin
12
13# Actualizar todas las dependencias
14go get -u ./...
El archivo go.sum se genera automaticamente y contiene los hashes criptograficos de cada dependencia. Nunca lo edites manualmente. Su proposito es garantizar que las dependencias no han sido modificadas.
go.sum debe estar en tu repositorio Git. Garantiza builds reproducibles y seguridad en la cadena de dependencias.
Crear tus propios paquetes
Veamos como crear y usar tus propios paquetes dentro de un proyecto. Supongamos esta estructura:
1mi-proyecto/
2 go.mod
3 main.go
4 utils/
5 math.go
6 strings.go
1// Archivo: utils/math.go
2package utils
3
4import "math"
5
6// DistanciaEuclidiana calcula la distancia entre dos puntos 2D
7func DistanciaEuclidiana(x1, y1, x2, y2 float64) float64 {
8 dx := x2 - x1
9 dy := y2 - y1
10 return math.Sqrt(dx*dx + dy*dy)
11}
12
13// EsPrimo verifica si un numero es primo
14func EsPrimo(n int) bool {
15 if n < 2 {
16 return false
17 }
18 for i := 2; i*i <= n; i++ {
19 if n%i == 0 {
20 return false
21 }
22 }
23 return true
24}
1// Archivo: utils/strings.go
2package utils
3
4import "strings"
5
6// Capitalizar pone en mayuscula la primera letra de cada palabra
7func Capitalizar(s string) string {
8 return strings.Title(s)
9}
10
11// ContarPalabras cuenta las palabras en un texto
12func ContarPalabras(s string) int {
13 campos := strings.Fields(s)
14 return len(campos)
15}
1// Archivo: main.go
2package main
3
4import (
5 "fmt"
6 "github.com/tu-usuario/mi-proyecto/utils"
7)
8
9func main() {
10 // Usar funciones de nuestro paquete utils
11 dist := utils.DistanciaEuclidiana(0, 0, 3, 4)
12 fmt.Printf("Distancia: %.2f\n", dist) // 5.00
13
14 fmt.Println(utils.EsPrimo(17)) // true
15 fmt.Println(utils.ContarPalabras("Hola mundo desde Go")) // 4
16}
Biblioteca estandar: los paquetes mas usados
Go tiene una biblioteca estandar increiblemente rica. Aqui estan los paquetes que usaras con mas frecuencia:
| Paquete | Uso | Ejemplo |
|---|---|---|
fmt | Formateo y salida | fmt.Printf("Hola %s", nombre) |
os | Sistema operativo, archivos, env | os.Getenv("HOME") |
io | Interfaces de I/O | io.Copy(dst, src) |
strings | Manipulacion de strings | strings.Contains(s, "go") |
strconv | Conversion de tipos | strconv.Atoi("42") |
time | Fecha y hora | time.Now() |
math | Funciones matematicas | math.Sqrt(16) |
sort | Ordenamiento | sort.Ints(nums) |
net/http | Cliente y servidor HTTP | http.Get(url) |
encoding/json | Serializar/deserializar JSON | json.Marshal(data) |
1package main
2
3import (
4 "fmt"
5 "math"
6 "sort"
7 "strings"
8 "strconv"
9 "time"
10)
11
12func main() {
13 // strings
14 fmt.Println(strings.ToUpper("hola")) // HOLA
15 fmt.Println(strings.Contains("golang", "go")) // true
16 fmt.Println(strings.Split("a,b,c", ",")) // [a b c]
17 fmt.Println(strings.Join([]string{"x","y"}, "-")) // x-y
18
19 // strconv
20 num, _ := strconv.Atoi("42")
21 fmt.Println(num + 8) // 50
22 texto := strconv.Itoa(100)
23 fmt.Println(texto) // "100"
24
25 // math
26 fmt.Println(math.Max(10, 20)) // 20
27 fmt.Println(math.Ceil(3.2)) // 4
28
29 // sort
30 nums := []int{5, 3, 8, 1, 9}
31 sort.Ints(nums)
32 fmt.Println(nums) // [1 3 5 8 9]
33
34 // time
35 ahora := time.Now()
36 fmt.Println(ahora.Format("2006-01-02 15:04:05"))
37}
Paquetes de terceros: go get
Ademas de la biblioteca estandar, puedes usar miles de paquetes creados por la comunidad. El comando go get descarga e instala paquetes de repositorios remotos.
1# Instalar un paquete popular para APIs web
2go get github.com/gin-gonic/gin
3
4# Instalar un ORM
5go get gorm.io/gorm
6go get gorm.io/driver/sqlite
7
8# Instalar un paquete para variables de entorno
9go get github.com/joho/godotenv
Despues de instalar, puedes importar y usar el paquete normalmente:
1package main
2
3import (
4 "github.com/gin-gonic/gin"
5)
6
7func main() {
8 r := gin.Default()
9 r.GET("/", func(c *gin.Context) {
10 c.JSON(200, gin.H{"message": "Hola mundo"})
11 })
12 r.Run(":8080")
13}
pkg.go.dev, que es el repositorio oficial de documentacion y paquetes de Go. Ahi puedes ver la documentacion, el codigo fuente, las versiones y las dependencias de cualquier paquete.
Go toolchain: las herramientas que ya tienes
Go viene con un conjunto completo de herramientas integradas. No necesitas instalar nada adicional para formatear, analizar, testear o compilar tu codigo.
1# Compilar y ejecutar
2go run main.go # Compilar y ejecutar en un paso
3go build -o mi-app . # Compilar a un binario ejecutable
4
5# Formatear codigo (obligatorio en Go)
6go fmt ./... # Formatea todos los archivos del proyecto
7gofmt -w main.go # Formatea un archivo especifico
8
9# Analisis estatico
10go vet ./... # Detecta errores comunes que el compilador no encuentra
11
12# Testing
13go test ./... # Ejecutar todos los tests
14go test -v ./... # Tests con salida detallada
15go test -cover ./... # Tests con reporte de cobertura
16go test -race ./... # Tests con detector de race conditions
17
18# Documentacion
19go doc fmt # Ver documentacion del paquete fmt
20go doc fmt.Println # Ver documentacion de una funcion especifica
21
22# Dependencias
23go mod init modulo # Inicializar modulo
24go mod tidy # Limpiar dependencias
25go get paquete # Descargar paquete
go fmt formatea automaticamente tu codigo. No hay debates sobre tabs vs espacios, llaves en la misma linea o en la siguiente, etc. Esto elimina discusiones innecesarias en los equipos.
Estructura de proyecto recomendada
Go no impone una estructura de directorios rigida, pero la comunidad ha establecido convenciones que la mayoria de proyectos siguen:
1mi-proyecto/
2 cmd/ # Puntos de entrada (ejecutables)
3 server/
4 main.go
5 cli/
6 main.go
7 internal/ # Codigo privado (no importable por otros proyectos)
8 database/
9 connection.go
10 auth/
11 jwt.go
12 pkg/ # Codigo publico (importable por otros proyectos)
13 validator/
14 email.go
15 config/ # Archivos de configuracion
16 config.go
17 config.json
18 go.mod
19 go.sum
20 README.md
Las carpetas mas importantes:
- cmd/: Contiene los puntos de entrada de la aplicacion. Cada subdirectorio es un ejecutable diferente.
- internal/: Paquetes privados del proyecto. Go impide que otros modulos importen desde
internal/. - pkg/: Paquetes publicos que otros proyectos pueden importar. Es opcional.
Trabajar con JSON: encoding/json y struct tags
El paquete encoding/json permite convertir entre structs de Go y JSON. Usa struct tags para controlar como se serializan los campos.
1package main
2
3import (
4 "encoding/json"
5 "fmt"
6)
7
8type Usuario struct {
9 Nombre string "json:\"nombre\""
10 Email string "json:\"email\""
11 Edad int "json:\"edad\""
12 Activo bool "json:\"activo\""
13 Roles []string "json:\"roles,omitempty\""
14 Password string "json:\"-\"" // Nunca se serializa
15}
16
17func main() {
18 // Struct a JSON (Marshal)
19 usuario := Usuario{
20 Nombre: "Ana Garcia",
21 Email: "[email protected]",
22 Edad: 28,
23 Activo: true,
24 Roles: []string{"admin", "editor"},
25 Password: "secreto123",
26 }
27
28 jsonBytes, err := json.MarshalIndent(usuario, "", " ")
29 if err != nil {
30 fmt.Println("Error:", err)
31 return
32 }
33 fmt.Println(string(jsonBytes))
34 // {
35 // "nombre": "Ana Garcia",
36 // "email": "[email protected]",
37 // "edad": 28,
38 // "activo": true,
39 // "roles": ["admin", "editor"]
40 // }
41 // Nota: Password no aparece gracias a json:"-"
42
43 // JSON a Struct (Unmarshal)
44 jsonStr := "{\"nombre\":\"Carlos\",\"email\":\"[email protected]\",\"edad\":35,\"activo\":true}"
45 var usuario2 Usuario
46 err = json.Unmarshal([]byte(jsonStr), &usuario2)
47 if err != nil {
48 fmt.Println("Error:", err)
49 return
50 }
51 fmt.Printf("Nombre: %s, Email: %s\n", usuario2.Nombre, usuario2.Email)
52}
encoding/json lo ignora completamente. Usa struct tags para controlar el nombre en el JSON.
Variables de entorno con os.Getenv
Las variables de entorno son la forma estandar de configurar aplicaciones en produccion. Go las lee con os.Getenv.
1package main
2
3import (
4 "fmt"
5 "os"
6)
7
8func getEnv(key, defaultValue string) string {
9 value := os.Getenv(key)
10 if value == "" {
11 return defaultValue
12 }
13 return value
14}
15
16func main() {
17 // Leer variables de entorno con valores por defecto
18 puerto := getEnv("PORT", "8080")
19 dbHost := getEnv("DB_HOST", "localhost")
20 dbName := getEnv("DB_NAME", "mi_base_datos")
21 entorno := getEnv("GO_ENV", "development")
22
23 fmt.Printf("Servidor en puerto: %s\n", puerto)
24 fmt.Printf("Base de datos: %s@%s\n", dbName, dbHost)
25 fmt.Printf("Entorno: %s\n", entorno)
26}
Ejemplo practico: herramienta CLI que lee configuracion de JSON
Vamos a construir una herramienta de linea de comandos que lee una configuracion desde un archivo JSON, procesa la informacion y muestra un reporte.
1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "os"
7 "sort"
8 "strings"
9)
10
11// Config representa la configuracion de la aplicacion
12type Config struct {
13 AppName string "json:\"app_name\""
14 Version string "json:\"version\""
15 Port int "json:\"port\""
16 Debug bool "json:\"debug\""
17 AllowedHosts []string "json:\"allowed_hosts\""
18 Database DBConfig "json:\"database\""
19}
20
21type DBConfig struct {
22 Host string "json:\"host\""
23 Port int "json:\"port\""
24 Name string "json:\"name\""
25 User string "json:\"user\""
26 SSLMode string "json:\"ssl_mode\""
27}
28
29func cargarConfig(ruta string) (Config, error) {
30 var config Config
31
32 data, err := os.ReadFile(ruta)
33 if err != nil {
34 return config, fmt.Errorf("no se pudo leer el archivo: %w", err)
35 }
36
37 err = json.Unmarshal(data, &config)
38 if err != nil {
39 return config, fmt.Errorf("JSON invalido: %w", err)
40 }
41
42 return config, nil
43}
44
45func validarConfig(config Config) []string {
46 var errores []string
47
48 if config.AppName == "" {
49 errores = append(errores, "app_name es obligatorio")
50 }
51 if config.Port < 1 || config.Port > 65535 {
52 errores = append(errores, "port debe estar entre 1 y 65535")
53 }
54 if config.Database.Host == "" {
55 errores = append(errores, "database.host es obligatorio")
56 }
57 if config.Database.Name == "" {
58 errores = append(errores, "database.name es obligatorio")
59 }
60
61 return errores
62}
63
64func mostrarReporte(config Config) {
65 separador := strings.Repeat("=", 50)
66
67 fmt.Println(separador)
68 fmt.Printf(" Aplicacion: %s v%s\n", config.AppName, config.Version)
69 fmt.Println(separador)
70 fmt.Printf(" Puerto: %d\n", config.Port)
71 fmt.Printf(" Debug: %v\n", config.Debug)
72
73 if len(config.AllowedHosts) > 0 {
74 sort.Strings(config.AllowedHosts)
75 fmt.Printf(" Hosts: %s\n", strings.Join(config.AllowedHosts, ", "))
76 }
77
78 fmt.Println()
79 fmt.Println(" Base de datos:")
80 fmt.Printf(" Host: %s:%d\n", config.Database.Host, config.Database.Port)
81 fmt.Printf(" Nombre: %s\n", config.Database.Name)
82 fmt.Printf(" Usuario: %s\n", config.Database.User)
83 fmt.Printf(" SSL: %s\n", config.Database.SSLMode)
84 fmt.Println(separador)
85}
86
87func main() {
88 // Determinar la ruta del archivo de configuracion
89 ruta := "config.json"
90 if len(os.Args) > 1 {
91 ruta = os.Args[1]
92 }
93
94 fmt.Printf("Cargando configuracion desde: %s\n\n", ruta)
95
96 config, err := cargarConfig(ruta)
97 if err != nil {
98 fmt.Fprintf(os.Stderr, "Error: %v\n", err)
99 os.Exit(1)
100 }
101
102 // Validar la configuracion
103 errores := validarConfig(config)
104 if len(errores) > 0 {
105 fmt.Println("Errores de validacion:")
106 for _, e := range errores {
107 fmt.Printf(" - %s\n", e)
108 }
109 os.Exit(1)
110 }
111
112 // Mostrar el reporte
113 mostrarReporte(config)
114 fmt.Println("Configuracion cargada correctamente.")
115}
Y el archivo config.json de ejemplo:
1// config.json
2{
3 "app_name": "Mi API",
4 "version": "1.0.0",
5 "port": 8080,
6 "debug": true,
7 "allowed_hosts": ["localhost", "api.ejemplo.com"],
8 "database": {
9 "host": "localhost",
10 "port": 5432,
11 "name": "mi_base_datos",
12 "user": "admin",
13 "ssl_mode": "disable"
14 }
15}
main.go y el JSON como config.json en el mismo directorio. Luego ejecuta go run main.go. Tambien puedes pasar una ruta diferente: go run main.go /ruta/a/otro/config.json.
Resumen y proximo articulo
En este articulo aprendiste sobre el ecosistema de herramientas y organizacion de Go:
- Paquetes: la unidad fundamental de organizacion con
package - Nombres exportados: la convencion de mayuscula inicial para visibilidad
- Go Modules:
go.mod,go.sum,go mod initygo mod tidy - Crear paquetes propios: organizar tu codigo en directorios reutilizables
- Biblioteca estandar: fmt, os, strings, strconv, time, sort, net/http, encoding/json
- Paquetes de terceros: instalar con
go get - Toolchain: go build, go run, go test, go fmt, go vet, go doc
- Estructura de proyecto: cmd/, internal/, pkg/
- JSON: encoding/json con struct tags
- Variables de entorno: os.Getenv para configuracion
Con estos 8 articulos ya tienes una base solida en Go. Puedes crear programas concurrentes, organizar tu codigo en paquetes, gestionar dependencias y usar las herramientas del ecosistema. El siguiente paso es practicar construyendo proyectos reales.
Comments
Sign in to leave a comment
No comments yet. Be the first!