Cuando un proyecto Go crece y se descompone en varios módulos (una librería compartida, un servicio backend, un worker, una herramienta CLI), el desarrollo simultáneo presenta una fricción conocida: cada cambio en la librería obliga a publicar una versión intermedia y actualizar manualmente las referencias en los consumidores. Los workspaces eliminan esa fricción permitiendo que varios módulos locales se compilen y testeen como un conjunto.
El problema que resuelve go.work
Antes de los workspaces, la solución clásica para desarrollar un módulo y sus consumidores en paralelo era añadir directivas replace en el go.mod de cada consumidor, apuntando a la copia local de la librería. Ese enfoque tenía varios inconvenientes:
- 1. Ensuciaba el
go.modcon rutas absolutas que no debían llegar al repositorio. - 2. Era fácil olvidarse de eliminar el
replaceantes de publicar. - 3. No escalaba cuando había varios consumidores compartiendo la misma librería local.
El fichero go.work aísla esta configuración de desarrollo en un fichero independiente que no se publica y que sustituye a los replace temporales:
flowchart LR
A[go.work workspace] --> B[modulo app]
A --> C[modulo libreria]
A --> D[modulo worker]
B -->|import| C
D -->|import| C
C -->|publicado| E[Repositorio remoto]
Con un workspace activo, el comando go build, go test o go run detecta automáticamente qué módulos locales debe usar en lugar de descargar dependencias remotas.
Crear un workspace
Un workspace nace con go work init indicando las carpetas de los módulos que quieres combinar. El comando crea un fichero go.work en el directorio actual:
# Estructura inicial
mis-proyectos/
libreria-comun/
go.mod
util.go
app-web/
go.mod
main.go
worker-procesos/
go.mod
worker.go
# Crear workspace incluyendo los tres módulos
cd mis-proyectos
go work init ./libreria-comun ./app-web ./worker-procesos
El go.work resultante tiene una sintaxis muy similar a go.mod:
go 1.24
use (
./libreria-comun
./app-web
./worker-procesos
)
A partir de este momento, cualquier comando Go ejecutado dentro del árbol del workspace reconoce los módulos locales como si fuesen dependencias remotas. Un import "github.com/acme/libreria-comun" en app-web se resuelve al fuente local en lugar de descargar una versión publicada.
El fichero
go.workno se debe versionar normalmente. Es una configuración de desarrollo personal. Para facilitar a otros desarrolladores la reconstrucción del workspace se proporciona un script de arranque o una entrada en elMakefile, pero el fichero se añade a.gitignore.
Añadir y quitar módulos
Los subcomandos go work use y go work edit permiten modificar el workspace sin editar el fichero a mano. Para incorporar un nuevo módulo se añade con use:
# Clonar un nuevo repositorio en la carpeta
git clone https://github.com/acme/libreria-extra ./libreria-extra
# Incorporarla al workspace
go work use ./libreria-extra
Para sacar un módulo del workspace sin borrar sus ficheros, se usa use -r con la carpeta, o se edita el go.work directamente eliminando la línea. Mientras el módulo está fuera del workspace, sus cambios locales se ignoran y Go vuelve a usar la versión publicada referenciada en go.mod.
Armonizar versiones con go work sync
Cuando varios módulos del workspace dependen de una tercera librería, Go puede seleccionar distintas versiones en cada uno. El comando go work sync armoniza esas versiones copiando al go.mod de cada módulo la versión mínima común que satisfaga a todos:
go work sync
Este comando evita discrepancias sutiles donde una librería se compila contra una versión en local y otra distinta al publicar. Conviene lanzarlo antes de enviar un pull request para dejar los go.mod en un estado coherente.
Workspaces y testing
Los tests funcionan de la manera habitual. La ventaja es que un test en app-web que consuma libreria-comun ejecuta el código fuente actual del módulo local. Si detectas un bug durante el desarrollo de app-web, puedes corregir libreria-comun, volver a ejecutar los tests en app-web y validar el fix sin una publicación intermedia.
# Desde la raíz del workspace
go test ./libreria-comun/...
go test ./app-web/...
go test ./worker-procesos/...
# O, más breve, cubriendo todo lo del workspace
go test ./...
Cuando lances los tests desde la raíz, ./... incluye todos los paquetes de todos los módulos del workspace. Esto resulta útil para scripts de CI locales que quieran validar la integración antes de abrir un PR.
Publicar sin romper consumidores
Un flujo de trabajo saludable con workspaces sigue esta secuencia cuando vas a liberar una nueva versión de la librería:
- 1. Consolidar el cambio en
libreria-comuncon los tests correspondientes. - 2. Verificar que
app-webyworker-procesossiguen funcionando usando el workspace. - 3. Publicar
libreria-comuncon un tag semántico (v1.3.0). - 4. Actualizar cada consumidor con
go get github.com/acme/libreria-comun@v1.3.0. - 5. Opcionalmente, eliminar la entrada del workspace si ya no necesitas editarla en paralelo.
Este flujo elimina la necesidad de replace en go.mod. El fichero de cada consumidor queda limpio y publicable.
Diferencias entre go.work y replace
Aunque go work use y replace cumplen funciones parecidas, tienen propósitos distintos:
-
1.
go.workes para desarrollo multi-módulo temporal, vive fuera del repositorio y no afecta a quien consuma el código publicado. -
2.
replaceengo.modes para sustituciones permanentes de dependencias (fork privado, bypass de versión rota). Se incluye en el repositorio y afecta a todos los consumidores.
Si tu caso es "quiero editar dos repos en paralelo durante unas horas", usa go.work. Si tu caso es "dependo de un fork permanente de una librería", usa replace.
Comandos esenciales
Un resumen compacto de los comandos que convendrá recordar:
# Inicializar un workspace
go work init ./mod1 ./mod2
# Añadir un módulo existente
go work use ./mod3
# Ver qué módulos están en el workspace
go work edit -json
# Sincronizar versiones de dependencias compartidas
go work sync
# Desactivar el workspace para un comando puntual
GOWORK=off go build ./...
La variable de entorno GOWORK=off resulta útil en CI para reproducir exactamente el comportamiento que verá un consumidor externo que no disponga del workspace. Debe estar establecida cuando se construyen artefactos oficiales o se publican módulos.
Con
go.workpresente,go mod tidymodifica elgo.moddel módulo actual como siempre, pero resuelve las referencias locales en el momento de compilar. Esto te permite iterar rápido sin mezclar ficheros.
Estructura recomendada para proyectos multi-módulo
Una organización habitual para un proyecto Go compuesto por varios módulos distribuye el código en repositorios separados o en un monorepo con carpetas hermanas. Un ejemplo típico de empresa con varios servicios:
mi-empresa/
libreria-modelos/ # tipos compartidos del dominio
go.mod
libreria-clientes/ # SDK interno para consumir APIs
go.mod
servicio-pedidos/ # microservicio HTTP
go.mod
servicio-facturacion/ # microservicio HTTP
go.mod
worker-emails/ # consumidor de colas
go.mod
go.work # workspace de desarrollo (no versionado)
Los equipos trabajan en cada módulo de forma independiente, pero los desarrolladores que tocan funciones transversales (añadir un campo al modelo, ampliar el SDK) pueden activar el workspace y editar todo de una vez sin bloqueos.
Compatibilidad con IDE y herramientas
Los IDE modernos (GoLand, IntelliJ IDEA con el plugin Go) reconocen go.work automáticamente: abres la carpeta raíz del workspace y el IDE indexa los módulos referenciados, ofreciendo navegación cruzada entre ellos sin configuración adicional. Los análisis estáticos como go vet, staticcheck y golangci-lint respetan la selección del workspace cuando se lanzan desde la raíz con ./....
De este modo, la experiencia de desarrollo se asemeja a la de un monorepo clásico, pero conservando las ventajas de tener módulos independientes con sus propios ciclos de publicación y versiones semánticas.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Go es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Go
Explora más contenido relacionado con Go y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Crear un go.work con go work init, añadir módulos locales con go work use, compartir dependencias entre módulos sin replace y aplicar go work sync para armonizar versiones.