Kode dibaca jauh lebih sering daripada ditulis, dan komunitas Go menjaga satu gaya bersama supaya kode dari pengembang mana pun terasa familiar. Go unik karena urusan format sudah diurus gofmt secara otomatis; energi kita cukup untuk keputusan yang belum diotomatisasi: penamaan, komentar, dan pengelompokan logika. Chapter ini merangkum konvensi inti dari Effective Go, disertai tabel kontras lakukan-vs-hindari dan program demonstrasi nama buruk versus nama idiomatis Go.
Prinsip Dasar Penamaan #
Nama yang baik di Go singkat dan jelas: cukup panjang menyampaikan maksud, cukup pendek agar struktur kode terlihat. Aturan besar yang paling sering mengejutkan pendatang dari bahasa lain: Go memakai MixedCaps (camelCase dan PascalCase), bukan snake_case, bahkan untuk nama pribadi sekalipun. ALL_CAPS seperti MAX_RETRY yang lazim di C atau Java juga dihindari.
| Aspek | ✅ Lakukan | ❌ Hindari |
|---|---|---|
| Nama lokal | maxRetry, userName | max_retry, user_name |
| Nama konstanta | statusOK, defaultTimeout | STATUS_OK, DEFAULT_TIMEOUT |
| Singkatan | userID, httpServer, xmlParser | userId, HTTPserver |
| Nama tipe | TaskManager, UserAccount | task_manager, TManager |
| Panjang nama | count, buf untuk cakupan kecil | theTotalNumberOfItems |
Singkatan ditulis sama kapital penuh (userID, bukan userId) karena ID dibaca sebagai satu kesatuan.
Penamaan Berdasarkan Visibilitas #
Kapitalisasi bukan gaya kosmetik: huruf besar pada PascalCase menandakan identifier exported (terlihat dari package lain), sedangkan huruf kecil pada camelCase berarti unexported. Mekanisme satu huruf ini menggantikan kata kunci public/private di bahasa lain dan telah kita bahas pada chapter Export dan Unexported
. Memilih Name atau name sekaligus memutuskan siapa yang boleh memakainya.
Konvensi Khusus Komunitas #
Beberapa pola nama sudah menjadi kosakata standar Go:
| Pola | Contoh | Makna bagi pembaca Go |
|---|---|---|
Getter tanpa awalan Get | user.Name(), server.Addr() | Fungsi pengambil nilai field; GetName() terasa janggal |
Setter dengan awalan Set | SetName(n string) | Fungsi pengubah nilai field |
Interface akhiran -er | Reader, Writer, Notifier | Satu perilaku utama; nama dari kata kerja + -er |
| Sentinel error | ErrNotFound, ErrTimeout | Nilai error pembanding errors.Is |
| Tipe error | ValidationError, ConfigError | Tipe error buatan sendiri, diakhiri Error |
| Konstruktor | NewUserAccount(...), ParseTime(...) | Fungsi pembuat nilai siap pakai |
| Nama package | strconv, http, filepath | Singkat, lowercase, tanpa underscore, umumnya singular |
Nama package adalah bagian dari kalimat pemanggilan: strconv.Atoi terasa lebih natural daripada string_conversions.Atoi. Standard library konsisten memilih time.Now(), bukan time.GetCurrentTime().
Komentar dan Dokumentasi #
Komentar dokumentasi (doc comment) dikumpulkan oleh go doc dan pkg.go.dev, sehingga mengikuti tata bahasa ketat. Baris komentar di atas deklarasi diawali nama identifier, ditulis sebagai kalimat lengkap dengan huruf kapital dan titik:
// Name mengembalikan nama pemilik akun tanpa awalan Get.
func (u *UserAccount) Name() string {
return u.name
}Komentar // fungsi ini mengembalikan nama tidak dikenali sebagai dokumentasi karena tidak menyebut Name. Di dalam fungsi, pastikan komentar menjelaskan mengapa, bukan mengulang kode.
Disiplin yang Ditegakkan Compiler #
Go memilih disiplin lewat compiler: import yang tidak terpakai dan variabel lokal yang tidak terpakai keduanya error kompilasi, bukan peringatan. Lapisan berikutnya dijaga gofmt untuk format dan go vet untuk pola mencurigakan — dasar dari workflow tooling Go
.
Program Demonstrasi: Sebelum dan Sesudah #
Berkas konvensi-demo.go membandingkan dua versi yang sama-sama berjalan: versi “sebelum” memakai nama panjang, snake_case, ALL_CAPS, dan awalan get; versi “sesudah” mengikuti konvensi Go:
package main
import (
"errors"
"fmt"
)
// ErrAccountInactive adalah sentinel error untuk akun yang tidak aktif.
var ErrAccountInactive = errors.New("akun tidak aktif")
// Bagian "sebelum": nama-nama di bawah legal bagi compiler, tetapi
// menyimpang dari konvensi komunitas Go (snake_case, awalan get, ALL_CAPS).
type user_account_data struct {
user_id int
user_name string
email_address string
ACTIVE_STATUS bool
}
func (u user_account_data) get_user_name() string {
return u.user_name
}
func (u user_account_data) get_email_address() string {
return u.email_address
}
// Bagian "sesudah": nama singkat, MixedCaps, getter tanpa awalan Get,
// interface berakhiran -er, dan error berawalan Err.
type UserAccount struct {
id int
name string
email string
active bool
}
// Notifier menyampaikan pesan keluar dari sistem.
type Notifier interface {
Notify(message string) error
}
// NewUserAccount membuat akun baru yang siap dipakai.
func NewUserAccount(id int, name, email string) *UserAccount {
return &UserAccount{id: id, name: name, email: email, active: true}
}
// Name mengembalikan nama pemilik akun tanpa awalan Get.
func (u *UserAccount) Name() string {
return u.name
}
// Email mengembalikan alamat surel pemilik akun tanpa awalan Get.
func (u *UserAccount) Email() string {
return u.email
}
// Notify meneruskan pesan ke pemilik akun.
func (u *UserAccount) Notify(message string) error {
if !u.active {
return fmt.Errorf("notify %s: %w", u.email, ErrAccountInactive)
}
fmt.Printf("notifikasi ke %s: %s\n", u.email, message)
return nil
}
func main() {
old := user_account_data{
user_id: 1,
user_name: "Budi",
email_address: "[email protected]",
ACTIVE_STATUS: true,
}
fmt.Println("sebelum :", old.get_user_name(), old.get_email_address())
account := NewUserAccount(2, "Siti", "[email protected]")
fmt.Println("sesudah:", account.Name(), account.Email())
err := account.Notify("Tagihan bulanan sudah terbit")
fmt.Println("hasil notifikasi:", err)
}Jalankan dari direktori chapter:
go run konvensi-demo.goOutput yang Dihasilkan:
sebelum : Budi [email protected]
sesudah: Siti [email protected]
notifikasi ke [email protected]: Tagihan bulanan sudah terbit
hasil notifikasi: <nil>Mari kita telaah bagian pentingnya:
- Baris 9 (
var ErrAccountInactive): sentinel error berawalanErr, siap dibandingkan denganerrors.Is. - Baris 14-27 (
user_account_datadan kedua getter): versi “sebelum” — legal bagi compiler, tetapi melanggar MixedCaps dan menambah kebisingan baca. - Baris 32-37 (
UserAccount): tipe exportedPascalCase; field unexportedcamelCasesejajar otomatis olehgofmt. - Baris 40-42 (
Notifier): interface satu perilaku berakhiran-er, sepertiReaderdanWriter. - Baris 45-47 (
NewUserAccount): konstruktor berawalanNew;Parsejuga lazim bila lebih deskriptif. - Baris 49-65 (
Name,Email,Notify): getter tanpa awalanGet; komentar tiap deklarasi diawali nama identifier. - Baris 81 (
fmt.Println("hasil notifikasi:", err)): mencetak nilai balikNotifyberupanil— error dikembalikan sebagai nilai terakhir.
Memformat Otomatis dengan gofmt #
Semua keraguan soal indentasi, spasi, dan alignment selesai dengan gofmt: perintah ini menulis ulang berkas ke format standar tanpa mengubah perilaku program:
| Perintah | Fungsi |
|---|---|
gofmt -l . | Menampilkan daftar berkas yang belum berformat |
gofmt -d namafile.go | Menampilkan diff perubahan tanpa menulis ulang berkas |
gofmt -w namafile.go | Menulis ulang berkas langsung ke format standar |
Berikut hasil gofmt -d pada berkas uji yang sengaja ditulis buruk (indentasi spasi, struct tak sejajar, spasi berlebih di dalam kurung):
diff format-buruk.go.orig format-buruk.go
--- format-buruk.go.orig
+++ format-buruk.go
@@ -3,11 +3,11 @@
import "fmt"
type produk struct {
- id int
- nama string
+ id int
+ nama string
}
func main() {
- p := produk{id: 1, nama: "Buku Go"}
- fmt.Println( p.nama, p.id )
+ p := produk{id: 1, nama: "Buku Go"}
+ fmt.Println(p.nama, p.id)
}Diff di atas memperlihatkan tiga perbaikan khas gofmt: indentasi spasi menjadi tab, field struct id dan nama disejajarkan satu kolom, dan spasi berlebih di dalam kurung dirapikan. gofmt juga menjaga kebiasaan baris kosong: satu baris kosong memisahkan kelompok logika sehingga mata pembaca berhenti di tempat yang tepat.
Dengan penamaan, komentar, dan format yang seragam, kode kita terbaca seperti karya penulis Go mana pun. Kualitas berikutnya bergantung pada kemampuan mencari jawaban sendiri di dokumentasi resmi — topik chapter Membaca Dokumentasi Resmi .
Referensi #
- Effective Go — esai resmi gaya idiomatik Go.
- Doc comments — aturan penulisan komentar dokumentasi.
- Exported identifiers (Spesifikasi Go) — definisi resmi kapitalisasi dan visibilitas.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
konvensi-demo.go
Unduh berkaspackage main
import (
"errors"
"fmt"
)
// ErrAccountInactive adalah sentinel error untuk akun yang tidak aktif.
var ErrAccountInactive = errors.New("akun tidak aktif")
// Bagian "sebelum": nama-nama di bawah legal bagi compiler, tetapi
// menyimpang dari konvensi komunitas Go (snake_case, awalan get, ALL_CAPS).
type user_account_data struct {
user_id int
user_name string
email_address string
ACTIVE_STATUS bool
}
func (u user_account_data) get_user_name() string {
return u.user_name
}
func (u user_account_data) get_email_address() string {
return u.email_address
}
// Bagian "sesudah": nama singkat, MixedCaps, getter tanpa awalan Get,
// interface berakhiran -er, dan error berawalan Err.
type UserAccount struct {
id int
name string
email string
active bool
}
// Notifier menyampaikan pesan keluar dari sistem.
type Notifier interface {
Notify(message string) error
}
// NewUserAccount membuat akun baru yang siap dipakai.
func NewUserAccount(id int, name, email string) *UserAccount {
return &UserAccount{id: id, name: name, email: email, active: true}
}
// Name mengembalikan nama pemilik akun tanpa awalan Get.
func (u *UserAccount) Name() string {
return u.name
}
// Email mengembalikan alamat surel pemilik akun tanpa awalan Get.
func (u *UserAccount) Email() string {
return u.email
}
// Notify meneruskan pesan ke pemilik akun.
func (u *UserAccount) Notify(message string) error {
if !u.active {
return fmt.Errorf("notify %s: %w", u.email, ErrAccountInactive)
}
fmt.Printf("notifikasi ke %s: %s\n", u.email, message)
return nil
}
func main() {
old := user_account_data{
user_id: 1,
user_name: "Budi",
email_address: "[email protected]",
ACTIVE_STATUS: true,
}
fmt.Println("sebelum :", old.get_user_name(), old.get_email_address())
account := NewUserAccount(2, "Siti", "[email protected]")
fmt.Println("sesudah:", account.Name(), account.Email())
err := account.Notify("Tagihan bulanan sudah terbit")
fmt.Println("hasil notifikasi:", err)
}