Pola pengembangan modern sering memecah satu produk menjadi banyak module: library utilitas, library domain, lalu beberapa aplikasi. Masalahnya, mode default Go menyelesaikan dependency lewat go.mod — yang berarti pihak lain harus merilis versi dulu sebelum bisa dipakai. Mau memperbaiki satu baris di library sambil langsung mengujinya di aplikasi? Kita terpaksa salin-salin path atau main replace manual. Sejak Go 1.18, berkas go.work menjadi jawaban resmi untuk ini: satu workspace yang menyatukan beberapa module lokal dalam satu sesi pengembangan. Pada chapter ini kita akan membangun workspace nyata dari dua module dan membandingkannya dengan cara lama.

Masalah: Module Terpisah yang Saling Membutuhkan #

Demo kita terdiri dari dua module terpisah. Module contoh.com/formatter menyediakan fungsi Kapital, sementara module contoh.com/aplikasi mengimpornya:

go
package main

import (
	"fmt"

	"contoh.com/formatter"
)

func main() {
	fmt.Println(formatter.Kapital("go workspace"))
}
go
package formatter

import "strings"

// Kapital mengubah seluruh huruf menjadi kapital.
func Kapital(s string) string {
	return strings.ToUpper(s)
}

Menjalankan aplikasi dalam keadaan polos gagal — go.mod-nya belum menyebut formatter karena modul itu belum pernah dirilis:

bash
cd mod-b && go run .

Output yang Dihasilkan:

text
main.go:6:2: no required module provides package contoh.com/formatter; to add it:
	go get contoh.com/formatter

Saran go get contoh.com/formatter hanya berhasil bila module tersebut sudah dipublikasikan ke repository dan proxy. Untuk pengembangan lokal, kita butuh mekanisme lain.

Cara Lama: Directive replace #

Solusi era sebelum workspace adalah replace pada go.mod pemanggil, diarahkan ke direktori lokal:

text
module contoh.com/aplikasi

go 1.27.2

require contoh.com/formatter v0.0.0

replace contoh.com/formatter => ../mod-a

Dengan konfigurasi tersebut go run . kembali berhasil dan mencetak GO WORKSPACE (exit 0). Namun cara ini punya tiga ketidaknyamanan: setiap rekan tim harus menulis replace versinya sendiri, path relatif menuntut struktur folder yang sama persis di semua mesin, dan go.mod produksi harus dibersihkan sebelum rilis agar tidak menunjuk ke folder pribadi pengembang.

go work init: Workspace Resmi #

Perintah go work init membuat berkas go.work yang mendaftarkan module lokal ke dalam satu workspace. Dijalankan dari direktori induk yang memuat kedua module:

bash
go work init ./mod-a ./mod-b

Perintah ini berhasil dengan status 0 tanpa keluaran. Isi go.work yang terbentuk:

text
go 1.27.2

use (
	./mod-a
	./mod-b
)

Direktif use menyatakan: “saat build berlangsung dalam workspace ini, resolusi import path contoh.com/formatter diarahkan ke ./mod-a lokal” — tanpa menyentuh go.mod kedua module sama sekali. Sekarang program berjalan:

bash
go run ./mod-b

Output yang Dihasilkan:

text
GO WORKSPACE

Mari kita telaah mekanisme yang barusan terjadi:

  1. Pencarian workspace: tool go mencari go.work mulai dari direktori aktif naik ke atas; workspace aktif berlaku untuk semua perintah di dalam pohonnya.
  2. Resolusi lewat use: import contoh.com/formatter terjawab oleh module lokal ./mod-a — go.mod milik mod-b tidak perlu require maupun replace.
  3. Tanpa publikasi: perubahan apa pun di mod-a langsung terasa di mod-b pada build berikutnya — siklus edit-build kembali secepat proyek tunggal.

Perintah dan Berkas Pendukung #

PerintahFungsi
go work init ./a ./bMembuat go.work berisi module yang disebut
go work use ./cMenambah (atau menghapus) module dari workspace
go work syncMenyelaraskan go.mod module workspace dengan build list
go work editMengubah go.work dari skrip

Berkas go.work.sum menyimpan checksum module yang dibutuhkan khusus oleh workspace — sepadan dengan go.sum pada module, muncul hanya bila workspace turut menarik dependency eksternal. Menonaktifkan workspace sesaat bisa dilakukan tanpa menghapus berkasnya:

bash
GOWORK=off go run ./mod-b

Output yang Dihasilkan:

text
main.go:6:2: no required module provides package contoh.com/formatter; to add it:
	go get contoh.com/formatter

Tanpa workspace, kita dilempar kembali ke masalah awal — bukti bahwa resolusi lokal tadi memang kerja go.work, bukan sulap pada go.mod.

Kapan Workspace Dipakai #

Workspace bersinar pada tiga skenario: monorepo berisi banyak aplikasi dan library yang saling memakai; migrasi besar satu module menjadi beberapa module secara bertahap; serta pengembangan library bersama aplikasi konsumen di repo berbeda. Sebaliknya, proyek satu module tidak butuh go.work — go.mod sudah memadai.

Workspace menyatukan module-module lokal, tetapi suatu saat karya kita harus meninggalkan meja kerja: diberi nomor versi, ditandai, lalu dikonsumsi orang lain. Aturan penomoran tersebut — semantic versioning, tag git, dan konsekuensi /v2 — kita bahas pada chapter Versioning Module .

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

src/mod-a/formatter.go

Unduh berkas
go
package formatter

import "strings"

// Kapital mengubah seluruh huruf menjadi kapital.
func Kapital(s string) string {
	return strings.ToUpper(s)
}

src/mod-b/main.go

Unduh berkas
go
package main

import (
	"fmt"

	"contoh.com/formatter"
)

func main() {
	fmt.Println(formatter.Kapital("go workspace"))
}