1 poin oleh GN⁺ 3 jam lalu | 1 komentar | Bagikan ke WhatsApp
  • Go adalah pilihan untuk mengurangi kompleksitas berlebihan dalam pengembangan backend, dengan keunggulan utama kompilasi cepat, deploy satu binary, dan pengelolaan dependensi yang stabil
  • Alih-alih abstraksi rumit seperti decorator, metaclass, macro, trait, dan monad, Go memilih desain bahasa yang sederhana dengan fokus pada struct, fungsi, interface, goroutine, dan channel
  • Hanya dengan standard library dan tool bawaan seperti embed, html/template, net/http, database/sql, encoding/json, go test, dan pprof, kita bisa menangani web app, database, testing, benchmark, hingga profiling
  • Goroutine adalah unit eksekusi stackful dengan biaya sekitar 2KB, dan lewat channel, sync.Mutex, race detector, serta context.Context, penanganan concurrency dan propagasi pembatalan bisa dilakukan dengan sederhana
  • Alur go mod init, go build, scp, systemctl restart mendorong deploy sederhana berbasis satu binary Go dan Postgres, alih-alih node_modules, konfigurasi Docker·Kubernetes yang rumit, atau microservices yang berlebihan

Alasan memilih Go

  • Go adalah pilihan untuk mengurangi kompleksitas berlebihan dalam pengembangan backend, dengan keunggulan utama kompilasi cepat, deploy satu binary, dan pengelolaan dependensi yang stabil
  • Seperti HTML yang tetap menjadi alternatif terhadap kompleksitas berlebihan di frontend, Go juga telah hadir lebih dari 10 tahun sebagai pilihan untuk menyederhanakan backend
  • Untuk aplikasi CRUD sederhana atau aplikasi dengan sekitar 40 request per detik, terlalu berlebihan jika harus melibatkan banyak paket Node, tool build TypeScript, Kubernetes, tim platform Rails, sampai rewrite ke Rust
  • Arah Go lebih menekankan kode yang mudah dibaca, hasil yang bisa langsung dideploy, dan beban operasional yang kecil daripada “abstraksi pintar”

Desain bahasa yang sengaja dibuat membosankan

  • Go terasa membosankan karena memang dirancang seperti itu; ia tidak menyediakan abstraksi rumit seperti decorator, metaclass, macro, trait, atau monad
  • Komponen intinya dibatasi pada struct, fungsi, interface, goroutine, dan channel
  • Tujuannya adalah kesederhanaan, sampai-sampai spesifikasinya bisa dibaca dalam waktu singkat dan pada hari yang sama orang sudah bisa produktif menulis kode
  • Kebosanan ini justru menjadi keunggulan dalam codebase tim
    • Junior yang baru masuk bulan lalu pun bisa membaca kode yang ditulis principal dua tahun lalu
    • Karena gofmt memaksa satu format, perdebatan soal style kode jadi berkurang
    • Bahasa ini sendiri membuat abstraksi yang terlalu rumit sulit disisipkan ke dalam codebase

Standard library berperan sebagai framework

  • Go dapat digunakan untuk membuat web app hanya dengan standard library, tanpa framework web terpisah
  • Dengan embed, html/template, dan net/http, kita bisa membuat aplikasi yang menyertakan template HTML ke dalam binary dan merendernya lewat HTTP handler
package main

import (
    "embed"
    "html/template"
    "net/http"
)

//go:embed templates/*.html
var files embed.FS

var tmpl = template.Must(template.ParseFS(files, "templates/*.html"))

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        tmpl.ExecuteTemplate(w, "index.html", map[string]string{
            "Name": "asshole",
        })
    })

    http.ListenAndServe(":8080", nil)
}
  • Contoh ini adalah web app yang benar-benar berjalan, dan template HTML-nya ikut dikompilasi ke dalam binary
  • Tanpa webpack, Vite, development server, atau node_modules raksasa, kita cukup menjalankan go build lalu deploy satu file
  • Hanya dengan standard library dan tool dasar, sebagian besar pekerjaan backend bisa ditangani
    • Database: database/sql
    • JSON: encoding/json
    • Memanggil service lain: client net/http
    • Eksekusi konkuren: keyword go
    • Testing: go test
    • Benchmark: go test -bench
    • Profiling: pprof

