O que são módulos e pacotes em Go
Em Go, pacote é a unidade de organização e reutilização de código: um diretório com arquivos .go que declaram o mesmo package. Já um módulo é uma coleção versionada de pacotes, definida por um arquivo go.mod na raiz. O módulo determina: (1) o caminho de importação base, (2) a versão mínima do Go usada, e (3) as dependências externas e suas versões.
Na prática: você importa pacotes pelo caminho (ex.: fmt ou github.com/google/uuid), e o módulo resolve de onde vem cada import e em qual versão.
Criando um módulo com go mod init (passo a passo)
Crie uma pasta para o projeto e inicialize o módulo. O nome do módulo normalmente é o caminho do repositório (quando você pretende publicar), mas pode ser um nome local durante estudos.
mkdir hello-modules && cd hello-modules go mod init example.com/hello-modulesIsso cria um go.mod inicial. Um exemplo típico:
module example.com/hello-modules go 1.22module define o prefixo dos imports do seu próprio código. go define a versão mínima/target da linguagem e influencia comportamentos do sistema de módulos e do compilador.
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
Importando pacotes padrão
Pacotes da biblioteca padrão não entram como dependências no go.mod. Exemplo:
package main import ( "fmt" "time" ) func main() { fmt.Println("Agora:", time.Now()) }Os imports fmt e time são resolvidos localmente pela toolchain do Go.
Importando pacotes externos (e como a dependência entra no módulo)
Quando você importa um pacote externo, o Go baixa o módulo correspondente e registra a dependência no go.mod (e checksums no go.sum).
Exemplo usando UUID:
package main import ( "fmt" "github.com/google/uuid" ) func main() { id := uuid.New() fmt.Println("ID:", id.String()) }Ao rodar o comando que resolve dependências (por exemplo, ao compilar/testar), o Go adiciona a dependência. No fluxo moderno, você pode adicionar explicitamente com go get:
go get github.com/google/uuid@latestDepois, o go.mod passa a ter uma seção require:
module example.com/hello-modules go 1.22 require github.com/google/uuid v1.6.0Entendendo go.mod na prática
O go.mod é a “fonte da verdade” das dependências diretas do seu módulo. As partes mais comuns:
module ...: caminho do módulo (prefixo dos imports internos).go ...: versão do Go alvo.require ...: dependências e versões.replace ...(opcional): substitui um módulo por outro caminho/versão (muito usado em desenvolvimento local).exclude ...(raro): impede uma versão específica.
Exemplo com replace (útil quando você tem um módulo local em desenvolvimento):
replace example.com/outro-modulo => ../outro-moduloIsso faz o Go usar o diretório local em vez de baixar do repositório remoto.
Entendendo go.sum (integridade e reprodutibilidade)
O arquivo go.sum contém hashes criptográficos (checksums) das versões dos módulos baixados. Ele ajuda a garantir que o conteúdo baixado hoje é o mesmo conteúdo baixado amanhã, evitando alterações inesperadas na cadeia de dependências.
Pontos importantes:
go.sumpode ter entradas de dependências diretas e indiretas.- É normal ele ter mais linhas do que o
go.mod. - Você deve versionar
go.sumjunto com o código para builds reprodutíveis.
Adicionar, atualizar e remover dependências (fluxo moderno)
Adicionar uma dependência
Você pode adicionar de duas formas comuns:
- Importar no código e rodar um comando que resolva módulos (ex.:
go test), deixando o Go atualizargo.mod/go.sum. - Usar
go getexplicitamente.
go get github.com/google/uuid@latestPara fixar uma versão específica:
go get github.com/google/uuid@v1.5.0Atualizar dependências
Para atualizar uma dependência para a última versão compatível (dentro do mesmo major, em geral):
go get -u github.com/google/uuidPara atualizar todas as dependências (use com cuidado em projetos reais):
go get -u ./...Para atualizar para uma versão exata:
go get github.com/google/uuid@v1.6.0Remover dependências
Em Go, você normalmente remove uma dependência removendo o import/uso do pacote e depois limpando o grafo de módulos com go mod tidy.
go mod tidygo mod tidy faz duas coisas principais: (1) adiciona dependências que estão sendo usadas mas não estão declaradas, e (2) remove dependências que não são mais necessárias (incluindo indiretas), ajustando go.mod e go.sum.
Inspecionando módulos e versões com go list -m
Para ver o módulo principal:
go list -mPara listar todos os módulos no build (diretos e indiretos):
go list -m allPara ver detalhes (inclui diretório, versão, substituições):
go list -m -json allPara verificar por que um módulo está no grafo (quem o puxa):
go mod why -m github.com/google/uuidVersionamento semântico (SemVer) do ponto de vista do consumidor
Como consumidor de uma biblioteca, você quer atualizações seguras e previsíveis. O versionamento semântico usa o formato MAJOR.MINOR.PATCH:
- PATCH (ex.: 1.6.0 → 1.6.1): correções de bug, sem quebrar compatibilidade.
- MINOR (ex.: 1.6.0 → 1.7.0): novas funcionalidades compatíveis (não deve quebrar código existente).
- MAJOR (ex.: 1.x → 2.0.0): mudanças incompatíveis (pode quebrar seu código).
Em Go Modules, majors a partir de v2 normalmente aparecem no caminho do módulo/import. Exemplo típico:
- v1:
example.com/lib - v2:
example.com/lib/v2
Como consumidor, isso significa que atualizar de v1 para v2 geralmente exige mudar imports e possivelmente adaptar chamadas. Já atualizações de patch/minor dentro do mesmo major tendem a ser mais seguras.
Boas práticas para você como consumidor:
- Prefira atualizar dentro do mesmo major quando quiser baixo risco.
- Fixe versões quando precisar de reprodutibilidade estrita (ex.: em ambientes regulados), e use
go mod tidypara manter o grafo consistente. - Antes de grandes atualizações, inspecione o changelog e rode sua suíte de testes.
Exercício: criando dois pacotes internos (internal/) e públicos (pkg/)
Objetivo
Você vai criar um módulo com:
- Um pacote em
internal/(uso restrito ao módulo). - Um pacote em
pkg/(API pública para outros módulos consumirem). - Um
mainque usa ambos.
Estrutura sugerida
hello-modules/ go.mod cmd/ app/ main.go internal/ secret/ secret.go pkg/ greet/ greet.goPasso 1: criar o pacote público em pkg/greet
Crie pkg/greet/greet.go:
package greet import "fmt" // Hello é parte da API pública do módulo. func Hello(name string) string { if name == "" { name = "mundo" } return fmt.Sprintf("Olá, %s!", name) }Note que Hello começa com letra maiúscula: isso o torna exportado (visível para outros pacotes).
Passo 2: criar o pacote interno em internal/secret
Crie internal/secret/secret.go:
package secret // token não é exportado (minúsculo). func token() string { return "abc-123" } // TokenForLog expõe algo controlado para uso interno do módulo. func TokenForLog() string { return token() }Qualquer pacote fora do seu módulo não conseguirá importar example.com/hello-modules/internal/secret. Essa é uma regra do Go: diretórios sob internal/ só podem ser importados por código dentro do diretório pai (e seus subdiretórios) que contém internal.
Passo 3: criar o executável em cmd/app
Crie cmd/app/main.go:
package main import ( "fmt" "example.com/hello-modules/internal/secret" "example.com/hello-modules/pkg/greet" ) func main() { fmt.Println(greet.Hello("Gopher")) fmt.Println("token interno:", secret.TokenForLog()) }Passo 4: validar dependências e limpeza do módulo
Rode:
go mod tidy go list -m allComo esse exercício usa apenas biblioteca padrão, o go.mod deve permanecer sem require de módulos externos.
Boas práticas: exposição de APIs e organização de pacotes
Quando usar internal/
- Use para código que você quer poder refatorar sem se preocupar com consumidores externos.
- Ótimo para detalhes de implementação, integrações, helpers e camadas que não devem virar contrato público.
Quando usar pkg/
- Use quando você quer sinalizar explicitamente que aquele pacote é pensado para ser importado por outros módulos.
- Mantenha a API pequena e estável: exporte o mínimo necessário.
Dicas para desenhar uma API pública melhor
- Evite exportar tipos/funções “por conveniência”: uma vez exportado, vira compromisso de compatibilidade.
- Prefira nomes claros e coesos: pacotes com responsabilidade única e nomes curtos.
- Documente com comentários em identificadores exportados (ex.:
// Hello ...), pois isso alimenta a documentação do Go. - Não exponha detalhes internos (struct fields, tipos concretos) se você pode expor interfaces ou funções de alto nível.
- Evite ciclos de import: se dois pacotes dependem um do outro, reavalie a separação de responsabilidades.
Armadilhas comuns com módulos
- Import path inconsistente: o caminho em
module ...deve bater com os imports internos do projeto. - Atualizar tudo sem critério:
go get -u ./...pode introduzir mudanças indiretas; prefira atualizar dependências específicas. - Esquecer o
go mod tidy: é o comando que mantém o módulo “honesto” em relação ao que o código realmente usa.