unisbadri.com » Python Java Golang Typescript Kotlin Ruby Rust Dart PHP

Vendoring #

Dependency management adalah salah satu aspek yang paling berevolusi dalam sejarah Go. Era GOPATH (sebelum 2018) memaksa semua kode berada di satu direktori global dengan satu versi per dependency. Era dep (2016-2018) membawa semver tapi masih eksperimental. Era Go Modules (Go 1.11, 2018) akhirnya memberikan solusi resmi yang matang: setiap proyek punya file go.mod yang mendefinisikan identitas dan semua dependencynya secara eksplisit, proyek bisa berada di direktori mana saja, dan versi di-lock dengan checksum kriptografis di go.sum. Artikel ini membahas Go Modules dari dasar hingga skenario produksi nyata.

Evolusi Singkat Dependency Management Go #

Memahami sejarahnya membantu memahami mengapa Go Modules dirancang seperti ini:

Era GOPATH (sebelum Go 1.11):
  - Semua kode HARUS di $GOPATH/src/
  - Tidak ada versi — hanya satu versi per dependency
  - go get selalu mengambil versi terbaru
  - Masalah: reproducibility nol, "it works on my machine"

Era dep (2016-2018):
  - Alat pihak ketiga, bukan official
  - File Gopkg.toml dan Gopkg.lock
  - Mendukung semver, tapi setup rumit
  - Tidak terintegrasi dengan go tool

Era Go Modules (Go 1.11+, sekarang):
  - Resmi, terintegrasi penuh dengan go tool
  - File go.mod dan go.sum di root proyek
  - Proyek bisa di direktori mana saja
  - Versi eksplisit dan reproducible build
  - Module proxy untuk keamanan dan ketersediaan

go.mod — Jantung Setiap Modul Go #

File go.mod mendefinisikan identitas modul dan semua dependencynya. Buat dengan go mod init:

# Buat direktori proyek
mkdir myapp && cd myapp

# Inisialisasi modul
go mod init github.com/username/myapp

File go.mod yang dihasilkan:

module github.com/username/myapp

go 1.23

Setelah menambahkan beberapa dependency, go.mod akan terlihat seperti ini:

module github.com/username/myapp

go 1.23

require (
    github.com/gin-gonic/gin v1.9.1
    github.com/go-redis/redis/v9 v9.3.0
    go.uber.org/zap v1.26.0
    gorm.io/driver/postgres v1.5.4
    gorm.io/gorm v1.25.5
)

require (
    // Indirect dependencies — otomatis dikelola go mod tidy
    github.com/bytedance/sonic v1.10.2 // indirect
    github.com/gin-contrib/sse v0.1.0 // indirect
    // ... banyak lagi
)

Anatomi go.mod #

module — nama modul yang unik, biasanya mengikuti format domain/repo. Ini adalah prefix yang digunakan untuk semua package dalam modul ini:

module github.com/username/myapp
         ↑ domain     ↑ repo name

go — versi minimum Go yang dibutuhkan. Sejak Go 1.21, ini juga mempengaruhi semantic versioning dan toolchain selection.

require — daftar dependency langsung (direct) dan tidak langsung (indirect). Dependency indirect ditandai dengan komentar // indirect.

replace — mengganti sumber sebuah dependency, berguna untuk:

replace (
    // Gunakan fork lokal untuk development
    github.com/original/pkg => ../local-fork

    // Gunakan fork di GitHub
    github.com/original/pkg => github.com/myfork/pkg v1.2.3

    // Patch versi yang bermasalah
    golang.org/x/net v0.17.0 => golang.org/x/net v0.18.0
)

exclude — mencegah versi tertentu digunakan (karena ada bug atau vulnerability):

exclude (
    github.com/some/package v1.2.3  // ada CVE di versi ini
)

go.sum — Checksum untuk Keamanan Supply Chain #

go.sum adalah file yang berisi hash kriptografis dari setiap versi module yang digunakan:

github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPys6V/8s+4yBVwsgWHXFkh4sGPJDW9B6NXTqjh/RuM=

Setiap entry berisi:

  • Nama module dan versi
  • Hash SHA-256 dari zip semua file source (h1:...)
  • Hash SHA-256 dari go.mod-nya saja (/go.mod h1:...)

Go menolak menggunakan dependency jika hash tidak cocok — ini adalah perlindungan dari serangan supply chain seperti package tampering. Jangan edit go.sum secara manual — biarkan go tool yang mengelolanya.

Commit go.sum ke version control. Ini memastikan semua developer dan CI/CD menggunakan dependency yang identik secara kriptografis. Tanpa go.sum di repo, tidak ada jaminan bahwa dependency yang diunduh sama dengan yang kamu gunakan saat development.

Mengelola Dependency #

Menambah Dependency Baru #

# Tambah versi terbaru
go get github.com/gin-gonic/gin

# Tambah versi spesifik
go get github.com/gin-gonic/[email protected]

# Tambah versi terbaru dari major version tertentu
go get github.com/go-redis/redis/v9@latest

# Tambah versi dari commit hash (jarang dipakai)
go get github.com/pkg/errors@abc123def

# Tambah versi pre-release
go get github.com/some/[email protected]

Memperbarui Dependency #

# Update ke versi patch terbaru (v1.9.1 → v1.9.2)
go get github.com/gin-gonic/gin@patch

# Update ke versi minor terbaru (v1.9.x → v1.10.x)
go get github.com/gin-gonic/gin@latest

# Update SEMUA dependency ke versi terbaru yang kompatibel
go get -u ./...

# Update hanya patch versions (lebih aman)
go get -u=patch ./...

Menghapus Dependency #

# Hapus dependency dari go.mod dan go.sum
go mod tidy  # otomatis menghapus yang tidak terpakai

# Atau manual: hapus import dari kode, lalu jalankan go mod tidy

Semantic Versioning di Go #

Go mengikuti Semantic Versioning (SemVer): MAJOR.MINOR.PATCH:

v1.9.1
 │ │ └── PATCH: bug fix, backward compatible
 │ └──── MINOR: fitur baru, backward compatible
 └────── MAJOR: breaking changes

Aturan kompatibilitas Go Modules:
- v0.x.x — tidak ada jaminan stabilitas
- v1.x.x — backward compatible untuk semua v1.x.x
- v2.x.x — breaking change, harus import path berbeda!

Major Version dalam Import Path #

Ini adalah aturan penting yang sering membingungkan: jika sebuah module melakukan breaking change ke v2 atau lebih, path import-nya harus berubah dengan menambahkan /v2:

// Import v1 — tidak ada suffix
import "github.com/some/package"

// Import v2 — wajib ada /v2 di path
import "github.com/some/package/v2"

// Import v3
import "github.com/some/package/v3"

Kedua versi bisa dipakai bersamaan dalam satu program — mereka adalah module yang berbeda dari sudut pandang Go:

import (
    packagev1 "github.com/some/package"
    packagev2 "github.com/some/package/v2"
)

go mod tidy — Merapikan Dependency #

go mod tidy adalah perintah yang paling sering dijalankan selama development. Ia:

  1. Menambahkan dependency yang dipakai tapi belum ada di go.mod
  2. Menghapus dependency yang ada di go.mod tapi tidak dipakai
  3. Memperbarui go.sum dengan checksum yang diperlukan
# Jalankan setelah:
# - Menambah import baru di kode
# - Menghapus import dari kode
# - Mengubah go.mod secara manual
go mod tidy

# Periksa apakah go.mod dan go.sum sudah up-to-date (untuk CI)
go mod tidy -diff  # Go 1.22+: tampilkan perubahan tanpa menerapkan

Vendoring — Menyimpan Dependency di Repo #

Vendoring berarti menyalin semua source code dependency ke direktori vendor/ di dalam proyek. Ini membuat proyek sepenuhnya self-contained:

# Buat direktori vendor/ dari go.mod dan go.sum saat ini
go mod vendor

# Build menggunakan vendor/ (Go otomatis menggunakan vendor/ jika ada)
go build ./...

# Build eksplisit dengan vendor
go build -mod=vendor ./...

# Jalankan test dengan vendor
go test -mod=vendor ./...

Struktur direktori setelah go mod vendor:

myapp/
  ├── go.mod
  ├── go.sum
  ├── main.go
  ├── vendor/
  │   ├── modules.txt          ← metadata vendor (jangan diedit)
  │   ├── github.com/
  │   │   ├── gin-gonic/
  │   │   │   └── gin/         ← source code gin
  │   │   └── go-redis/
  │   │       └── redis/       ← source code redis
  │   └── go.uber.org/
  │       └── zap/             ← source code zap
  └── internal/
      └── ...

Kapan Menggunakan Vendoring? #

GUNAKAN VENDOR jika:
  ✓ Build harus bisa berjalan tanpa akses internet (air-gapped environment)
  ✓ CI/CD tidak boleh mengunduh dependency saat build (keamanan, kecepatan)
  ✓ Perlu audit lengkap semua kode yang dijalankan
  ✓ Dependency dari repo privat yang susah diakses di semua environment
  ✓ Perusahaan dengan policy: semua kode yang masuk produksi harus di-review

TIDAK PERLU VENDOR jika:
  ✗ CI/CD punya akses internet dan caching module yang baik
  ✗ Menggunakan GOPROXY (proxy lokal atau Athens) yang sudah meng-cache
  ✗ Tim kecil dengan environment yang konsisten
  ✗ Repo sudah besar dan vendor/ akan membuat ukurannya meledak

GOPROXY — Module Proxy #

Go mengunduh module melalui GOPROXY — sebuah proxy server yang meng-cache module. Default-nya adalah proxy.golang.org yang dikelola Google:

# Lihat konfigurasi GOPROXY saat ini
go env GOPROXY
# Output: https://proxy.golang.org,direct

# Format: daftar URL proxy dipisahkan koma
# "direct" berarti download langsung dari VCS (GitHub, dll) jika proxy gagal

Konfigurasi untuk Private Modules #

Jika proyek menggunakan dependency dari repository privat, kamu perlu mengkonfigurasi GONOSUMCHECK dan GONOSUMDB:

# Bypass proxy dan sum check untuk module privat
export GONOSUMDB="gitlab.company.com,github.com/company/*"
export GOPRIVATE="gitlab.company.com,github.com/company/*"

# Atau set di go env
go env -w GOPRIVATE="gitlab.company.com"
go env -w GONOSUMDB="gitlab.company.com"

Dengan GOPRIVATE, Go akan:

  • Tidak menggunakan GOPROXY untuk module yang cocok
  • Tidak memverifikasi checksum ke sumdb
  • Mengunduh langsung dari VCS

Private Module dengan Autentikasi #

# Untuk GitHub/GitLab private repos via HTTPS
# Buat ~/.netrc atau git config
echo "machine github.com login YOUR_TOKEN password x-oauth-basic" >> ~/.netrc

# Atau gunakan SSH
git config --global url."[email protected]:".insteadOf "https://github.com/"

Module Workspace — Bekerja dengan Banyak Modul Lokal #

go work (Go 1.18+) memungkinkan bekerja dengan beberapa modul secara bersamaan tanpa perlu replace di go.mod. Sangat berguna saat mengembangkan library dan aplikasinya bersamaan:

# Buat workspace dari direktori yang berisi beberapa modul
go work init ./myapp ./mylib ./shared

# Hasilnya: go.work

File go.work:

go 1.23

use (
    ./myapp    // modul utama
    ./mylib    // library yang sedang dikembangkan
    ./shared   // shared utilities
)
# Tambah modul ke workspace yang sudah ada
go work use ./new-module

# Sync workspace
go work sync

