← Voltar para o blog

Go Workspaces com go.work: Guia para Múltiplos Módulos

Aprenda a usar go work e go.work para desenvolver vários módulos Go juntos, organizar monorepos, testar mudanças locais e evitar replaces frágeis.

Resposta rápida: use Go Workspaces quando você precisa alterar e testar dois ou mais módulos Go ao mesmo tempo. Na raiz do trabalho, rode go work init, adicione os módulos com go work use e execute os comandos normalmente. O arquivo go.work faz o toolchain resolver os imports pelo código local, sem publicar versões provisórias e sem espalhar replace ../outro-modulo pelos arquivos go.mod. Ele é especialmente útil em monorepos, na evolução conjunta de uma biblioteca e uma API e na reprodução local de mudanças que atravessam repositórios.

O recurso não transforma vários módulos em um único projeto. Cada diretório continua com seu próprio go.mod, suas dependências e seu ciclo de versão. O workspace é uma camada de desenvolvimento por cima desses módulos.

Quando usar go.work

Um workspace resolve um problema específico: o módulo app depende de lib, você está mudando os dois, mas a nova versão de lib ainda não existe em um proxy de módulos.

Sem workspace, as alternativas comuns são:

  1. publicar uma versão provisória da biblioteca;
  2. editar o go.mod da aplicação com um replace para um caminho local;
  3. copiar código entre projetos;
  4. esperar a mudança da biblioteca ser publicada antes de testar a aplicação.

Com go.work, o código local entra na resolução sem modificar o go.mod que será consumido por outras pessoas.

SituaçãoGo Workspace ajuda?Motivo
Monorepo com várias APIs e bibliotecasSimPermite desenvolver e testar módulos juntos
Biblioteca e aplicação em clones vizinhosSimLiga os módulos locais sem publicar uma versão
Projeto com um único móduloGeralmente nãoUm go.mod já resolve o caso
Dependência externa sem mudanças locaisNãoUse a versão normal declarada no go.mod
Fork permanente de uma dependênciaTalvezUm replace versionado pode expressar melhor a decisão
Pacotes internos de uma única aplicaçãoNão necessariamenteAntes de criar módulos, considere manter apenas pacotes

A última linha é importante. Nem todo diretório merece um módulo. Criar muitos go.mod aumenta o trabalho de versionamento, atualização e CI. Use múltiplos módulos quando existem fronteiras reais: publicação independente, consumidores diferentes, restrições de dependência ou ciclos de release separados.

Se você ainda está consolidando esses conceitos, comece pelo guia de Go Modules na prática e pela referência de módulos e dependências em Go.

Exemplo: uma API e uma biblioteca local

Imagine esta estrutura:

plataforma/
├── api/
│   ├── go.mod
│   └── main.go
└── saudacao/
    ├── go.mod
    └── saudacao.go

A biblioteca possui seu próprio módulo:

// saudacao/saudacao.go
package saudacao

import "fmt"

func Ola(nome string) string {
	return fmt.Sprintf("Olá, %s!", nome)
}
// saudacao/go.mod
module example.com/plataforma/saudacao

go 1.27

A API importa a biblioteca pelo caminho normal do módulo:

// api/main.go
package main

import (
	"fmt"

	"example.com/plataforma/saudacao"
)

func main() {
	fmt.Println(saudacao.Ola("Gopher"))
}
// api/go.mod
module example.com/plataforma/api

go 1.27

require example.com/plataforma/saudacao v0.0.0

A versão v0.0.0 representa aqui uma dependência ainda não publicada. Na raiz plataforma, crie o workspace:

cd plataforma
go work init ./api ./saudacao
go run ./api

O comando gera um arquivo parecido com este:

// go.work
go 1.27

use (
	./api
	./saudacao
)

A partir desse diretório ou de seus subdiretórios, o comando go encontra go.work, reconhece os dois módulos e usa a implementação local de saudacao.

Comandos essenciais do go work

go work init

Cria o arquivo inicial:

go work init

Você pode informar módulos no mesmo comando:

go work init ./api ./libs/auth ./libs/observabilidade

Os caminhos devem apontar para diretórios que possuem go.mod.

go work use

Adiciona ou remove módulos da diretiva use:

go work use ./libs/pagamentos
go work use -r ./services

Com -r, o comando percorre recursivamente o diretório e adiciona os módulos encontrados. Isso é prático em um monorepo, mas revise o diff: incluir ferramentas, exemplos ou módulos experimentais sem intenção pode deixar todos os comandos mais amplos e lentos.

Para remover uma diretiva explicitamente, use go work edit:

go work edit -dropuse=./libs/legado

Outra opção é apagar ou mover o diretório e rodar go work use novamente: o comando remove entradas cujos módulos já não existem.

go work edit

Edita o arquivo de forma programática. É útil em scripts que não devem manipular texto com sed:

go work edit -use=./libs/catalogo
go work edit -dropuse=./libs/legado
go work edit -json