Susunan standard library yang dalam

  • io.Reader dan io.Writer

    • io.Reader dan io.Writer masing-masing hanya interface dengan satu method, tetapi menjadi fondasi penting di seluruh ekosistem Go
    • Menghubungkan response body HTTP ke gzip writer lalu meneruskannya lagi ke file di disk bisa dilakukan dengan sedikit kode
    • Karena banyak package utama berbagi dua interface ini, pola yang sama bisa dipakai berulang kali di berbagai tempat
  • context.Context

    • context.Context adalah cara standar untuk propagasi pembatalan
    • Jika pengguna menutup tab browser, request context akan dibatalkan, dan sesudah itu query database serta pemanggilan HTTP turunan juga bisa ikut dibatalkan
    • Untuk menghindari kebocoran goroutine atau query zombie yang menghabiskan connection pool, context harus diteruskan sebagai argumen pertama dan dihormati
  • Package encoding

    • encoding/json, encoding/xml, encoding/csv, dan encoding/binary semuanya termasuk dalam standard library
    • Karena pola penggunaannya mirip, seperti struct tag dan decoding berbasis pointer, mempelajari satu package memudahkan penggunaan package lain

Model concurrency yang mengurangi penderitaan

  • Goroutine bukan OS thread itu sendiri, melainkan unit eksekusi stackful yang dimultipleks runtime di atas OS thread
  • Biaya memulai goroutine sekitar 2KB, sehingga membuat 100 ribu goroutine pun memungkinkan bahkan di laptop
  • Channel berfungsi sebagai pipa bertipe di antara goroutine; saat satu sisi mengirim dan sisi lain menerima, runtime yang menangani sinkronisasinya
  • Saat membutuhkan shared state, kita bisa memakai sync.Mutex, dan race detector akan membantu menemukan data race
  • Bahkan parallel HTTP fetcher bisa ditulis tanpa library tambahan, framework, atau ritual async/await
results := make(chan string, len(urls))
for _, url := range urls {
    go func(u string) {
        resp, _ := http.Get(u)
        results <- resp.Status
    }(url)
}
for range urls {
    fmt.Println(<-results)
}

Contoh route CRUD nyata

  • Route bergaya CRUD yang membaca post dari Postgres lalu merender HTML juga bisa disusun sesederhana hingga muat dalam satu layar
//go:embed templates/*.html
var tmplFS embed.FS

var tmpl = template.Must(template.ParseFS(tmplFS, "templates/*.html"))

type Post struct {
    ID    int
    Title string
    Body  string
}

func postsHandler(db *sql.DB) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        rows, err := db.QueryContext(r.Context(),
            "SELECT id, title, body FROM posts ORDER BY id DESC LIMIT 50")
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        defer rows.Close()

        var posts []Post
        for rows.Next() {
            var p Post
            if err := rows.Scan(&p.ID, &p.Title, &p.Body); err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
            }
            posts = append(posts, p)
        }

        tmpl.ExecuteTemplate(w, "posts.html", posts)
    }
}
  • Contoh ini memperlihatkan database, template, dan HTTP handler di satu tempat
  • Karena r.Context() diteruskan ke query SQL, saat koneksi ditutup query juga bisa dibatalkan
  • Tanpa ORM, container DI, service layer, atau direktori controllers/ penuh abstract base class, perilakunya bisa dipahami dengan membaca dari atas ke bawah

Pengelolaan dependensi yang tidak merusak akhir pekan

  • Saat memulai modul dengan go mod init, dependensi akan dicatat di go.mod dan go.sum
  • go.sum pada dasarnya adalah catatan kriptografis dari item yang benar-benar diunduh, sehingga kita bisa memeriksa bila dependensi yang masuk berbeda dari yang diharapkan
  • Tidak ada kompleksitas seperti direktori node_modules, lockfile drift antara environment development dan CI, peer dependencies, optional dependencies, devDependencies, atau peerDependenciesMeta
  • Jika butuh build offline, go mod vendor akan mengunduh dependensi ke direktori vendor/, dan toolchain akan menggunakannya secara otomatis
  • Seluruh project beserta dependensinya bisa dimasukkan ke satu tarball, yang menguntungkan dari sisi operasional dan review keamanan

Tool yang datang bersama compiler

  • Tool dasar Go disediakan tanpa plugin pihak ketiga atau file konfigurasi tambahan
  • gofmt menstandarkan format kode dan mengurangi perdebatan formatting serta membesarnya diff akibat perubahan spasi
  • go vet digunakan untuk menangkap kesalahan yang jelas
  • go test menjalankan testing
  • go test -race menjalankan testing dengan race detector untuk menemukan data race
  • go test -bench menjalankan benchmark
  • go test -cover memeriksa coverage testing
  • go tool pprof memungkinkan kita mendapatkan flame graph penggunaan CPU dan memori melalui HTTP endpoint dari service production yang sedang berjalan