Dengan workspace, myapp bisa langsung menggunakan kode terbaru dari mylib tanpa harus publish ke GitHub terlebih dahulu. Go akan menggunakan versi lokal alih-alih versi di go.mod.

Jangan commit go.work ke repository (kecuali proyek memang berbentuk monorepo dengan banyak modul). File ini adalah konfigurasi development lokal. Tambahkan go.work dan go.work.sum ke .gitignore. Developer lain yang meng-clone repo tidak perlu go.work untuk build dan test.

Perintah go mod Lengkap #

# Inisialisasi modul baru
go mod init [module-name]

# Tambah/update dependency
go get package[@version]

# Hapus dependency tidak terpakai, tambah yang kurang
go mod tidy

# Salin semua dependency ke vendor/
go mod vendor

# Verifikasi dependency tidak berubah sejak diunduh
go mod verify

# Tampilkan dependency graph
go mod graph

# Unduh semua dependency ke module cache
go mod download

# Edit go.mod secara programatik (berguna di scripts)
go mod edit -require github.com/pkg/[email protected]
go mod edit -droprequire github.com/old/pkg
go mod edit -replace github.com/orig/pkg=../local-fork

# Tampilkan mengapa sebuah package diperlukan
go mod why github.com/gin-gonic/gin

# Daftar semua modul yang digunakan
go list -m all

# Daftar versi yang tersedia
go list -m -versions github.com/gin-gonic/gin

Best Practice Dependency Management #

Pilih Dependency dengan Bijak #

Pertimbangkan sebelum menambah dependency baru:

  ✓ Apakah standard library sudah cukup?
     (encoding/json, net/http, sync, dll sangat lengkap)
  ✓ Berapa besar ukurannya?
     (dependency besar = binary besar = startup lebih lambat)
  ✓ Seberapa aktif dikelola?
     (cek tanggal commit terakhir, jumlah issue terbuka)
  ✓ Berapa dependency yang dia bawa?
     (dependency transitif ikut masuk semua)
  ✓ Apakah ada alternatif dengan dependency lebih sedikit?
  ✓ Apakah license-nya kompatibel dengan proyek?
     (MIT, Apache 2.0, BSD biasanya OK; GPL bisa bermasalah)

Pin Versi, Jangan Pakai Latest #

// go.mod yang baik — versi semua di-pin secara eksplisit
require (
    github.com/gin-gonic/gin v1.9.1     // ✓ pin ke versi spesifik
    go.uber.org/zap v1.26.0             // ✓
)

// Hindari menggunakan "latest" di go.mod hasil akhir
// go get -u ./... hanya untuk update yang disengaja, bukan rutin

Workflow Development Standar #

# 1. Mulai proyek baru
go mod init github.com/company/myservice

# 2. Tambah dependency yang dibutuhkan
go get github.com/gin-gonic/[email protected]
go get go.uber.org/[email protected]
go get gorm.io/[email protected]

# 3. Setelah coding, rapikan dependency
go mod tidy

# 4. Verifikasi tidak ada yang rusak
go mod verify
go build ./...
go test ./...

# 5. Untuk production/CI: buat vendor (opsional)
go mod vendor

# 6. Commit semua file dependency
git add go.mod go.sum
git add vendor/  # jika menggunakan vendor
git commit -m "Add dependencies: gin, zap, gorm"

Contoh Workflow Proyek Lengkap #

Berikut contoh workflow nyata membangun REST API sederhana dari nol:

# 1. Buat dan inisialisasi modul
mkdir todo-api && cd todo-api
go mod init github.com/username/todo-api

# 2. Tambah dependency
go get github.com/gin-gonic/[email protected]         # HTTP framework
go get gorm.io/[email protected]                    # ORM
go get gorm.io/driver/[email protected]            # SQLite driver
go get go.uber.org/[email protected]                 # Logging
go get github.com/joho/[email protected]         # .env loader

Struktur proyek:

todo-api/
  ├── go.mod
  ├── go.sum
  ├── main.go
  ├── .env
  ├── internal/
  │   ├── handler/
  │   │   └── todo.go
  │   ├── model/
  │   │   └── todo.go
  │   └── repository/
  │       └── todo.go
  └── vendor/          ← setelah go mod vendor

File go.mod setelah go mod tidy:

module github.com/username/todo-api

go 1.23

require (
    github.com/gin-gonic/gin v1.9.1
    github.com/joho/godotenv v1.5.1
    go.uber.org/zap v1.26.0
    gorm.io/driver/sqlite v1.5.4
    gorm.io/gorm v1.25.5
)

require (
    github.com/bytedance/sonic v1.10.2 // indirect
    github.com/gabriel-vasile/mimetype v1.4.3 // indirect
    github.com/gin-contrib/sse v0.1.0 // indirect
    // ... dependency transitif lainnya
)

Contoh main.go yang menggunakan semua dependency:

package main

import (
    "log"
    "os"

    "github.com/gin-gonic/gin"
    "github.com/joho/godotenv"
    "go.uber.org/zap"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"

    "github.com/username/todo-api/internal/handler"
    "github.com/username/todo-api/internal/model"
)

func main() {
    // Load .env
    if err := godotenv.Load(); err != nil {
        log.Println("File .env tidak ditemukan, menggunakan environment variable sistem")
    }

    // Setup logger
    logger, err := zap.NewProduction()
    if err != nil {
        log.Fatal("Gagal membuat logger:", err)
    }
    defer logger.Sync()

    // Setup database
    db, err := gorm.Open(sqlite.Open("todo.db"), &gorm.Config{})
    if err != nil {
        logger.Fatal("Gagal koneksi database", zap.Error(err))
    }

    // Auto migrate
    if err := db.AutoMigrate(&model.Todo{}); err != nil {
        logger.Fatal("Gagal migrate database", zap.Error(err))
    }

    // Setup router
    r := gin.Default()
    h := handler.NewTodoHandler(db, logger)

    api := r.Group("/api/v1")
    {
        api.GET("/todos", h.List)
        api.POST("/todos", h.Create)
        api.GET("/todos/:id", h.Get)
        api.PUT("/todos/:id", h.Update)
        api.DELETE("/todos/:id", h.Delete)
    }

    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }

    logger.Info("Server berjalan", zap.String("port", port))
    if err := r.Run(":" + port); err != nil {
        logger.Fatal("Server gagal", zap.Error(err))
    }
}
# Setelah coding selesai
go mod tidy                    # rapikan dependency
go mod verify                  # verifikasi integritas
go test ./...                  # jalankan semua test
go build -o bin/todo-api .     # build binary

# Untuk deployment production
go mod vendor                  # buat vendor/
GOOS=linux GOARCH=amd64 go build -mod=vendor -o bin/todo-api-linux .

Ringkasan #

  • Go Modules adalah cara resmi dependency management sejak Go 1.11 — tidak perlu dep atau alat pihak ketiga.
  • go.mod mendefinisikan identitas modul dan semua dependency langsung; go.sum berisi checksum kriptografis untuk keamanan supply chain.
  • Commit keduanya (go.mod dan go.sum) ke version control untuk reproducible builds.
  • go get package@version untuk menambah dependency; go get -u ./... untuk update semua.
  • go mod tidy adalah perintah wajib setelah mengubah import — menambahkan yang kurang, menghapus yang tidak terpakai.
  • Major version v2+ harus ada di import path: github.com/pkg/v2 bukan github.com/pkg.
  • go mod vendor menyalin semua dependency ke direktori vendor/ untuk offline builds dan audit.
  • GOPRIVATE untuk mengkonfigurasi module privat agar bypass GOPROXY dan GONOSUMDB.
  • go work untuk bekerja dengan banyak modul lokal sekaligus saat development — jangan di-commit ke repo.
  • Pilih dependency dengan bijak — cek apakah standard library cukup, lihat ukuran dan aktifitas maintenance, perhatikan lisensi.

← Sebelumnya: Regex   Berikutnya: Keyword →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact