Bayangkan sebuah repo dengan lima programmer yang masing-masing membawa kebiasaan indentasi berbeda: satu memakai empat spasi, satu memakai dua spasi, satu lagi menaruh spasi acak di dalam tanda kurung. Pull request yang seharusnya berisi satu perubahan logika justru dipenuhi ratusan baris perubahan kosmetik, sehingga reviewer kesulitan menemukan perubahan yang benar-benar penting. Tool gofmt hadir untuk menutup debat ini selamanya: tool bawaan toolchain Go yang menulis ulang berkas ke satu gaya standar. Pada chapter ini kita akan menjalankan gofmt pada berkas yang sengaja ditulis berantakan, membaca keluaran diff-nya, lalu memverifikasi bahwa perilaku program tidak berubah sedikit pun.
Satu Gaya untuk Seluruh Ekosistem #
gofmt singkatan dari Go format. Perintah ini mem-parsing kode sumber, lalu mencetak ulang kode tersebut dengan aturan tetap: indentasi memakai tab, spasi mengikuti operator, dan penataan kurung tidak bisa dikonfigurasi. Ketiadaan opsi konfigurasi justru disengaja — tim Go percaya bahwa gaya pemformatan bukan topik yang layak diperdebatkan, sehingga energi lebih baik dihabiskan untuk desain program. Hasil praktisnya sederhana namun berdampak besar: kode yang dibaca di proyek mana pun terlihat sama, dan diff di code review hanya memuat perubahan makna, bukan kebisingan spasi.
gofmt bekerja pada level syntax tree, bukan penggantian teks sederhana. Karena perintah ini memahami struktur kode, hasil formatnya selalu valid secara sintaks selama masukan program Go yang valid.
Tiga Perintah Utama #
| Perintah | Fungsi |
|---|---|
gofmt -l . | Daftar berkas di direktori ini yang belum berformat standar |
gofmt -d berantakan.go | Tampilkan diff perubahan tanpa menulis ulang berkas |
gofmt -w berantakan.go | Tulis ulang berkas langsung ke format standar |
Tanda -l cocok untuk pemeriksaan cepat, -d untuk melihat apa yang akan berubah sebelum mengambil keputusan, dan -w untuk memperbaiki secara langsung. Seluruh demo pada chapter ini dijalankan nyata di direktori uji berisi satu berkas bernama berantakan.go.
Demo: Berkas yang Sengaja Berantakan #
Berkas berikut valid secara sintaks dan programnya berjalan, tetapi penulisannya melanggar semua kebiasaan gofmt: indentasi dua spasi, tanpa spasi pada operator := dan >=, serta spasi liar di sekitar argumen fmt.Printf:
package main
import "fmt"
func main() {
nama:="Budi"
umur:=25
if umur>=17 {
fmt.Printf( "%s sudah dewasa\n" , nama)
} else {
fmt.Printf( "%s masih anak\n",nama )
}
}Perintah gofmt -l memastikan berkas ini memang belum rapi:
gofmt -l .Output yang Dihasilkan:
berantakan.goSelanjutnya kita lihat diff yang diusulkan gofmt tanpa mengubah berkas:
gofmt -d berantakan.goOutput yang Dihasilkan:
diff berantakan.go.orig berantakan.go
--- berantakan.go.orig
+++ berantakan.go
@@ -3,11 +3,11 @@
import "fmt"
func main() {
- nama:="Budi"
- umur:=25
- if umur>=17 {
- fmt.Printf( "%s sudah dewasa\n" , nama)
- } else {
- fmt.Printf( "%s masih anak\n",nama )
- }
+ nama := "Budi"
+ umur := 25
+ if umur >= 17 {
+ fmt.Printf("%s sudah dewasa\n", nama)
+ } else {
+ fmt.Printf("%s masih anak\n", nama)
+ }
}Mari kita baca diff di atas baris demi baris:
- Baris
- nama:="Budi"menjadi+\tnama := "Budi": indentasi dua spasi diganti satu tab, dan operator:=kini diapit spasi. - Baris
- umur:=25menjadi+\tumur := 25: aturan spasi operator yang sama berlaku seragam untuk semua deklarasi. - Baris
- if umur>=17 {menjadi+\tif umur >= 17 {: pembanding>=juga diapit spasi, dan blok diindentasi dengan tab. - Baris
- fmt.Printf( "%s sudah dewasa\n" , nama)menjadi+\tfmt.Printf("%s sudah dewasa\n", nama): spasi setelah(dan sebelum,dihapus sehingga pemanggilan fungsi terbaca ringkas. - Baris
- fmt.Printf(...)dengan indentasi enam spasi diganti satu tab — blokelsekini sejajar dengan blokif.
Sekarang kita terapkan perubahan tersebut dan periksa ulang:
gofmt -w berantakan.go
gofmt -l .Perintah kedua tidak mencetak apa pun — tidak ada berkas yang dilaporkan, artinya seluruh berkas di direktori sudah memenuhi format standar. Berkas hasil format berbunyi:
package main
import "fmt"
func main() {
nama := "Budi"
umur := 25
if umur >= 17 {
fmt.Printf("%s sudah dewasa\n", nama)
} else {
fmt.Printf("%s masih anak\n", nama)
}
}Pemformatan tidak menyentuh logika program sama sekali. Program tetap berjalan dengan keluaran yang identik:
go run berantakan.goOutput yang Dihasilkan:
Budi sudah dewasaBerkas Contoh Terformat #
Berkas contoh.go pada bagian bawah halaman memperlihatkan hasil gaya gofmt pada program yang sedikit lebih kaya: struct dengan field sejajar, slice literal, dan for range. Seluruh berkas berbunyi:
package main
import "fmt"
// Pengguna menyimpan identitas dasar seorang pembeli.
type Pengguna struct {
Nama string
Umur int
}
// kategori mengembalikan label kelompok umur seorang pengguna.
func kategori(u Pengguna) string {
if u.Umur >= 17 {
return "dewasa"
}
return "anak"
}
func main() {
pengguna := []Pengguna{
{Nama: "Budi", Umur: 25},
{Nama: "Siti", Umur: 12},
}
for _, p := range pengguna {
fmt.Printf("%s berumur %d tahun: %s\n", p.Nama, p.Umur, kategori(p))
}
}Jalankan dengan perintah berikut:
go run contoh.goOutput yang Dihasilkan:
Budi berumur 25 tahun: dewasa
Siti berumur 12 tahun: anakPerhatikan dua detail gaya pada berkas tersebut: field struct Nama string dan Umur int ditata sejajar dalam satu kolom, sementara elemen slice literal {Nama: "Budi", Umur: 25} diberi indentasi satu tab per elemen.
Karena gofmt sudah menjadi kebiasaan harian, pemeriksaan gaya tinggal dijaga di pintu masuk proyek. Langkah berikutnya yang sama pentingnya adalah merapikan daftar import secara otomatis dengan goimports pada chapter berikutnya.
Referensi #
- gofmt: paksa gaya penulisan yang rapi — artikel blog resmi tim Go tentang filosofi gofmt.
- Dokumentasi perintah gofmt — rujukan resmi seluruh flag gofmt.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
contoh.go
Unduh berkaspackage main
import "fmt"
// Pengguna menyimpan identitas dasar seorang pembeli.
type Pengguna struct {
Nama string
Umur int
}
// kategori mengembalikan label kelompok umur seorang pengguna.
func kategori(u Pengguna) string {
if u.Umur >= 17 {
return "dewasa"
}
return "anak"
}
func main() {
pengguna := []Pengguna{
{Nama: "Budi", Umur: 25},
{Nama: "Siti", Umur: 12},
}
for _, p := range pengguna {
fmt.Printf("%s berumur %d tahun: %s\n", p.Nama, p.Umur, kategori(p))
}
}