ProgramaciónDesarrollador Go Senior

¿Cuáles son las características del trabajo con variables globales en Go, cómo evitar su inicialización incorrecta y la competencia por el orden de inicialización (init order)? ¿Cuáles son las trampas típicas?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

En Go, las variables globales se inicializan en el orden de declaración de los archivos y la secuencia de sus funciones init. Esto afecta la corrección del acceso al estado global: si una variable en un paquete depende del valor de una variable de otro paquete, pero ese paquete aún no ha sido inicializado, se producirá un error o un comportamiento no evidente.

No se puede establecer un "orden de inicio" directo entre las funciones init de diferentes paquetes. El compilador determina este orden según el gráfico de importaciones.

  • Todas las variables globales se inicializan antes de la llamada a la función main.main().
  • No se recomienda realizar la inicialización del estado con excesivas dependencias durante el init del paquete!
  • Para la seguridad en hilo de las variables globales se utilizan sync.Once o mutexes en la inicialización compleja.

Ejemplo de inicialización diferida segura (lazy init):

var cfg *Config var once sync.Once func GetConfig() *Config { once.Do(func() { cfg = LoadConfigFromDisk() }) return cfg }

Pregunta capciosa.

Pregunta: ¿Las funciones init de diferentes paquetes pueden ejecutarse al mismo tiempo (en paralelo) al iniciar un programa en Go?

Respuesta correcta: No, las funciones init y las variables globales se ejecutan estrictamente de manera secuencial en el orden que el compilador determina al analizar las dependencias entre los paquetes. No hay ejecución paralela de las funciones init.


Ejemplos de errores reales por desconocer los detalles del tema.


Historia

En un gran proyecto, varios módulos cargaban configuraciones globales desde el disco a través de init(). Un módulo dependía de otro, pero debido a la reestructuración del gráfico go.mod, el orden de inicialización cambió — y de repente los valores de configuración resultaron vacíos. El error se manifestaba de manera aleatoria, dependiendo del orden de compilación, y solo se encontró después de analizar las dependencias y trasladar la inicialización a una función explícita.


Historia

En un servicio REST para el almacenamiento en caché de diccionarios se utilizaba un mapa global sin mutex. La inicialización comenzaba desde varios goroutines, que se iniciaban en init. Como resultado: carrera de datos, pánicos periódicos, datos no válidos dentro del mapa. Después de cambiar a sync.Once y realizar una llamada explícita a la inicialización, el problema desapareció.


Historia

Un logger global se creaba en la función init de uno de los paquetes auxiliares, sin embargo, otro paquete accedía a este logger también al inicio, antes de su inicialización. Como resultado, parte de los logs de inicio se perdía mientras el logger aún no existía. La solución: inyección de dependencias e inicialización explícita del logger antes de iniciar todos los servicios.