Golang 06 — Módulos e dependências
- Golang – 01. Introdução
- Golang – 02. Tipos Básicos
- Golang – 03. Structs e Funções e Métodos
- Golang – 04. Estruturas de Controle
- Golang 05 — Pacotes em Go
- Golang 06 — Módulos e dependências
- Golang – 06. Biblioteca Padrão I – fmt e strings
- Golang – 07. Biblioteca Padrão II – os, os/exec e os/user
- Golang 08 — Interface
- Golang 09 — Goroutines – Concorrência e Paralelismo
- Golang – 10. Goroutines – Canais
- Golang – 11. Pacotes e Documentação no Go
Índice
- 1. Inicializando um módulo
- 1.1. Papel do go.mod
- 1.2. Impacto nos imports
- 1.3. Unidade de trabalho
- 1.4. Observação
- 2. Dependências
- 2.1. Adicionando uma dependência
- 2.2. Atualizando o go.mod
- 2.3. Limpando dependências
- 2.4. Fluxo prático
- 2.5. Observação
- 3. Instalando ferramentas com go install
- 3.1. Observação
- 3.2. Conexão com o fluxo anterior
- 4. Módulos locais
- 4.1. Cenário
- 4.2. Inicializando os módulos
- 4.3. Referenciando o módulo local
- 4.4. Adicionando a dependência
- 4.5. Observação
- 4.6. Fluxo de trabalho
- 5. Versionamento
- 5.1. Significado das versões
- 5.2. Uso no go.mod
- 5.3. Origem das versões
- 5.4. Durante o desenvolvimento
- 5.5. Observação
- 6. Ferramentas básicas
- 6.1. Listando dependências
- 6.2. Limpando o módulo
- 6.3. Análise de código
- 6.4. Observação
- 7. Conclusão
À 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:
- adicionar o
importno código - executar o programa
- 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.goO 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.
Deixe uma resposta