A saída JSON ajuda ferramentas internas a inspecionar o workspace sem depender da formatação textual.

go work sync

Sincroniza informações da lista de build do workspace com os módulos:

go work sync

Esse comando pode atualizar requisitos nos arquivos go.mod dos módulos. Por isso, não o trate como uma operação invisível: execute, revise os diffs e rode os testes. Ele não substitui o go mod tidy de cada módulo.

Como o Go encontra o arquivo go.work

Ao executar um comando, o toolchain procura go.work no diretório atual e depois sobe pelos diretórios pais. Você pode confirmar qual arquivo está ativo com:

go env GOWORK

A saída pode ser:

/home/ana/projetos/plataforma/go.work

Se não houver workspace ativo, o valor fica vazio ou indica que ele foi desabilitado, dependendo do ambiente.

Para ignorar o workspace em um único comando:

GOWORK=off go test ./...

Também é possível apontar explicitamente para um arquivo:

GOWORK=/tmp/workspaces/release.work go test ./...

O caminho explícito deve ser absoluto. Na prática, GOWORK=off é o caso mais importante: ele verifica se um módulo não depende acidentalmente do contexto local.

go.work não substitui go.mod

O go.mod continua sendo a declaração do módulo. Ele informa:

  • o caminho público do módulo;
  • a versão mínima da linguagem e do toolchain aplicável;
  • as dependências necessárias;
  • exclusões, retrações ou substituições relevantes ao módulo.

O go.work informa quais módulos principais locais participam da operação atual. Ele não deve ser usado para esconder que uma aplicação esqueceu de declarar uma dependência.

Uma verificação útil é rodar os testes de duas maneiras:

# Integração com todos os módulos locais do workspace
go test ./...

# Módulo isolado, usando apenas seu go.mod
cd api
GOWORK=off go test ./...

O primeiro comando responde: “as mudanças locais funcionam juntas?”. O segundo responde: “o módulo publicado continua reproduzível sozinho?”. Em uma base com releases independentes, você precisa das duas respostas.

go.work ou replace: qual escolher?

Uma diretiva replace no go.mod pode mapear um módulo para outro caminho ou versão:

replace example.com/plataforma/saudacao => ../saudacao

Isso funciona, mas um caminho relativo local tem dois riscos:

  • pode ser commitado por acidente e quebrar o build de outra pessoa;
  • mistura uma conveniência do ambiente de desenvolvimento com o contrato do módulo.

O workspace evita esses problemas porque concentra a ligação local em go.work.

Use go.work quando a substituição existe porque você está desenvolvendo módulos juntos. Considere replace quando o próprio módulo precisa expressar uma troca controlada, por exemplo um fork temporário fixado em uma versão ou um caso de compatibilidade que deve valer para todos os colaboradores. Mesmo assim, documente a decisão e planeje sua remoção.

O que é go.work.sum

Assim como um módulo pode ter go.sum, o workspace pode gerar go.work.sum. Ele registra hashes necessários para dependências usadas no contexto do workspace que não estejam cobertas pelos arquivos go.sum dos módulos.

Não edite esse arquivo manualmente. O comando go o mantém conforme resolve e baixa módulos.

Se o go.work é versionado, normalmente faz sentido avaliar o go.work.sum no mesmo contexto e mantê-lo versionado para reproduzir a resolução. Se o workspace é estritamente pessoal e está no .gitignore, o arquivo de soma correspondente também costuma ser ignorado.

Devo commitar go.work?

Não existe uma resposta única. Use a propriedade do repositório como critério.

Versione o workspace quando

  • o repositório é um monorepo deliberado;
  • os mesmos módulos devem ser usados juntos por toda a equipe;
  • comandos e documentação assumem uma raiz comum;
  • o CI testa o conjunto como workspace;
  • a lista de módulos é parte da arquitetura do repositório.

Ignore o workspace quando

  • cada pessoa combina clones em caminhos diferentes;
  • o arquivo serve apenas para testar uma mudança entre repositórios independentes;
  • os módulos precisam ser validados prioritariamente como unidades isoladas;
  • a lista local inclui forks ou experimentos pessoais;
  • o repositório já definiu outra estratégia oficial de build.

Para ignorá-lo:

go.work
go.work.sum

A equipe Go alerta que versionar um go.work pode fazer o CI testar combinações de dependências locais diferentes daquelas que os consumidores realmente recebem. Isso não significa “nunca commite”. Significa que o workspace deve ser uma decisão de repositório, não um arquivo pessoal enviado sem revisão.

Estratégia de CI para múltiplos módulos

Em um monorepo, execute uma camada integrada e uma camada isolada.

1. Teste integrado pelo workspace

Na raiz:

go test ./...
go vet ./...

Isso detecta incompatibilidades entre mudanças locais antes que as versões sejam publicadas.

2. Teste cada módulo com GOWORK=off

Um script simples pode localizar os módulos:

find . -name go.mod -not -path '*/vendor/*' -print0 |
while IFS= read -r -d '' mod; do
	dir=$(dirname "$mod")
	echo "==> testando $dir"
	(
		cd "$dir"
		GOWORK=off go test ./...
		GOWORK=off go vet ./...
	)
done

Assim, um módulo não passa no CI apenas porque outro módulo local forneceu uma dependência que ainda não foi publicada ou declarada corretamente.

Para suítes maiores, combine isso com as flags do go test e mantenha -race, cobertura e testes de integração em jobs apropriados.

Padrão prático para mudanças entre repositórios

Workspaces também ajudam fora de monorepos. Suponha dois clones vizinhos:

~/codigo/
├── sdk-pagamentos/
└── checkout-api/

Crie um workspace fora dos repositórios:

mkdir -p ~/codigo/workspaces/checkout
cd ~/codigo/workspaces/checkout
go work init ../../sdk-pagamentos ../../checkout-api

Agora você pode mudar o SDK e testar a API contra o código local. Como o go.work ficou em uma pasta separada, nenhum dos repositórios precisa receber um arquivo temporário.

Antes de abrir os pull requests:

  1. rode os testes integrados com o workspace;
  2. rode GOWORK=off go test ./... em cada módulo;
  3. publique ou faça merge da biblioteca na ordem necessária;
  4. atualize a versão exigida pela aplicação;
  5. remova qualquer dependência de uma versão fictícia usada apenas no experimento.

Esse fluxo reduz o intervalo entre “acho que as APIs combinam” e “testei o consumidor real”.

Erros comuns com Go Workspaces

Criar módulos demais

Separar cada pacote em um módulo gera releases, dependências e pipelines sem benefício. Pacotes dentro do mesmo módulo já podem ser importados normalmente. Comece com um módulo e extraia outro quando houver uma fronteira de distribuição real.

Testar somente com o workspace ativo

O código passa localmente, mas falha depois de publicado porque o go.mod exige uma versão antiga. Sempre inclua uma validação com GOWORK=off quando os módulos têm vida independente.

Usar go.work para esconder dependências ausentes

Se o código importa um módulo, o go.mod correspondente deve declarar a dependência adequada. O workspace não é licença para manter metadados incompletos.

Commitar caminhos pessoais

Entradas como /home/joao/fork não funcionam na máquina de outra pessoa. Para workspaces compartilhados, prefira caminhos relativos dentro da estrutura do repositório.

Rodar go work sync sem revisar o diff

A sincronização pode alterar requisitos nos módulos. Trate-a como qualquer comando que edita go.mod: revise, execute go mod tidy onde necessário e valide o build.

Confundir workspace com ferramenta de release

O go.work ajuda a desenvolver módulos em conjunto, mas não define ordem de publicação, tags, changelog ou compatibilidade semântica. O release continua exigindo versões coerentes e dependências publicadas.

Checklist para adotar go.work

  1. Confirme que existem fronteiras reais entre os módulos.
  2. Crie o workspace com go work init e go work use.
  3. Verifique o arquivo ativo com go env GOWORK.
  4. Rode testes integrados na raiz.
  5. Rode testes isolados com GOWORK=off em cada módulo.
  6. Decida explicitamente se go.work e go.work.sum entram no Git.
  7. Evite replace com caminho local no go.mod publicado.
  8. Documente o comando padrão de teste para a equipe.
  9. Revise os diffs depois de go work sync.
  10. Garanta que releases usem versões reais entre módulos.

Perguntas frequentes

Para que serve o arquivo go.work?

Ele reúne módulos locais em um workspace. Durante o desenvolvimento, imports entre esses módulos apontam para o código local, sem exigir uma versão publicada ou um replace temporário em cada go.mod.

Devo commitar o go.work no Git?

Em um monorepo cujo fluxo oficial depende do workspace, pode fazer sentido. Para combinações pessoais de clones, forks locais ou módulos que devem funcionar isoladamente, normalmente é melhor ignorá-lo. Registre a escolha na documentação do repositório.

Qual é a diferença entre go.work e replace?

go.work configura o ambiente de desenvolvimento que reúne módulos locais. replace altera a resolução declarada dentro de um go.mod. Caminhos locais em replace são fáceis de esquecer e prejudicam a reprodução em outras máquinas.

O go.work substitui o go.mod?

Não. Cada módulo mantém seu próprio go.mod. O workspace apenas coordena quais módulos principais locais participam dos comandos atuais.

Como desativar temporariamente um workspace?

Use GOWORK=off antes do comando:

GOWORK=off go test ./...

Próximos passos

Fontes oficiais

Go Workspaces são mais úteis quando permanecem simples: módulos continuam independentes, o workspace acelera o desenvolvimento conjunto e o CI verifica as duas realidades. Se o time entende essa separação, go.work elimina versões provisórias e alterações locais frágeis sem transformar o monorepo em mágica difícil de reproduzir.