This entry is parte 6 de 12 in the series Golang

À medida que o código cresce e passa a depender de outros pacotes — internos ou externos — surge a necessidade de controlar essas dependências de forma consistente.

Historicamente, o Go utilizava o GOPATH como base para organização e resolução de pacotes. Esse modelo funcionava, mas impunha restrições ao local onde o código poderia ser desenvolvido e dificultava o controle de versões.

O modelo atual é baseado em módulos.

Um módulo define:

  • a raiz de um projeto
  • o caminho de importação dos pacotes
  • as dependências utilizadas

Esse modelo elimina a dependência de um diretório global e permite que o código seja desenvolvido em qualquer local, com controle explícito de versões.

Neste artigo, o foco é compreender como o Go gerencia dependências a partir de módulos, utilizando as ferramentas padrão da linguagem.

Ao longo dos exemplos, vamos explorar:

  • criação de módulos
  • adição e resolução de dependências
  • uso de pacotes externos
  • controle básico de versões

Sem aprofundar em todos os detalhes do ecossistema, o objetivo é entender o fluxo real de trabalho com módulos no Go moderno.

Inicializando um módulo

Um módulo define a raiz de um projeto em Go. É a partir dele que o Go resolve pacotes e dependências.

Para criar um módulo, basta executar:

$ go mod init project

Esse comando cria um arquivo go.mod no diretório atual:

module project

go 1.26.2

Papel do go.mod

O arquivo go.mod define:

  • o nome do módulo (module project)
  • a versão da linguagem utilizada

Esse nome passa a ser o prefixo dos imports internos.

Impacto nos imports

Considere a estrutura:

project/
├── go.mod
├── mathlib/
│   └── vector.go
└── main.go

No main.go, o pacote é importado usando o caminho do módulo:

import "project/mathlib"

Ou seja:

  • o caminho não é relativo
  • não depende do sistema de arquivos
  • é definido pelo módulo

Unidade de trabalho

A partir do momento em que um módulo é definido:

  • todos os pacotes dentro dele passam a compartilhar o mesmo contexto
  • dependências são resolvidas a partir desse ponto
  • o projeto deixa de depender de uma estrutura global (GOPATH)

Observação

O nome do módulo não precisa, inicialmente, corresponder a um repositório remoto. Durante o desenvolvimento local, nomes simples como project ou calc são suficientes.

Quando o código for publicado, esse nome normalmente passa a refletir o endereço do repositório (por exemplo, github.com/usuario/projeto).

Dependências

Uma vez definido o módulo, o Go passa a gerenciar automaticamente as dependências utilizadas pelo projeto.

Na prática, isso acontece a partir dos imports no código.

Adicionando uma dependência

Considere o uso de um pacote externo:

import "github.com/google/uuid"

Ao utilizar esse pacote no código e executar o programa:

$ go run .

o Go automaticamente:

  • resolve o caminho do módulo
  • baixa a dependência
  • registra a versão no go.mod

Atualizando o go.mod

Após a execução, o arquivo passa a incluir a dependência:

require github.com/google/uuid v1.6.0

Além disso, o Go mantém um arquivo adicional: go.sum

Esse arquivo registra hashes das dependências, garantindo integridade.

Limpando dependências

Com o tempo, imports podem ser removidos do código. Para manter o módulo consistente:

$ go mod tidy

Esse comando:

  • adiciona dependências faltantes
  • remove dependências não utilizadas

Fluxo prático

O uso de dependências no Go segue um fluxo simples:

  1. adicionar o import no código
  2. executar o programa
  3. o Go resolve e registra automaticamente

Não há necessidade de comandos explícitos para “instalar” dependências no modelo moderno.

Observação

Esse comportamento elimina a necessidade de ferramentas externas para gerenciamento de dependências.

O próprio go controla:

  • resolução
  • versionamento
  • integridade

Instalando ferramentas com go install

Além de gerenciar dependências de código, o Go também permite instalar ferramentas diretamente a partir de módulos.

O comando utilizado é:

$ go install pkg@version

Por exemplo:

$ go install golang.org/x/tools/cmd/goimports@latest

Esse comando resolve o módulo, baixa o código, compila o programa e instala o binário no diretório de executáveis do Go, normalmente ~/go/bin.

Uma vez instalado, o programa passa a ser apenas mais um executável disponível no sistema:

$ goimports -w .

Esse modelo é consistente com o restante da linguagem: não há um processo de instalação complexo, apenas a geração e cópia de um binário.

Observação

Ferramentas instaladas com go install não fazem parte do go.mod. Elas são independentes do módulo atual e não afetam as dependências do projeto.

Por isso, o uso de @version (ou @latest) é obrigatório nesse contexto, garantindo que a versão do binário seja definida explicitamente.

Conexão com o fluxo anterior

No artigo anterior, vimos que um programa pode ser instalado localmente com:

$ go install

