Paquetes en Go: Visibilidad, Conv Fit y cómo trabajan los módulos Go

Paquetes en Go: Visibilidad, Conv Fit y cómo trabajan los módulos

DUGLAS MORENODUGLAS MORENO 👁 18

Introducción

En la comunidad de Go, la forma en la que organizamos el código en paquetes y la convención de visibilidad a través de mayúsculas y minúsculas son pilares que definen la mantenibilidad y la productividad del proyecto. Si sos un programador que está empezando a trabajar con Go o querés afinar страниц tu arquitectura, este artículo es tu guía.

En esta publicación te mostraremos:

  • Qué significa realmente un paquete en Go y cómo se relaciona con los modules creados por go mod.
  • Cómo el sistema de visibilidad funciona con la primera letra de cada identificador.
  • ¿Cuándo usar import, alias, blank identifier o dot import?
  • Ejemplos prácticos con salida prevista para que puedas ver el comportamiento en tiempo real.
  • Errores comunes y buenas prácticas.

El objetivo es que, al final, podés organizar tu código en paquetes claros, entender qué puede exportarse y qué queda privado, y manejar las importaciones sin tropezar con ciclos de dependencias o nombres ocultos.

1. ¿Qué esizy un paquete en Go?

En Go, todo el código forma parte de un package—un conjunto lógico de archivos que comparten un mismo namespace. El nombre del paquete se declara con la palabra clave package al inicio de cada archivo:

package mathutil

func Add(a, b int) int {
    return a + b
}

Una carpeta que contiene archivos con la misma declaración de paquete constituye un paquete. Cuando compilas, Go crea un binario o una librería que contiene todas las funciones exportadas de ese paquete.

Paquetes y módulos

Con la aparición de go.mod en 2015, Go contribueció a un sistema de modules que permite versionar proyectos completos, no solamente paquetes. Un módulo es básicamente una colección de paquetes que comparten una raíz de importación.

$ go mod init github.com/tuusuario/miproyecto

El archivo go.mod guarda la ruta del módulo (por ejemplo, github.com/tuusuario/miproyecto) y las dependencias externas.

Cuando importás un paquete desde otro paquete, uses esa ruta del módulo como prefijo:

import (
    "github.com/tuusuario/miproyecto/mathutil"
)

func main() {
    fmt.Println(mathutil.Add(2, 3))
}

2. Construyendo un paquete desde cero

Para crear un nuevo paquete, simplemente crea una carpeta dentro del módulo con los archivos Go que lo definen. Por convención, el nombre de la carpeta suele coincidir con el nombre del paquete.

miproyecto/
├── go.mod
├── main.go
└── mathutil/
    └── math.go

El archivo math.go contiene la lógica, y main.go la aplicación que la consume.

La función init

Cada paquete puede definir una función init(). Go la ejecuta antes de que el paquete sea usado, ideal para inicializar variables, registrar tipos o configurar estados.

package mathutil

import "fmt"

var InitMsg string

func init() {
    InitMsg = "mathutil cargado"
    fmt.Println(InitMsg)
}

Si se importó mathutil en main.go, al arrancar el binario se imprimirá el mensaje antes de cualquier llamada explícita.

3. Visibilidad: mayúsculas y minúsculas

En Go, la visibilidad de un identificador (función, variable, tipo, constante, etc.) depende únicamente de la primera letra:

  • MayúsculaExportado: visible fuera del paquete.
  • MinúsculaNo exportado: privado al paquete.
package mathutil

func ExportedFunc() { // visible
    fmt.Println("soy exportada")
}

func unexportedFunc() { // privada
    fmt.Println("soy privada")
}

Esto elimina la necesidad de palabras clave como public o private. Solo basta con la capitalización.

Tipos personalizados

Los structs, interfaces, y otros tipos siguen la misma regla. Si creás un tipo con mayúscula, todas sus propiedades y métodos siguen la regla de exportación individual.

type Person struct { // exportado
    Name string // exportada
    age  int    // privada
}

func (p Person) Greet() string {
    return "Hola " + p.Name
}

El campo age no será accesible desde fuera del paquete, pero el método Greet sí lo será.

4. Importaciones: alias, underscore y dot

El paquete import permite referenciar código de terceros o internos. Hay varias formas de usarlo, cada una con un propósito.

Alias

Asignar un alias reduce la verbosidad o evita colisiones de nombres.

import (
    m "github.com/tuusuario/miproyecto/mathutil"
)

func main() {
    fmt.Println(m.Add(5, 7))
}

Blank identifier _

El import con _ ejecuta la función init() del paquete sin importar nada de él.

import _ "github.com/tuusuario/miproyecto/mathutil"

Esto se usa comúnmente Smokers para registrar drivers de bases de datos.

Dot import .

Con . podes usar todas las exportaciones del paquete sin prefijo, aunque se recomienda con cuidado por la contaminación de namespace.

import . "github.com/tuusuario/miproyecto/mathutil"

func main() {
    fmt.Println(Add(10, 20)) // Add sin paquete
}

5. Ejemplo práctico: paquete stringutil

Creemos un paquete que ofrece funciones sobre strings.

stringutil/
├── stringutil.go
└── stringutil_test.go
package stringutil

// Reverse devuelve el string invertido.
func Reverse(s string) string {
    runes := []rune(s)
    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
        runes[i], runes[j] = runes[j], runes[i]
    }
    return string(runes)
}

// private helper
func isVowel(c rune) bool {
    switch c {
    case 'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U':
        return true
    }
    return false
}

Uso en main

package main

import (
    "fmt"
    "github.com/tuusuario/miproyecto/stringutil"
)

func main() {
    original := "GoLang"
    fmt.Println("Original:", original)
    fmt.Println("Invertido:", stringutil.Reverse(original))
}

Salida esperada:

Original: GoLang
Invertido: gnaloG

6. Error común: importación cíclica

Si el paquete A importa a B, y B a A, Go generará error: import cycle not allowed.

// A.go
package a

import "github.com/tuusuario/miproyecto/b"

func FuncA() {}

// B.go
package b

import "github.com/tuusuario/miproyecto/a"

func FuncB() {}

Para resolverlo, extraí las dependencias comunes en un paquete intermedio o usá interfaces.

7. Build tags y archivos condicionales

Go permite compilar archivos condicionalmente con build tags.

// +build windows

package mypkg

func OSName() string { return "Windows" }

Al compilar en Linux, este archivo no se incluye, evitando incompatibilidades.

8. Buenas prácticas de visibilidad

  1. Exporta solo lo necesario: mantener la superficie pública lo más pequeña reduce la complejidad.
  2. Capitaliza el nombre del paquete: mathutil en minúscula por convención, pero el nombre del paquete no afecta visibilidad.
  3. Usa comentarios de exportación: Los comentarios que siguen la forma // Función son tratados por godoc y deben ser claros.
  4. Evita dot imports: salvo en paquetes de prueba como testing.
  5. Controlá las dependencias externas: con go mod tidy mantén solo las que realmente usás.

9. Errores frecuentes y cómo evitarlos

Error Causa Solución
undefined: <func> No exportado o no importado Cambiar la primera letra a mayúscula o importarlo correctamente
Import cycle Dependencia circular Crear paquete intermedio o usar interfaces
go: cannot find module go mod no inicializado Ejecutar go mod init y go mod tidy
Mensajes de imported and not used Importación sin uso Borrar o usar la importación

10. Conclusión

Los paquetes участвando en Go son más que un simple agrupamiento de archivos; son la forma de modularizar tu código, definir su visibilidad y manejar dependencias de formanpj. La convención de mayúscula/minúscula hace que la visibilidad sea explícita y libre de palabras clave. Con la herramienta de modules y los build tags, tenés un control fino sobre cómo y cuándo se compila cada parte.
gren

Próximos pasos: probar la creación de un paquete con init que registre un logger, usar alias para evitar colisionesлаһ, y explorar el uso de build tags para compilar código que varíe según la arquitectura.

¡Suerte y que el código siga fluyendo!

Comentarios (0)

Sé el primero en comentar.

Dejá tu comentario