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 lokalmaxRetry, userNamemax_retry, user_name
Nama konstantastatusOK, defaultTimeoutSTATUS_OK, DEFAULT_TIMEOUT
SingkatanuserID, httpServer, xmlParseruserId, HTTPserver
Nama tipeTaskManager, UserAccounttask_manager, TManager
Panjang namacount, buf untuk cakupan keciltheTotalNumberOfItems

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:

PolaContohMakna bagi pembaca Go
Getter tanpa awalan Getuser.Name(), server.Addr()Fungsi pengambil nilai field; GetName() terasa janggal
Setter dengan awalan SetSetName(n string)Fungsi pengubah nilai field
Interface akhiran -erReader, Writer, NotifierSatu perilaku utama; nama dari kata kerja + -er
Sentinel errorErrNotFound, ErrTimeoutNilai error pembanding errors.Is
Tipe errorValidationError, ConfigErrorTipe error buatan sendiri, diakhiri Error
KonstruktorNewUserAccount(...), ParseTime(...)Fungsi pembuat nilai siap pakai
Nama packagestrconv, http, filepathSingkat, 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:

go
// 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:

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:

bash
go run konvensi-demo.go

Output yang Dihasilkan:

text
sebelum : Budi [email protected]
sesudah: Siti [email protected]
notifikasi ke [email protected]: Tagihan bulanan sudah terbit
hasil notifikasi: <nil>

Mari kita telaah bagian pentingnya:

  1. Baris 9 (var ErrAccountInactive): sentinel error berawalan Err, siap dibandingkan dengan errors.Is.
  2. Baris 14-27 (user_account_data dan kedua getter): versi “sebelum” — legal bagi compiler, tetapi melanggar MixedCaps dan menambah kebisingan baca.
  3. Baris 32-37 (UserAccount): tipe exported PascalCase; field unexported camelCase sejajar otomatis oleh gofmt.
  4. Baris 40-42 (Notifier): interface satu perilaku berakhiran -er, seperti Reader dan Writer.
  5. Baris 45-47 (NewUserAccount): konstruktor berawalan New; Parse juga lazim bila lebih deskriptif.
  6. Baris 49-65 (Name, Email, Notify): getter tanpa awalan Get; komentar tiap deklarasi diawali nama identifier.
  7. Baris 81 (fmt.Println("hasil notifikasi:", err)): mencetak nilai balik Notify berupa nil — 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:

PerintahFungsi
gofmt -l .Menampilkan daftar berkas yang belum berformat
gofmt -d namafile.goMenampilkan diff perubahan tanpa menulis ulang berkas
gofmt -w namafile.goMenulis 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):

text
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 #

Berkas Sumber

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

konvensi-demo.go

Unduh berkas
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)
}