Aqui, o mesmo comando é utilizado para instalar ferramentas externas, com a diferença de que o código não está no diretório atual e a versão precisa ser informada.

Módulos locais

Durante o desenvolvimento, é comum trabalhar com mais de um módulo ao mesmo tempo. Nesses casos, pode ser necessário referenciar um pacote local sem publicá-lo.

O Go permite isso através da diretiva replace.

Cenário

Considere dois projetos:

Projects/
├── mathlib/
│   ├── go.mod
│   └── vector.go
└── calc/
    ├── go.mod
    └── main.go

O módulo mathlib contém o código reutilizável, enquanto calc é um programa que o utiliza.

Inicializando os módulos

No diretório mathlib:

$ go mod init mathlib

No diretório calc:

$ go mod init calc

Referenciando o módulo local

No go.mod de calc, adicionamos:

module calc

go 1.26.2

replace mathlib => /caminho/para/mathlib

Essa diretiva informa ao Go que, ao importar mathlib, ele deve utilizar o diretório local indicado.

Adicionando a dependência

Após isso, basta importar o pacote no código:

import "mathlib"

E executar:

$ go run .

O Go resolve automaticamente a dependência utilizando o caminho definido em replace.

Observação

Sem a diretiva replace, o Go tentaria localizar mathlib como um módulo remoto.

O uso de replace permite desenvolver e testar módulos localmente, sem necessidade de publicação ou versionamento inicial.

Fluxo de trabalho

Esse padrão é comum durante desenvolvimento:

  • criar um módulo reutilizável (mathlib)
  • utilizar esse módulo em outro projeto (calc)
  • referenciar localmente com replace

Quando o módulo estiver pronto para uso externo, a diretiva pode ser removida e substituída por uma dependência versionada.

Versionamento

Quando um módulo passa a ser utilizado por outros projetos, torna-se necessário definir versões de forma explícita.

O Go utiliza o padrão de versionamento semântico (SemVer), no formato:

MAJOR.MINOR.PATCH

Por exemplo:

v1.2.3

Significado das versões

  • MAJOR: mudanças incompatíveis com versões anteriores
  • MINOR: novas funcionalidades compatíveis
  • PATCH: correções sem impacto na API

Esse modelo permite que quem utiliza o módulo entenda o impacto de uma atualização.

Uso no go.mod

Ao depender de um módulo versionado, o go.mod registra a versão:

require mathlib v1.2.3

Isso garante que o projeto utilize sempre a mesma versão do pacote, independentemente de alterações futuras.

Origem das versões

As versões são definidas a partir de tags no repositório Git.

Exemplo:

$ git tag v1.0.0
$ git push origin v1.0.0

O Go utiliza essas tags para identificar versões disponíveis do módulo.

Durante o desenvolvimento

Enquanto o módulo ainda está sendo desenvolvido localmente, é comum não haver versões definidas. Nesse caso:

  • o Go utiliza pseudo-versões
  • ou o módulo é referenciado via replace

Esse comportamento já foi observado no uso de módulos locais.

Observação

O versionamento não altera o código do módulo, apenas define como ele é consumido por outros projetos.

Ferramentas básicas

O Go fornece um conjunto de comandos integrados para inspecionar e manter módulos e dependências.

Listando dependências

Para visualizar os módulos utilizados pelo projeto:

$ go list -m all

Esse comando lista:

  • o módulo principal
  • dependências diretas
  • dependências indiretas

Limpando o módulo

Com o tempo, imports podem ser adicionados e removidos. Para manter o go.mod consistente:

$ go mod tidy

Esse comando:

  • adiciona dependências necessárias
  • remove dependências não utilizadas

Análise de código

O Go também inclui ferramentas para análise estática.

Para verificar possíveis problemas:

$ go vet ./...

Esse comando analisa o código em busca de erros comuns, como:

  • uso incorreto de funções
  • conversões suspeitas
  • variáveis não utilizadas em contextos específicos

Observação

Essas ferramentas fazem parte do fluxo normal de desenvolvimento e não exigem configuração adicional.

Elas atuam diretamente sobre o código e o módulo, mantendo o projeto consistente com o mínimo de intervenção manual.

Conclusão

Neste artigo, os módulos foram apresentados como a base para organização e resolução de dependências no Go.

A partir da inicialização com go mod init, o projeto passa a ter um contexto definido, no qual pacotes internos e externos são resolvidos de forma consistente. Dependências são introduzidas diretamente pelo uso no código e registradas no go.mod, enquanto ferramentas podem ser instaladas de forma independente com go install.

O uso de replace permite integrar módulos locais durante o desenvolvimento, mantendo o mesmo fluxo que será utilizado posteriormente com módulos versionados. Quando necessário, o versionamento por meio de tags passa a definir de forma explícita como esses módulos são consumidos.

Esse modelo elimina a necessidade de estruturas globais e ferramentas externas, concentrando no próprio Go o controle sobre dependências, versões e integridade do projeto.