Ein Modul ist ein Verzeichnisbaum aus Go-Paketen mit einer go.mod-Datei an der Wurzel. go.mod benennt das Modul und listet die Versionen aller anderen Module, von denen es abhängt. Du legst eins mit go mod init an:
mkdir myapp
cd myapp
go mod init example.com/myapp
go: creating new go.mod: module example.com/myapp
Die entstandene go.mod:
module example.com/myapp
go 1.24.5
Das reicht zum Bauen. Jedes Go-Projekt seit Go 1.16 ist ein Modul, und go build, go run . und go test verweigern ohne eins den Dienst:
go: go.mod file not found in current directory or any parent directory; see 'go help modules'
Einen Modulpfad wählen
Der Modulpfad ist das Präfix jedes Importpfads innerhalb des Moduls. Beim Modul example.com/myapp wird ein Paket im Verzeichnis internal/store als example.com/myapp/internal/store importiert.
| Situation | Modulpfad |
|---|---|
| Code auf GitHub, den andere importieren könnten | github.com/yourname/project |
| Code deiner Firma | yourcompany.com/project oder die URL des Repositorys |
| Ein privates Programm oder eine Übung | example.com/myapp oder einfach myapp |
| Version 2 oder höher eines veröffentlichten Moduls | github.com/yourname/project/v2 |
Bei einer Bibliothek muss der Pfad dazu passen, wo der Code liegt, weil go get damit das Repository findet. Bei einem Programm, das nur du ausführst, ist der Pfad nur ein Name. Vermeide ein einzelnes Wort, das einem Paket der Standardbibliothek entspricht, etwa go mod init fmt oder go mod init strings: Der Build scheitert dann mit ambiguous import: found package fmt in multiple modules.
go mod init ohne Argument scheitert außerhalb des alten GOPATH-Layouts:
go: cannot determine module path for source directory /home/ana/myapp (outside GOPATH, module path must be specified)
Gib ihm einen Pfad.
Abhängigkeiten hinzufügen
Schreib den Import und lass Go das Paket holen. Angenommen, main.go importiert github.com/google/uuid. Ein Build, bevor das Modul davon weiß, liefert eine klare Anweisung:
main.go:6:2: no required module provides package github.com/google/uuid; to add it:
go get github.com/google/uuid
Jeder der beiden Befehle behebt das:
go get github.com/google/uuid
# or, to sync go.mod with every import in the module:
go mod tidy
go get meldet, was es geändert hat:
go: downloading github.com/google/uuid v1.6.0
go: added github.com/google/uuid v1.6.0
go mod tidy meldet, wie es den Import aufgelöst hat:
go: finding module for package github.com/google/uuid
go: downloading github.com/google/uuid v1.6.0
go: found github.com/google/uuid in github.com/google/uuid v1.6.0
go.mod hat jetzt eine require-Zeile:
module example.com/myapp
go 1.24.5
require github.com/google/uuid v1.6.0
go get mit Versionen
| Befehl | Wirkung |
|---|---|
go get pkg | pkg in der neuesten Release hinzufügen oder die aktuelle Version behalten, wenn es schon gefordert ist |
go get pkg@v1.5.0 | genau v1.5.0 verwenden (Upgrade oder Downgrade) |
go get pkg@latest | auf die neueste Release wechseln |
go get pkg@abc1234 | einen bestimmten Commit verwenden (als Pseudo-Version gespeichert) |
go get -u ./... | jede Abhängigkeit auf ihre neueste Minor- oder Patch-Release heben |
go get -u=patch ./... | nur auf die neuesten Patch-Releases heben |
go get pkg@none | die Anforderung entfernen |
go get go@1.24 | die minimale Go-Version des Moduls anheben |
go get ändert go.mod. Programme baut oder installiert es nicht mehr; seit Go 1.18 ist das die Aufgabe von go install pkg@version.
Indirekte Abhängigkeiten
Mit // indirect markierte Anforderungen sind Module, die dein Code nicht direkt importiert, die der Build aber braucht. Seit Go 1.17 listet go.mod jedes Modul, das dem Build ein Paket liefert, also tauchen die Abhängigkeiten deiner Abhängigkeiten hier mit dieser Markierung auf. go mod tidy verwaltet diese Markierungen; du fügst sie nicht selbst hinzu.
go mod tidy
Führ go mod tidy aus, wann immer du Imports hinzufügst oder entfernst. Es:
- fügt Anforderungen für importierte Pakete hinzu, die fehlen,
- entfernt Anforderungen, die nichts mehr importiert,
- fügt die
go.sum-Einträge hinzu, die der Build braucht, und entfernt veraltete.
Es betrachtet jedes Paket im Modul, einschließlich Tests und jeder Kombination von Build-Tags, und behält daher manchmal eine Abhängigkeit, deren Verwendung du auf deiner Plattform nicht siehst. go mod tidy vor jedem Commit auszuführen und in der CI zu prüfen, dass es keinen Diff erzeugt, hält go.mod ehrlich.
go.sum
go.sum speichert einen Hash für jede Modulversion, die der Build verwendet:
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
Die erste Zeile hasht die Dateien des Moduls, die zweite nur seine go.mod. Lädt der Befehl go ein Modul herunter, prüft er den Hash gegen go.sum und gegen die öffentliche Checksummen-Datenbank (sum.golang.org), also lässt eine manipulierte oder still geänderte Release den Build scheitern. Committe go.sum neben go.mod und bearbeite es nie von Hand.
Wie Go Versionen auswählt
Go verwendet Minimal Version Selection. Jedes Modul listet die Mindestversion jeder Abhängigkeit, die es braucht, und der Build nimmt die höchste dieser Mindestversionen, nie etwas Neueres. Fordert dein Modul uuid v1.5.0 und eine Abhängigkeit uuid v1.6.0, nimmt der Build v1.6.0, auch wenn es v1.7.0 gibt. Nichts wird aktualisiert, außer jemand verlangt es mit go get.
Die Folge: Builds sind ohne Lockfile reproduzierbar. go.mod plus der Abhängigkeitsgraph bestimmen jede Version vollständig. go list -m all gibt das Ergebnis aus:
go list -m all
example.com/myapp
github.com/google/uuid v1.6.0
Major-Versionen
Ein Modul ab v2 muss die Major-Version in seinem Pfad tragen: github.com/yourname/project/v2. Der Importpfad ändert sich mit, also sind project und project/v2 verschiedene Module und können beide in einem Build vorkommen. Das ist die Regel von Go für inkompatible Änderungen, und deshalb siehst du Imports wie github.com/jackc/pgx/v5.
replace: an einer Abhängigkeit lokal arbeiten
Um Änderungen an einer Abhängigkeit zu testen, bevor du sie veröffentlichst, lenk ihren Pfad auf ein lokales Verzeichnis um:
module example.com/myapp
go 1.24.5
require example.com/mylib v1.2.0
replace example.com/mylib => ../mylib
Oder von der Kommandozeile:
go mod edit -replace example.com/mylib=../mylib
go mod tidy
Das Verzeichnis muss eine eigene go.mod enthalten. replace gilt nur, wenn dieses Modul direkt gebaut wird, nicht wenn jemand anderes davon abhängt. Denk also daran, es vor dem Taggen einer Release zu entfernen.
Um mehrere Module gleichzeitig zu bearbeiten, ohne ihre go.mod-Dateien anzufassen, hat Go 1.18 Workspaces eingeführt:
go work init . ../mylib
Das schreibt eine go.work-Datei, durch die das lokale mylib Vorrang bekommt. Halte go.work aus der Versionsverwaltung heraus, außer das ganze Team nutzt dasselbe Layout.
Tool-Abhängigkeiten (Go 1.24)
Go 1.24 hat die Direktive tool eingeführt, sodass Code-Generatoren und Linter in go.mod versioniert werden können, statt global installiert zu werden:
go get -tool golang.org/x/tools/cmd/stringer
go tool stringer -type=Color
func init ist etwas anderes
Suchen nach „golang init“ meinen oft die Funktion init, die mit go mod init nichts zu tun hat. Jedes Paket kann func init() deklarieren. Sie nimmt keine Argumente, kann von deinem Code nicht aufgerufen werden und läuft einmal automatisch, nachdem die Variablen auf Paketebene gesetzt sind und bevor main startet:
Eine Datei kann mehrere init-Funktionen haben, und sie laufen in der Reihenfolge, in der sie stehen. Importierte Pakete schließen ihre eigene Initialisierung zuerst ab. Halte init klein: Arbeit, die fehlschlagen kann, lässt sich als gewöhnliche Funktion, die du aus main aufrufst, leichter behandeln und testen.
Private Module und Proxys
Standardmäßig lädt go Module über proxy.golang.org herunter. Dieser Proxy sieht keine privaten Repositorys, also sag Go, welche Pfade privat sind:
go env -w GOPRIVATE=github.com/yourcompany/*
Module, die zu GOPRIVATE passen, werden direkt mit deinen Git-Zugangsdaten aus dem Repository geholt und umgehen die Checksummen-Datenbank.
Häufig gestellte Fragen
Was macht go mod init?
Es legt im aktuellen Verzeichnis eine go.mod-Datei an und macht dieses Verzeichnis damit zur Wurzel eines Moduls. go mod init example.com/myapp schreibt den Modulpfad und die Go-Version:
module example.com/myapp
go 1.24.5
Führ es einmal pro Projekt aus, vor go build oder go get.
Was sollte ich bei go mod init als Modulpfad verwenden?
Die Adresse, von der der Code geholt wird, wenn andere ihn importieren, meist den Pfad des Repositorys: go mod init github.com/yourname/project. Für ein Programm, das niemand importieren wird, funktioniert jeder Name (go mod init myapp), aber ein Pfad im Domain-Stil mit Punkt wie example.com/myapp vermeidet Kollisionen mit Paketnamen der Standardbibliothek.
Was ist der Unterschied zwischen go get und go mod tidy?
go get pkg@version fügt eine Abhängigkeit hinzu oder ändert ihre Version. go mod tidy liest deine Quelldateien, fügt jedes Modul hinzu, das deine Imports brauchen, das aber in go.mod fehlt, entfernt Anforderungen, die nichts importiert, und aktualisiert go.sum. Ein üblicher Ablauf: den Import schreiben, dann go mod tidy ausführen.
Sollte ich go.sum committen?
Ja. go.sum enthält kryptografische Hashes jeder Modulversion, die dein Build verwendet. Wenn du es committest, kann der Befehl go prüfen, dass alle byteidentische Abhängigkeiten herunterladen. Bearbeite es nie von Hand; go mod tidy pflegt es.
Ist „golang init“ dasselbe wie go mod init?
Nein, das sind verschiedene Dinge, die sich ein Wort teilen. go mod init ist ein Terminalbefehl, der go.mod anlegt. func init() ist eine Funktion, die du in Go-Code schreibst; sie läuft automatisch einmal vor main, nachdem die Variablen des Pakets initialisiert wurden.