Deploy selesai dengan perintah salin

  • Alur inti deploy Go adalah membangun binary, menyalinnya ke server, lalu menjalankannya
GOOS=linux GOARCH=amd64 go build -o myapp ./cmd/myapp
scp myapp user@server:/usr/local/bin/
ssh user@server 'systemctl restart myapp'
  • Alur ini memungkinkan deploy tanpa Dockerfile, multi-stage build, notifikasi CVE base image, manifest Kubernetes, Helm chart, ArgoCD, service mesh, atau sidecar
  • Dengan binary static-linked sekitar 12MB dan file unit systemd sepanjang 20 baris, deploy production sudah bisa dilakukan
  • Jika Docker memang diperlukan, cukup masukkan binary Go ke image FROM scratch

Perbandingan dengan framework

  • Framework seperti Rails, Django, Express, dan Next.js masing-masing membawa beban seperti prosedur deploy sendiri, ORM, admin, middleware, warning npm, dan perubahan konvensi routing
  • Binary Go dikompilasi lalu dijalankan, dan keunggulannya adalah stabilitas sehingga masih mungkin tetap berjalan 5 tahun kemudian
  • Di tengah kenyataan bahwa framework bisa lebih cepat ditinggalkan atau maintainernya mengalami burnout, model eksekusi Go yang sederhana jadi makin menonjol

Satu binary Go lebih baik daripada microservices

  • Microservices seharusnya bukan pilihan default; lebih baik menulis monolith terlebih dahulu
  • Susunan yang direkomendasikan adalah satu binary Go, satu Postgres, dan satu Redis hanya jika benar-benar perlu
  • HTML dan JSON API bisa disajikan dari port yang sama, dan seluruh aplikasi dapat berjalan di satu VPS
  • Karena biaya goroutine rendah dan penanganan concurrency kuat, Go dapat diskalakan hingga 10 ribu request per detik tanpa kesulitan berarti
  • Jika nantinya benar-benar perlu dipisah, package dalam monolith Go bisa dipindahkan ke repository terpisah
  • Karena interface sudah ada, bahasa ini secara alami mendorong struktur yang mempertimbangkan pemisahan

Generic dan penanganan error

  • if err != nil bukan bug, melainkan fitur
  • Ini memaksa kita memutuskan sendiri apa yang harus dilakukan di setiap titik kegagalan, tanpa menyembunyikan error
  • Tumpukan try/catch tidak menghilangkan error, hanya bisa menyembunyikannya sampai gangguan production benar-benar terjadi
  • Generic diperkenalkan pada Go 1.18, dan cukup dipakai saat memang diperlukan

Kesimpulan

  • Framework, microservices, rewrite ke Rust, atau meta-framework JavaScript baru tidak selalu diperlukan
  • Jalankan go mod init, tulis main.go, embed template, lalu kompilasi dan deploy—alur sederhana itulah yang direkomendasikan
  • Pilihan yang membosankan adalah pilihan yang benar, dan Go adalah pilihan itu

