Belajar Golang dari Nol #8: Package dan Go Module
Di Belajar Golang dari Nol #7 kita sudah membereskan pointer. Materi paling menakutkan di seri ini sudah lewat. Sekarang saatnya bicara soal kerapian. Selama tujuh bagian, semua latihan kita tulis di satu file bernama main.go. Untuk belajar, itu wajar. Untuk proyek sungguhan, itu resep pusing. Di bagian ini kita belajar memecah kode ke banyak file dan banyak folder. Kuncinya dua hal: package dan Go module.
Masalahnya: main.go yang mulai gemuk
Coba lihat file latihan Anda dari bagian 4 sampai 7. Isinya kira-kira begini. Ada struct Produk. Ada beberapa method. Ada function pembantu untuk format harga. Ada function main. Semua tumpuk di satu file.
// main.go, semua campur di sini
package main
import "fmt"
type Produk struct { ... }
func (p *Produk) TambahStok(jumlah int) { ... }
func (p *Produk) KurangiStok(jumlah int) error { ... }
func formatRupiah(angka int) string { ... }
func cetakLaporan(daftar []Produk) { ... }
func main() { ... }
Untuk 100 baris, ini masih aman. Tapi bayangkan aplikasi kasir sederhana. Ada produk, pelanggan, transaksi, dan laporan. Kalau semua ditulis di main.go, file itu bisa tembus ribuan baris. Anda akan scroll ke sana kemari cuma untuk cari satu function. Nama function juga mulai bentrok. Simpan() untuk produk atau untuk pelanggan? Terpaksa dinamai SimpanProduk dan SimpanPelanggan, lalu file makin ramai.
Bahasa lain menyelesaikan ini dengan class dan namespace. Go menyelesaikannya dengan package. Satu package berisi kode yang topiknya sama. Kode soal produk masuk package produk. Alur utama program tetap di package main.
Go module: KTP proyek Anda
Sebelum bikin package, kita lunasi dulu satu utang dari bagian 1. Waktu itu saya minta Anda menjalankan go mod init tanpa penjelasan panjang. Sekarang waktunya dijelaskan tuntas.
Module adalah unit proyek di Go. Satu module berisi satu proyek utuh: semua file, semua folder, semua package di dalamnya. Module juga mencatat proyek Anda bergantung ke library apa saja. Anggap saja module itu KTP proyek. Dia menyimpan nama proyek dan daftar kenalannya.
Membuatnya cukup satu perintah di folder proyek.
go mod init github.com/budi/tokokita
Perintah ini menghasilkan file go.mod. Isinya pendek.
module github.com/budi/tokokita
go 1.22
Baris pertama adalah nama module. Baris kedua adalah versi Go minimum yang dipakai proyek ini. Sudah, itu saja untuk sekarang.
Soal penamaan, ada kebiasaan yang hampir semua programmer Go ikuti: nama module memakai path repository tempat kode itu nanti disimpan. Kalau username GitHub Anda budi dan proyeknya bernama tokokita, nama module-nya github.com/budi/tokokita. Kenapa begitu? Karena kalau suatu hari orang lain mau memakai kode Anda, Go bisa langsung tahu harus download dari mana. Nama module sekaligus jadi alamatnya. Untuk latihan lokal, Anda bebas menamai apa saja, misalnya tokokita saja. Tapi biasakan format path repo sejak awal supaya tidak perlu ganti nanti.
Membuat package sendiri
Sekarang kita pecah proyeknya. Aturannya sederhana: satu folder berisi satu package. Semua file di folder yang sama harus memakai nama package yang sama. Kita buat folder produk di dalam proyek, lalu isi dengan file produk.go.
tokokita/
├── go.mod
├── main.go
└── produk/
└── produk.go
Baris pertama di produk/produk.go bukan lagi package main, tapi package produk. Nama package sebaiknya sama dengan nama foldernya. Go tidak memaksa, tapi kalau berbeda, orang yang baca kode Anda akan bingung.
Satu hal penting: hanya package main yang punya function main() dan bisa dijalankan langsung. Package lain sifatnya seperti kotak perkakas. Isinya dipakai oleh package lain, tidak jalan sendiri.
Huruf besar dan huruf kecil: aturan akses Go
Di banyak bahasa, ada kata kunci public dan private untuk mengatur apa yang boleh diakses dari luar. Go tidak punya kata kunci itu. Aturannya lebih sederhana dan agak unik: dilihat dari huruf pertama namanya.
- Nama diawali huruf besar, misalnya
ProdukatauTambahStok: exported. Bisa diakses dari package lain. - Nama diawali huruf kecil, misalnya
hitungDiskon: unexported. Hanya bisa dipakai di dalam package itu sendiri. Privat.
Aturan ini berlaku untuk semuanya: function, struct, method, field struct, sampai variabel level package. Field struct yang huruf kecil pun tidak bisa dibaca dari luar package-nya.
Masih ingat di bagian 1 saya bilang penulisan fmt.Println dengan P besar itu bukan selera, tapi keharusan? Ini jawabannya. Println ada di package fmt milik tim Go. Supaya bisa kita panggil dari luar, dia harus exported. Makanya hurufnya besar. Kalau tim Go menamainya println, kita tidak akan bisa memakainya sama sekali.
Meng-import package sendiri
Lalu bagaimana main.go memakai package produk? Lewat import, sama seperti kita meng-import fmt selama ini. Bedanya, path-nya adalah nama module ditambah nama folder.
import "github.com/budi/tokokita/produk"
Go membaca path itu begini: cari module github.com/budi/tokokita. Itu proyek kita sendiri, sesuai go.mod. Lalu masuk ke folder produk. Setelah di-import, semua isi yang exported bisa dipanggil dengan awalan nama package-nya: produk.Baru(...), mirip fmt.Println(...).
Praktik: memecah sistem stok jadi dua file
Teori cukup. Sekarang kita rapikan latihan sistem stok dari bagian 4. Semua yang berhubungan dengan produk pindah ke package produk: struct, constructor, dan method-nya. File main.go hanya berisi alur program. Ini isi lengkap produk/produk.go.
package produk
import "fmt"
// Produk menyimpan data satu barang di gudang.
type Produk struct {
Nama string
Stok int
Harga int
}
// Baru membuat Produk baru dan mengembalikan pointer-nya.
func Baru(nama string, stok int, harga int) *Produk {
return &Produk{Nama: nama, Stok: stok, Harga: harga}
}
// TambahStok menambah jumlah stok.
func (p *Produk) TambahStok(jumlah int) {
p.Stok += jumlah
}
// KurangiStok mengurangi stok, gagal kalau stok tidak cukup.
func (p *Produk) KurangiStok(jumlah int) error {
if jumlah > p.Stok {
return fmt.Errorf("stok %s tinggal %d, tidak cukup untuk keluar %d",
p.Nama, p.Stok, jumlah)
}
p.Stok -= jumlah
return nil
}
// Info mengembalikan ringkasan produk dalam satu baris.
func (p *Produk) Info() string {
return fmt.Sprintf("%s | stok %d | Rp%d", p.Nama, p.Stok, p.Harga)
}
Perhatikan function Baru. Ini pola yang disebut constructor. Dari luar, pemanggilannya jadi produk.Baru(...), enak dibaca seperti kalimat: buat produk baru. Method-nya memakai pointer receiver *Produk supaya perubahan stok benar-benar tersimpan. Itu materi bagian 7 yang langsung terpakai di sini.
Sekarang main.go. Pendek dan bersih.
package main
import (
"fmt"
"github.com/budi/tokokita/produk"
)
func main() {
kopi := produk.Baru("Kopi Arabika", 10, 25000)
kopi.TambahStok(5)
err := kopi.KurangiStok(20)
if err != nil {
fmt.Println("Gagal:", err)
}
err = kopi.KurangiStok(8)
if err != nil {
fmt.Println("Gagal:", err)
}
fmt.Println(kopi.Info())
}
Jalankan dengan go run . dari folder proyek. Titik di akhir artinya jalankan package di folder ini. Outputnya:
Gagal: stok Kopi Arabika tinggal 15, tidak cukup untuk keluar 20
Kopi Arabika | stok 7 | Rp25000
Logikanya sama persis dengan versi satu file. Yang berubah cuma letaknya. Tapi rasakan bedanya. Kalau besok Anda mau menambah fitur diskon, Anda tahu persis harus buka produk/produk.go. Kalau mau mengubah alur program, buka main.go. Kode soal pelanggan nanti tinggal dibuatkan folder pelanggan sendiri.
Memakai package buatan orang lain
Kekuatan module tidak berhenti di kode sendiri. Dengan go get, Anda bisa memakai ribuan package buatan komunitas. Kita coba satu yang populer dan stabil: github.com/google/uuid, pembuat ID unik. Jalankan dari folder proyek.
go get github.com/google/uuid
Go akan download package itu, lalu mencatatnya di go.mod. File-nya sekarang punya baris baru.
module github.com/budi/tokokita
go 1.22
require github.com/google/uuid v1.6.0
Artinya proyek ini butuh package uuid versi 1.6.0. Selain itu muncul file baru bernama go.sum. Isinya kode hash dari package yang di-download. Fungsinya seperti segel: kalau nanti Anda atau teman satu tim download ulang package itu dan isinya berbeda dari yang tercatat, Go langsung menolak. Dua file ini yang menjamin proyek Anda jalan sama persis di komputer siapa pun. Keduanya wajib ikut di-commit ke Git.
Memakainya sama seperti package lain. Tambahkan di import, panggil function-nya.
import "github.com/google/uuid"
// di dalam function:
id := uuid.NewString()
fmt.Println("ID transaksi:", id)
Setiap dipanggil, uuid.NewString() menghasilkan string acak seperti 3f1c9d2a-8b7e-4f0a-9c2d-1e5b6a7f8d90. Cocok untuk ID transaksi atau nomor pesanan yang tidak boleh kembar.
Kebiasaan penamaan package yang baik
Terakhir, soal nama. Komunitas Go punya selera yang cukup seragam di sini.
- Pendek dan satu kata:
produk,laporan,kasir. - Semua huruf kecil. Tanpa underscore, tanpa camelCase. Bukan
dataProdukataudata_produk. - Nama menggambarkan isi. Orang harus bisa menebak isi package dari namanya saja.
- Hindari pengulangan nama. Function
produk.Barulebih enak daripadaproduk.ProdukBaru. Nama package sudah memberi konteks, tidak perlu diulang.
Anti-pattern yang paling sering muncul di pemula: package util atau helper. Isinya gado-gado. Format tanggal, hitung pajak, validasi email, semua dilempar ke sana. Enam bulan kemudian package itu jadi tempat sampah yang tidak ada yang berani menyentuh. Kalau ada function yang bingung mau ditaruh di mana, itu tanda Anda belum menemukan nama topik yang tepat, bukan tanda butuh package util. Format tanggal bisa masuk package tanggal. Hitung pajak masuk pajak. Spesifik selalu menang.
Rangkuman dan bagian berikutnya
Hari ini kode Anda naik kelas. Dari satu file main.go menjadi proyek yang tertata. Yang perlu diingat:
- Module adalah KTP proyek. Dibuat dengan
go mod init, dicatat digo.mod, dinamai dengan path repo sepertigithub.com/username/tokokita. - Satu folder satu package. Kode dikelompokkan per topik, bukan ditumpuk di satu file.
- Huruf besar berarti exported, huruf kecil berarti privat. Itu alasan
Printlnber-P besar. - Import package sendiri memakai nama module ditambah nama folder.
go getmenarik package pihak ketiga, tercatat digo.moddan disegel digo.sum.- Nama package pendek, huruf kecil, spesifik. Jauhi package
utilcampur aduk.
Coba latihan kecil sebelum lanjut: pecah latihan Anda sendiri dari bagian 5 atau 6 menjadi minimal satu package terpisah, lalu pastikan go run . tetap jalan. Di bagian 9 kita masuk ke salah satu fitur paling khas Go: interface. Judulnya “Interface: Kontrak yang Bikin Kode Fleksibel”. Struktur proyek seperti ini juga yang kami pakai sehari-hari saat membangun sistem aplikasi untuk bisnis dengan Go di Arrazy.
Artikel Lainnya di Kategori Golang
Golang 28 Juli 2026
Belajar Golang dari Nol #2: Variabel, Tipe Data, dan Zero Value
Lanjutan seri Go pemula: tiga cara deklarasi variabel, tipe data dasar, zero value, konstanta, konversi tipe, plus latihan program hitung belanja.
Baca Artikel
Golang 31 Juli 2026
Belajar Golang dari Nol #5: Slice dan Map, Kumpulan Data di Go
Lanjutan seri Go pemula: slice, append, map, comma ok idiom, jebakan nil map, plus latihan keranjang belanja mini dengan pengecekan stok.
Baca Artikel
Golang 29 Juli 2026
Belajar Golang dari Nol #3: Function, Multiple Return, dan Error
Lanjutan seri Golang pemula: belajar function, multiple return, konvensi error, pola if err != nil, dan variadic lewat latihan kalkulator diskon.
Baca ArtikelIngin Membaca Artikel Lainnya?
Temukan lebih banyak insight dan tips tentang teknologi dan bisnis digital.
Lihat Semua Artikel