Cristhian Villegas
Backend10 min read1 views

Paquetes, Modulos y Herramientas en Go — Curso Go #8

Paquetes, Modulos y Herramientas en Go — Curso Go #8

Introduccion: el sistema de paquetes de Go

Logo 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.

go
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}
Regla importante: El paquete 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
go
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
Sin palabras clave: Go no necesita 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

bash
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:

go
1module github.com/tu-usuario/mi-proyecto
2
3go 1.22

Agregar y limpiar dependencias

bash
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.

Siempre incluye go.sum en tu repositorio: Aunque parece un archivo generado que no deberia versionarse, 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:

bash
1mi-proyecto/
2  go.mod
3  main.go
4  utils/
5    math.go
6    strings.go
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}
go
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}
go
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:

PaqueteUsoEjemplo
fmtFormateo y salidafmt.Printf("Hola %s", nombre)
osSistema operativo, archivos, envos.Getenv("HOME")
ioInterfaces de I/Oio.Copy(dst, src)
stringsManipulacion de stringsstrings.Contains(s, "go")
strconvConversion de tiposstrconv.Atoi("42")
timeFecha y horatime.Now()
mathFunciones matematicasmath.Sqrt(16)
sortOrdenamientosort.Ints(nums)
net/httpCliente y servidor HTTPhttp.Get(url)
encoding/jsonSerializar/deserializar JSONjson.Marshal(data)
go
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.

bash
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:

go
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}
Buscar paquetes: Puedes buscar paquetes de Go en 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.

bash
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 es obligatorio: A diferencia de otros lenguajes donde el formato es cuestion de preferencia, en Go todos usan el mismo formato. El comando 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:

bash
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.

go
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}
Los campos deben ser exportados: Solo los campos que empiezan con mayuscula se serializan/deserializan. Si un campo empieza con minuscula, 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.

go
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.

go
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:

go
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}
Ejecuta este ejemplo: Guarda el archivo Go como 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 init y go 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.

Share:
CV

Cristhian Villegas

Software Engineer specializing in Java, Spring Boot, Angular & AWS. Building scalable distributed systems with clean architecture.

Comments

Sign in to leave a comment

No comments yet. Be the first!

Related Articles