1 komentar

 
GN⁺ 3 jam lalu
Pendapat di Lobste.rs
  • Bukan mau menyalahkan penyampai pesannya, tapi gaya tulisan blog seperti ini melelahkan dan kekanak-kanakan. Mungkin awalnya lucu, tapi makin sering diulang, rasa sebalnya naik secara eksponensial
    Meski begitu, Go tetap bagus. Belakangan ini aku pindah dari proyek TypeScript ke proyek Go, dan kesehatan mental serta semangat kerjaku membaik dengan cepat
    Aku menerima bahwa if err != nil bukan bug melainkan fitur, tapi tetap menganggapnya sebagai cacat terbesar Go. Kalau ada sum type, ini bisa dibuat jauh lebih ergonomis tanpa bergantung pada type assertion saat runtime

    • Menurutku ini lebih baik daripada tulisan AI yang berusaha main aman di semua sisi tapi sebenarnya tidak punya posisi apa pun
    • Seru dibaca, tapi aku belum sering melihat tulisan seperti ini. Tetap saja, memanggil orang “walnut” menurutku lebih lucu daripada “dipshit”
      Kalau mau menulis seperti ini, setidaknya hinaannya harus sedikit lebih cerdas
    • Setuju. Apa ada cara untuk melaporkannya? Ini tidak cocok dengan kategori pelaporan mana pun
  • Dari komentar lain, sepertinya ini pendapat yang tidak populer, dan aku tidak ingin terdengar kasar, tapi aku benar-benar benci Go
    Go adalah bahasa dengan sintaks yang lumayan di atas runtime yang efisien untuk konkurensi, lalu ekosistemnya didorong dengan kekuatan Google. Selain itu menurutku mengerikan
    Masalah terbesarnya adalah bahasa ini tampak seperti sengaja dirancang untuk mengabaikan puluhan tahun riset desain bahasa pemrograman, atau bahkan praktik nyata di lapangan. Generics baru muncul setelah puluhan tahun
    Bukan berarti kita harus selalu memakai dependent types, tapi tetap ada batasnya. Di Go hampir tidak ada fitur untuk pemodelan data, pemodelan invariant, atau penataan kode yang seharusnya dimiliki bahasa modern. Rust memang punya kurva belajar yang lebih curam, tapi dalam hal ini jauh lebih baik, dan sebenarnya tidak perlu sistem tipe serumit Rust untuk hasil yang cukup bagus. Kalau yang dikhawatirkan adalah waktu kompilasi, sistem tipe yang sound, cepat, dan ekspresif tetap bisa dibuat hanya dengan fitur-fitur sederhana namun berguna
    Dan if err != nil menurutku adalah cara terburuk untuk memenuhi kode dengan kebisingan penanganan error. Aku tidak mengerti kenapa kubu Go begitu alergi pada sum type. Dalam hal ini bahkan exception di Java lebih baik. Kenyataannya, karena bahasanya tidak punya fitur yang lebih baik untuk menangani error, orang-orang jadi mengira tambalan terburuk yang mungkin itu adalah fitur
    Seandainya tulisan aslinya tidak sok pintar sejak awal, aku juga tidak akan menulis komentar seperti ini. “Pakai saja X” adalah ucapan bodoh. Pakailah alat yang cocok dengan use case, nyaman dipakai, dan produktif. Kalau itu Go, pakai Go. Kalau bukan, pilih yang lain

    • Menurutku posisi Go dalam ruang desain adalah pilihan yang memprioritaskan kesederhanaan bagi developer junior di codebase besar dan organisasi besar di atas hampir segala hal lain. Karena itu, developer yang kurang berpengalaman pun relatif mudah membaca kode dan memperbaiki hal-hal secara lokal tanpa harus membangun banyak konteks
      Ini terutama membantu di organisasi seperti Google, yang punya ribuan developer dan masa tinggal di tim atau perusahaan tertentu bisa singkat
      Dalam konteks seperti ini, terutama untuk developer yang belum matang, ketiadaan sistem tipe tingkat lanjut justru sampai batas tertentu menjadi kelebihan. Mereka hampir tidak perlu memikirkan tipe di luar konsep sangat dasar seperti tipe primitif atau struct. Bahasa ini hampir tidak memberi alat untuk memodelkan data, tapi sebagai gantinya memungkinkan banyak kode ditulis tanpa banyak berpikir
      Menurutku ini tidak bagus untuk ketepatan di level bahasa. Tapi di organisasi besar, beban itu lebih banyak ditopang oleh infrastruktur sekitar seperti analisis monorepo, CI/CD, canary testing, dan alat observabilitas. Infrastruktur itu menopang jauh lebih banyak dibanding di organisasi kecil
      Aku juga agak menyukai Go karena beban kognitifnya rendah. Aku hanya sesekali menulis kode untuk proyek tertentu, dan saat ini tidak terlibat mendalam setiap hari di proyek jangka panjang. Bisa masuk ke codebase yang tidak kulihat selama sebulan dan mengerjakan sesuatu dalam waktu kurang dari satu jam adalah kelebihan besar. Tapi kalau aku menjadi developer penuh waktu untuk proyek yang kompleks, kurasa aku akan kurang menyukainya
    • Dart juga bahasa Google yang tidak terlihat seperti mengabaikan puluhan tahun riset, tapi di luar Flutter hampir tidak ada yang memakainya. Go ya lumayan
    • Tulisan ini meniru format meme yang agresif dan sok pintar. Jadi sudah jelas akan memancing orang, dan menurutku sayang karena inti pesannya sebenarnya layak dibicarakan dengan benar, bukan dijadikan perang komentar
      Menurutku developer Go fokus merapikan fundamental, karena bahasa-bahasa sebelumnya dan komunitas riset teori bahasa pemrograman selama ini mengabaikan hal-hal mendasar itu. Orang-orang terobsesi pada sistem tipe yang paling komprehensif, padahal semakin kompleks dan ekspresif sistem tipenya, semakin kecil imbal hasilnya. Dan sekeras apa pun kita menggarap sistem tipe, itu tidak bisa menutupi package management yang buruk, build tool yang memaksa tim mempelajari DSL baru, sistem dokumentasi yang tidak otomatis membuat info tipe atau tautan dokumentasi paket pihak ketiga, standard library yang lemah, masalah performa serius, tidak adanya strategi kompilasi statis, waktu build yang menyakitkan, kurva belajar yang curam, sistem tipe yang menghukum, sintaks yang sulit dibaca, atau integrasi editor yang buruk
      Mengatakan Go sama sekali tidak punya fitur pemodelan data jelas salah. Di bahasa apa pun data dan invariant bisa dimodelkan, dan Go juga menyediakan sistem tipe yang cukup untuk menegakkan model itu
      Rust itu hebat, dan pilihan bagus bila kecepatan iterasi tidak penting, atau bila deploy ke bare metal, atau bila tuntutan correctness dan performa sangat tinggi. Tapi sebagai pilihan default untuk pengembangan aplikasi umum, terutama dalam tim, itu bukan pilihan yang bagus. Memang kita banyak mengetik if err != nil, tapi menurutku tidak ada orang yang bottleneck-nya ada pada jumlah penekanan tombol per detik
    • Selain Rust, hampir tidak ada bahasa modern yang punya fitur semacam ini. Kecuali kalau kamu memang ingin memrogram dengan Gleam atau Swift, tapi kalau sudah seniche itu ya sekalian saja pakai Haskell
  • Pernyataan bahwa if err != nil bukan bug melainkan fitur, dan membuatmu melihat semua titik yang berpotensi bermasalah, itu salah
    Pada praktiknya ini tidak dipaksakan. Mengabaikan error justru lebih mudah kalau kamu tidak memeriksanya sendiri
    Untuk cara menangani atau meneruskan error, Rust tetap jadi contoh yang menonjol

    • Betul. Tanpa sesuatu seperti errcheck, error terlalu mudah diabaikan, dan itu memang bodoh. Setidaknya seharusnya dipaksa untuk membuang error secara eksplisit
      Untungnya, semua proyek Go yang kukerjakan beberapa tahun terakhir memakai golangci-lint di atas pemeriksaan statis bawaan Go yang lemah. Jujur saja, ini seharusnya wajib di semua proyek Go
    • Dalam hal ini Swift lebih baik. Secara fungsional modelnya sama, tapi di Swift propagasi error antar-library jadi lebih mudah. Namun itu hanya perbedaan trade-off dan pilihan desain, bukan soal lebih baik atau lebih buruk secara mutlak
  • Aku benar-benar benci tren gaya penulisan seperti ini, tapi aku setuju dengan inti yang mau disampaikan tulisan itu
    Ungkapan “tidak ada node_modules seukuran Volkswagen” memang benar, tapi yang ada hanyalah cache paket global di ~/go, bukan node_modules lokal per proyek

    • Dan itu juga mengotori direktori home. Bahkan tidak diawali titik. Aku tidak tahu bagaimana ini bisa dianggap wajar
    • Sebelum mengejek ukuran pohon dependensi di ekosistem bahasa lain, aku selalu ingin menyuruh orang menjalankan wc -l go.sum dulu
  • Begitu membuka halaman dan melihat “Hey, dipshit.”, aku langsung menutupnya

  • Ini punya masalah yang sama seperti kebanyakan tulisan yang mengagung-agungkan bahasa pemrograman. Fokusnya bukan pada betapa hebat bahasa saat ini, melainkan pada betapa buruk bahasa yang sebelumnya dipakai
    Penulisnya tampaknya sangat menderita dengan Ruby dan TypeScript, mungkin juga Python, lalu Go menyelesaikan masalah itu baginya. Tapi aku tidak memakai Ruby atau TypeScript, jadi tulisannya tidak terlalu mengena bagiku
    Rasanya seperti sudah puluhan kali membaca variasi seperti ini selama bertahun-tahun. Karena punya static typing tidak seperti Python dan JavaScript, pakailah Haskell. Karena bisa dideploy sebagai satu biner tidak seperti Perl dan Erlang, pakailah Rust. Karena punya konkurensi dan channel yang layak tidak seperti Ruby dan Tcl, pakailah Elixir
    Aku senang penulisnya menemukan bahasa yang cocok untuk dirinya, tapi aku tidak akan mengikuti sarannya

    • Sepertinya ada cukup banyak orang di sini yang merasa perlu menjual Go kepada pembaca Lobsters. Bagi sebagian orang ini justru bisa menjadi bumerang
  • Nilai nol di Go selalu terasa seperti kekurangan bagiku. Menurutku lebih baik memaksa pengguna menyatakan nilai default secara eksplisit. Selain itu, untuk bahasa yang bukan OCaml, Go lumayan bagus

    • Aku suka nilai nol dan menurutku itu cukup cerdas. Tapi aku sangat merindukan fitur penetapan nilai default. Misalnya, sangat sulit melakukan marshal objek JSON yang bool-nya harus bernilai true saat tidak diisi
  • Pengalaman deploy dan kompilasinya luar biasa, tapi aku benar-benar tidak suka menulis bahasanya sendiri. Setiap kali memakainya, pengalamannya buruk. Apakah ada bahasa lain yang tidak seketat Go tapi tetap punya pengalaman deploy yang bagus?
    Apa ada sesuatu yang kulewatkan dari Go?
    Aku baru-baru ini mencoba men-deploy aplikasi Rails kecil, dan butuh terlalu banyak konfigurasi, jadi aku jelas jadi lebih bisa menghargai kelebihan Go

    • Belakangan ini aku mulai mengompilasi proyek Rust ke x86_64-unknown-linux-musl. Dengan begitu hasilnya adalah biner statis yang bisa langsung dijalankan di semua mesin Linux 64-bit. Lalu aku memindahkannya pakai scp dan menjalankannya
      Masih ada masalah harus menetapkan port dan memulainya secara manual, tapi aku berencana menyelesaikannya dengan sedikit sihir systemd
    • Dari sisi pengalaman deploy, di perusahaan kami justru cukup sukses dengan nix bundler. Sebagai konteks, kami membuat aplikasi GUI Qt6
      Dengan bundler itu, kami bisa membuat satu executable yang bisa dijatuhkan ke mesin Linux distro lain, dan bahkan jika Qt belum terpasang, pengguna cukup menjalankan executable-nya dan seluruh GUI akan berfungsi
      Tapi ada catatan bahwa driver OpenGL bisa menimbulkan masalah. Tetap mungkin dilakukan, hanya saja lebih rumit daripada sekadar “salin lalu jalankan”
  • Masalah terbesar adalah orang mengklaim Go dirancang untuk konkurensi, tapi bahasa ini justru punya pointer mentah bawaan yang mudah sekali dipakai untuk berbagi state tanpa sengaja

  • Kebosanan itu sendiri tidak masalah, tapi menurutku Go justru sangat gagal menjadi bahasa yang benar-benar membosankan
    Katanya “tidak ada decorator”, tapi ada struct tags dan reflection. Sulit memahami bagaimana keduanya berinteraksi sampai kamu benar-benar menjalankannya
    Interface struktural dan reflection adalah sumber menakutkan dari perubahan perilaku yang muncul dari tempat yang jauh. Cukup tambahkan satu method yang salah ke struct, dan perilaku library bisa berubah total
    Dari sudut pandang dokumentasi juga aneh. Mengapa orang tidak ingin secara jelas menunjukkan interface apa yang memang dimaksudkan untuk dipenuhi oleh suatu tipe?
    Aku juga tidak mengerti kenapa goroutine tidak disebut thread saja
    Dan kenapa channel harus menjadi fitur bahasa? Menurutku karena mereka terlambat 10 tahun untuk mengakui bahwa generics berguna untuk lebih dari sekitar tiga jenis tipe

    • Goroutine bukan thread, melainkan abstraksi yang lebih ringan yang berjalan di atas thread pool. Karena itu, kamu bisa dengan mudah membuat ribuan goroutine
      Menurutku channel menjadi bagian dari runtime agar scheduler goroutine mengetahui channel itu, sehingga lebih mudah membangunkan goroutine penerima ketika channel tidak lagi kosong. Mungkin cara ini memang lebih mudah
    • Goroutine itu green thread dengan berbagai fasilitas tambahan