Werken met externe pakketten is in elk serieus Go-project onvermijdelijk. Dependency management in Go gebeurt tegenwoordig volledig via Go modules, het ingebouwde systeem dat sinds Go 1.11 standaard is en vanaf Go 1.16 verplicht werd. Of je nu een kleine CLI bouwt of een grote microservice, je moet begrijpen hoe modules werken om je project voorspelbaar en reproduceerbaar te houden.
In deze gids leer je hoe Go modules werken, wat go.mod en go.sum doen, hoe je versies beheert en hoe je omgaat met private repositories. Als je nog niet eerder een Go project hebt opgezet, lees dan eerst onze installatie en eerste Go programma.
Wat zijn Go modules?
Een Go module is een verzameling gerelateerde Go packages die samen worden ge-versioned en gedistribueerd. Elke module heeft een unieke module path, meestal de locatie van de repository, zoals github.com/gebruiker/projectnaam.
Vóór modules gebruikte Go een systeem met GOPATH en tools zoals dep of glide. Dat was verre van ideaal: versies waren onduidelijk en reproduceerbare builds vereisten handmatig werk. Modules lossen dat probleem op met een declaratief bestand dat precies vastlegt welke versies je gebruikt.
Je initialiseert een nieuwe module met één commando:
go mod init github.com/jouwnaam/mijnproject
Dit creëert een go.mod bestand in de root van je project. Vanaf dat moment is alles wat je project nodig heeft traceerbaar via dat bestand.
Het go.mod bestand begrijpen
Het go.mod bestand is het hart van dependency management in Go. Het is menselijk leesbaar en ziet er zo uit:
module github.com/jouwnaam/mijnproject
go 1.22
require (
github.com/gorilla/mux v1.8.1
github.com/lib/pq v1.10.9
)
require (
github.com/stretchr/testify v1.9.0 // indirect
)
De verschillende secties hebben elk een doel:
- module: de unieke identifier van jouw project.
- go: de minimale Go-versie die vereist is.
- require: directe dependencies die jouw code importeert.
- indirect: transitive dependencies die door andere modules worden gebruikt.
Je kunt ook replace en exclude directives toevoegen om specifiek gedrag af te dwingen, bijvoorbeeld bij forks of bij tijdelijke bugfixes.
Het go.sum bestand en integriteit
Naast go.mod krijg je ook een go.sum bestand. Dit bevat cryptografische hashes van elke module die je gebruikt, inclusief transitive dependencies.
go.sum is geen lock-file in de traditionele zin zoals package-lock.json bij npm. Het is een integriteitscontrole. Als iemand een versie van een module stiekem zou wijzigen, zou de hash niet meer kloppen en zou de build falen. Check beide bestanden altijd in bij Git.
Dependencies toevoegen
Het toevoegen van een nieuwe dependency is eenvoudig. Je importeert het pakket in je code:
package main
import (
"fmt"
"github.com/google/uuid"
)
func main() {
id := uuid.New()
fmt.Println("Nieuwe UUID:", id)
}
Daarna draai je:
go mod tidy
Go zoekt automatisch de laatste stabiele versie, voegt die toe aan go.mod en download de module. Alternatief kun je expliciet go get github.com/google/uuid gebruiken.
Zie onze gids over HTTP servers bouwen in Go voor een praktijkvoorbeeld waarin je routing-libraries als dependency toevoegt.
Versies kiezen en semantic versioning
Go modules volgen semantic versioning. Een versie zoals v1.4.2 betekent: major 1, minor 4, patch 2. De regels:
- Patch (
v1.4.3): backward-compatible bugfixes. - Minor (
v1.5.0): backward-compatible nieuwe features. - Major (
v2.0.0): breaking changes.
Major versies vanaf v2 vereisen een speciale module path-suffix, bijvoorbeeld github.com/foo/bar/v2. Dit zorgt ervoor dat je meerdere major versies tegelijk in één build kunt hebben als dat echt niet anders kan.
Een specifieke versie installeren
Wil je niet de laatste versie, maar een specifieke? Dat kan:
go get github.com/gorilla/[email protected]
go get github.com/gorilla/mux@latest
go get github.com/gorilla/mux@master
Je kunt ook een exacte commit hash gebruiken, wat handig is voor ongepubliceerde fixes:
go get github.com/gorilla/mux@abc1234
Dependencies updaten
Om te zien welke updates beschikbaar zijn:
go list -m -u all
Dit toont alle modules met een indicator als er een nieuwere versie bestaat. Update vervolgens selectief:
go get -u github.com/lib/pq
Wil je alles tegelijk updaten? Gebruik:
go get -u ./...
Let op: dit kan breaking changes introduceren. Draai na elke update je tests. Voor database-gerelateerde dependencies is dit extra belangrijk, zoals we bespreken in database toegang in Go.
De Minimal Version Selection strategie
Go gebruikt een unieke strategie genaamd Minimal Version Selection (MVS). In tegenstelling tot npm of pip, dat altijd de hoogste compatibele versie probeert te kiezen, kiest Go de laagste versie die alle requirements bevredigt.
Dit klinkt contra-intuïtief, maar het maakt builds extreem voorspelbaar. Twee developers met hetzelfde go.mod krijgen altijd exact dezelfde dependency-tree zonder extra lock-files. Als je een nieuwere versie wilt, vraag je daar expliciet om.
Ongebruikte dependencies opruimen
Tijdens development verwijder je regelmatig code, waardoor imports verdwijnen. Om je go.mod en go.sum netjes te houden:
go mod tidy
Dit commando verwijdert ongebruikte dependencies en voegt ontbrekende toe. Draai het altijd voordat je een commit maakt. Het voorkomt vervuiling van je dependency-graph, wat belangrijk is voor een schone project structuur in Go.
Private repositories gebruiken
Veel bedrijven hebben interne Go modules in private Git repositories. Standaard probeert Go alle modules via de public proxy (proxy.golang.org) te downloaden, wat voor private modules niet werkt.
De oplossing is de GOPRIVATE environment variable:
export GOPRIVATE=github.com/jouwbedrijf/*,gitlab.com/jouwbedrijf/*
Dit vertelt Go om modules van deze paden direct bij de Git-server op te halen. Zorg dat je SSH-key of een Personal Access Token correct is geconfigureerd, anders krijg je authenticatiefouten.
Voor CI/CD-pipelines gebruik je vaak een GOPROXY met een privé-proxy, zoals Athens of de commerciële JFrog Artifactory.
Vendoring: dependencies meeleveren
Soms wil je alle dependencies fysiek in je repository hebben staan, bijvoorbeeld voor air-gapped builds of om zekerheid te hebben dat code altijd beschikbaar blijft. Dat heet vendoring:
go mod vendor
Dit creëert een vendor/ map met alle bron-code van je dependencies. Go detecteert deze map automatisch en gebruikt hem in plaats van de module cache. Builds worden dan volledig reproduceerbaar zonder netwerk-toegang.
Het nadeel: de repository wordt fors groter. Gebruik vendoring alleen als je daar een goede reden voor hebt.
Werken met replace directives
Soms wil je tijdelijk een lokale versie van een dependency gebruiken, bijvoorbeeld om een bug te fixen. Dat kan met replace:
replace github.com/gorilla/mux => ../mijn-mux-fork
Of om naar een fork op GitHub te wijzen:
replace github.com/gorilla/mux => github.com/jouwnaam/mux v1.8.1-patched
replace werkt alleen in de go.mod van het main module. Dit is bewust zo: je kunt geen replace-directives van libraries forceren op je eigen project.
Security scanning van dependencies
Dependencies introduceren risico's. Go biedt sinds 1.18 een ingebouwde tool om bekende kwetsbaarheden op te sporen:
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
govulncheck scant je code tegen de officiële Go Vulnerability Database en rapporteert alleen kwetsbaarheden die je daadwerkelijk aanroept. Dat voorkomt valse positieven.
Voeg dit toe aan je CI-pipeline zodat je team direct wordt gewaarschuwd bij nieuwe CVE's in je dependencies.
Praktische tips voor dagelijks gebruik
Een paar vuistregels die je veel tijd besparen:
- Commit altijd zowel
go.modalsgo.sum. - Draai
go mod tidyvoor elke commit. - Update dependencies in kleine stappen, niet allemaal tegelijk.
- Pin kritieke dependencies op een exacte versie als de API onstabiel is.
- Gebruik
replacespaarzaam, want het is makkelijk te vergeten. - Lees de release notes voordat je een major versie update.
Als je veelvuldig nieuwe functies toevoegt, combineer dependency-updates met goede error handling zodat problemen in een update zichtbaar worden in plaats van stilletjes door te sijpelen.
Veelgestelde vragen
Wat is dependency management in Go?
Dependency management in Go is het proces van het beheren van externe pakketten waar jouw project van afhankelijk is. Sinds Go 1.11 gebeurt dit via Go modules, met go.mod en go.sum als centrale bestanden.
Wat is het verschil tussen go.mod en go.sum?
Het go.mod bestand definieert welke modules en versies jouw project gebruikt. Het go.sum bestand bevat cryptografische hashes om te verifiëren dat downloads niet gewijzigd zijn sinds de eerste keer dat ze werden opgehaald.
Hoe update ik een dependency in Go?
Gebruik go get gevolgd door de module path om naar de laatste versie te updaten, of voeg @v1.2.3 toe voor een specifieke versie. Daarna ruim je ongebruikte dependencies op met go mod tidy.
Kan ik private repositories gebruiken als dependency?
Ja, dat kan door de GOPRIVATE environment variable te zetten en SSH of een token te configureren voor Git authenticatie. Zo kan Go private modules ophalen zonder ze via de public proxy te proberen te downloaden.
Wat doet go mod tidy precies?
go mod tidy synchroniseert go.mod en go.sum met de daadwerkelijk gebruikte imports in je code. Het voegt ontbrekende dependencies toe en verwijdert ongebruikte entries.
Conclusie
Go modules maken dependency management bijna onzichtbaar eenvoudig zodra je de basics begrijpt. Met go.mod, go.sum, semantic versioning en tools als go mod tidy en govulncheck heb je alles in handen om projecten reproduceerbaar, veilig en onderhoudbaar te houden.
Begin klein: initialiseer een module, voeg één dependency toe, experimenteer met versies. Voor meer achtergrond over waarom Go zo prettig werkt in productie, lees waarom Go populair is voor backends. De officiële Go Modules Reference is daarnaast een uitstekend naslagwerk voor alle geavanceerde opties.