Web Artisan
Beranda

Progress belajar

Modul 3 dari 73

0% 0/73 modul selesai

Setelah selesai, tandai modul ini agar progres kursus tetap rapi.

Progress disimpan lokal di browser ini.

Roadmap 1 · Fondasi

Variabel, Tipe,
dan Zero Value

Di Go, data tidak sekadar disimpan. Data diberi kontrak tipe yang jelas sejak program dikompilasi, jauh sebelum baris pertama dijalankan.

Bahasa: Go 1.26~70 menit bacaProyek: Online Shop Skincare
01

Kenapa Sistem Tipe Go Terasa Berbeda

Dari fleksibilitas JavaScript menuju kontrak eksplisit Go

Kamu sudah mengenal TypeScript sebagai lapisan tipe di atas JavaScript, tetapi Go membawa tipe turun ke level bahasa dan compiler, bukan sekadar bantuan saat menulis kode.

Di JavaScript, sebuah nilai bisa berganti tipe saat program berjalan. Di TypeScript, tipe membantu saat development, tetapi hasil akhirnya tetap JavaScript yang dieksekusi runtime. Di Go, tipe adalah bagian dari bahasa yang diperiksa compiler sebelum program berjalan, sehingga banyak bug ketahuan lebih awal, terutama di backend yang berurusan dengan harga, stok, status produk, dan payload API.

🌉Jembatan: dari TypeScript ke Go

Anggap Go seperti TypeScript tanpa mode longgar: tidak ada any sebagai jalan pintas harian, tidak ada coercion diam-diam, dan nilai kosong pun selalu punya bentuk yang jelas menurut tipenya.

Ada satu perbedaan halus yang penting untuk pembaca dari TypeScript. TypeScript memakai structural typing, jadi dua tipe dengan bentuk sama dianggap kompatibel. Go memakai named typing, jadi type ProductStatus string adalah tipe yang benar-benar baru dan tidak otomatis bisa dipertukarkan dengan string. Konsekuensi ini akan terasa jelas saat kita membahas custom type.

sistem tipe Go

Sekumpulan aturan yang menentukan jenis nilai apa yang boleh disimpan, dikirim ke fungsi, dibandingkan, dikonversi, dan dikembalikan oleh program Go, diperiksa oleh compiler sebelum eksekusi.

Modul ini tidak mengajari variabel dari nol. Fokusnya adalah cara berpikir Go saat kamu memodelkan data backend online shop skincare, lewat var dan :=, const, tipe dasar, zero value, konversi tipe, custom type, dan type alias.

Dokumentasi resmi yang relevan: Go Specification, A Tour of Go: Basic types, A Tour of Go: Zero values, Constants (Go Blog), dan Go Modules Reference.

02

var dan Short Declaration

Dua cara deklarasi, dua konteks penggunaan

Go punya var untuk deklarasi yang bisa eksplisit dan := untuk deklarasi ringkas di dalam fungsi.

Di JavaScript kamu terbiasa memakai let dan const. Di Go, pilihan paling sering adalah var atau short declaration :=. Keduanya membuat variabel, tetapi aturan tempat pakainya berbeda, dan perbedaan itulah yang sering membuat pendatang baru tersandung.

JavaScript / TypeScript
  • let price = 129000 membuat variabel runtime.
  • const status = "active" mengunci binding, bukan selalu isi objek.
  • Tipe bisa diinfer TypeScript, tetapi runtime tetap JavaScript.
Go
  • var price int = 129000 boleh di level package atau di dalam fungsi.
  • status := "active" hanya boleh di dalam fungsi.
  • Tipe diinfer compiler dan menjadi bagian dari program Go.

Tiga bentuk deklarasi yang perlu kamu kenal

Go memberi tiga cara menulis variabel: eksplisit dengan tipe, var dengan inferensi, dan short declaration. Ketiganya menghasilkan variabel, hanya beda gaya dan tempat.

cmd/playground/declare.go
package main import "fmt" func main() { var price int = 129000 // var eksplisit dengan tipe var stock = 12 // var dengan inferensi tipe (int) name := "Niacinamide Serum" // short declaration, hanya di dalam fungsi fmt.Println(price, stock, name) }

Aturan praktis

Gunakan var saat kamu butuh deklarasi di level package, ingin menulis tipe secara jelas, atau sengaja memanfaatkan zero value. Gunakan := untuk nilai lokal di dalam fungsi saat tipenya sudah jelas dari sisi kanan.

flowchart TD
  A["Mau mendeklarasikan variabel"] --> B{"Di dalam fungsi?"}
  B -->|"Tidak, level package"| C["Pakai var, const, type, atau func"]
  B -->|"Ya, di dalam fungsi"| D{"Tipe jelas dari sisi kanan?"}
  D -->|"Ya, ingin ringkas"| E["Pakai short declaration :="]
  D -->|"Butuh tipe eksplisit atau zero value"| F["Pakai var"]

Gambar 1. var bisa dipakai di mana saja, sedangkan := hanya hidup di dalam fungsi. Saat ragu di level package, jawabannya selalu var, const, type, atau func.

cmd/playground/main.go
package main import "fmt" var appName string = "skincare-backend" var maxFeaturedProducts int func main() { storeName := "GlowLab" isActive := true fmt.Println(appName) fmt.Println(storeName) fmt.Println(isActive) fmt.Println(maxFeaturedProducts) }

Perhatikan maxFeaturedProducts tidak diberi nilai awal, tetapi Go tetap memberinya nilai aman, yaitu 0 untuk int. Kita bahas lebih dalam di section zero value.

⚠️Jebakan: := bukan pengganti var di semua tempat

:= hanya boleh dipakai di dalam fungsi. Untuk level package, pakai var, const, type, atau deklarasi fungsi.

cmd/playground/invalid.go
package main productName := "Hydrating Toner" // tidak valid di level package func main() {}
contoh error compiler
syntax error: non-declaration statement outside function body

Short declaration harus memperkenalkan minimal satu nama baru

Di Go, := bukan operator update. Ia mendeklarasikan variabel baru. Bila semua nama di sisi kiri sudah ada dalam scope yang sama, compiler menolak. Namun bila ada minimal satu nama baru, := boleh dipakai walau sebagian nama lama ikut ditugasi ulang.

cmd/playground/redeclare.go
package main import "fmt" func main() { quantity := 2 quantity = 3 // assignment biasa, bukan deklarasi productName, quantity := "Serum", 4 // valid: productName nama baru fmt.Println(productName, quantity) }

Baris productName, quantity := "Serum", 4 valid karena productName adalah nama baru, walaupun quantity sudah ada. Pola ini sering muncul saat fungsi mengembalikan dua nilai, misalnya result, err := doSomething(), lalu err dipakai ulang di pemanggilan berikutnya.

03

const dan Tipe Dasar

Konstanta Go bukan object freeze ala JavaScript

const di Go menyimpan nilai konstan untuk angka, string, rune, dan boolean, bukan object, slice, atau map.

Di JavaScript, const berarti binding tidak bisa diarahkan ke nilai lain, tetapi object atau array di dalamnya tetap bisa dimutasi. Di Go, const adalah nilai yang sudah pasti pada compile-time untuk jenis nilai tertentu, jadi kamu tidak membuat konstanta slice, map, struct, atau objek kompleks.

🌉Jembatan: const JS vs const Go

const JavaScript mengunci nama variabel, sedangkan const Go mendeklarasikan nilai konstan yang sudah diketahui compiler. Karena itu const Go cocok untuk status teks, batas angka, kode mata uang, atau flag tetap.

internal/product/constants.go
package product const DefaultPageSize = 20 const MaxPageSize = 100 const CurrencyCode = "IDR" const IsCatalogPublic = true

Tipe dasar yang akan sering kamu pakai di awal proyek skincare adalah string, int, int64, float64, dan bool.

string

Untuk nama produk, slug, SKU, kategori, email, dan status berbasis teks.

int

Untuk hitungan di memori aplikasi, seperti quantity di cart atau jumlah hasil.

int64

Untuk ID database, harga dalam rupiah, dan nilai numerik yang butuh ukuran eksplisit.

float64

Untuk angka yang memang pecahan seperti rating rata-rata, bukan untuk uang.

bool

Untuk flag seperti aktif, terbit, tersedia, atau alamat default.

const

Untuk nilai tetap seperti default page size, kode mata uang, atau batas paginasi.

internal/product/basic_types.go
package product type Product struct { ID int64 Name string PriceRupiah int64 Quantity int IsActive bool }
⚠️Uang disimpan sebagai integer

Sepanjang proyek ini, harga disimpan sebagai PriceRupiah int64, bukan float64. Floating point menyimpan 0.1 + 0.2 tidak persis, dan untuk uang itu bisa membuat total checkout meleset satu rupiah. Float kita simpan hanya untuk nilai yang memang pecahan seperti rating.

Konstanta typed dan untyped

Ini bagian yang sering membingungkan, padahal sangat berguna. Konstanta tanpa tipe eksplisit, seperti const DefaultPageSize = 20, disebut untyped constant. Ia punya default type (di sini int), tetapi bisa menyesuaikan diri ke konteks numeric lain tanpa konversi manual selama nilainya muat.

untyped constant

Konstanta yang dideklarasikan tanpa tipe, misalnya const x = 20. Ia punya default type (int, float64, string, bool, atau rune) tetapi bisa otomatis menyesuaikan diri saat dipakai di konteks tipe lain yang kompatibel.

cmd/playground/const_kind.go
package main import "fmt" const DefaultPageSize = 20 // untyped int constant const MaxPageSize int = 100 // typed: selalu int func main() { var limit int = DefaultPageSize // ok var ratio float64 = DefaultPageSize // ok juga, untyped menyesuaikan ke float64 // var bad float64 = MaxPageSize // ERROR: butuh float64(MaxPageSize) var ok float64 = float64(MaxPageSize) fmt.Println(limit, ratio, ok) }

Karena DefaultPageSize untyped, ia bisa langsung dipakai sebagai int maupun float64. Sebaliknya MaxPageSize sudah typed int, jadi memakainya sebagai float64 butuh konversi eksplisit. Aturan sederhananya: biarkan konstanta untyped kecuali kamu memang ingin mengunci tipenya.

💡Default biarkan untyped

Untuk angka dan teks tetap seperti DefaultPageSize atau CurrencyCode, tulis tanpa tipe agar fleksibel. Tambahkan tipe hanya saat kamu sengaja ingin membatasi, misalnya const MaxRetry uint8 = 3.

04

Zero Value, Nilai Awal yang Aman

Konsep penting yang tidak punya padanan langsung di JavaScript

Di Go, variabel yang dideklarasikan tanpa nilai selalu mendapat nilai awal sesuai tipenya, jadi tidak pernah ada keadaan undefined.

Di JavaScript, variabel yang belum diisi bernilai undefined. Di Go tidak ada undefined. Numeric menjadi 0, string menjadi string kosong, bool menjadi false, sedangkan pointer, slice, dan map menjadi nil.

🌉Jembatan: undefined vs zero value

Di JS, variabel tanpa isi sering berarti undefined, sumber klasik bug cannot read property of undefined. Di Go, variabel tanpa isi tetap punya nilai valid menurut tipenya, jadi banyak kelas bug itu hilang sejak awal.

zero value

Nilai default otomatis yang diberikan Go ketika variabel dideklarasikan tanpa initializer: 0 untuk numeric, "" untuk string, false untuk bool, dan nil untuk pointer, slice, dan map.

cmd/playground/zero_value.go
package main import "fmt" func main() { var quantity int var productName string var price float64 var isActive bool var note *string fmt.Printf("quantity: %d\n", quantity) fmt.Printf("productName: %q\n", productName) fmt.Printf("price: %.2f\n", price) fmt.Printf("isActive: %t\n", isActive) fmt.Printf("note is nil: %t\n", note == nil) }
output
quantity: 0 productName: "" price: 0.00 isActive: false note is nil: true
flowchart LR
  A["Deklarasi tanpa nilai awal"] --> B{"Tipe variabel"}
  B -->|"int, int64, float64"| C["0"]
  B -->|"string"| D["string kosong"]
  B -->|"bool"| E["false"]
  B -->|"pointer, slice, map"| F["nil"]
  C --> G["Variabel langsung valid, bukan undefined"]
  D --> G
  E --> G
  F --> G

Gambar 2. Zero value membuat setiap variabel Go punya nilai awal yang valid menurut tipenya, sehingga tidak ada keadaan undefined seperti di JavaScript.

Zero value juga berlaku untuk struct. Sebuah Product{} kosong langsung bisa dipakai, dan setiap field-nya terisi zero value masing-masing.

cmd/playground/zero_struct.go
package main import "fmt" type Product struct { ID int64 Name string PriceRupiah int64 IsActive bool } func main() { var p Product // semua field zero value fmt.Printf("%+v\n", p) // {ID:0 Name: PriceRupiah:0 IsActive:false} }
⚠️Zero value valid secara tipe, belum tentu valid secara bisnis

Quantity bernilai 0 valid sebagai angka, tetapi mungkin tidak sah untuk checkout. Status berupa string kosong valid sebagai tipe, tetapi tidak masuk akal sebagai status produk. Karena itu validasi domain tetap diperlukan.

internal/cart/validation.go
package cart import "errors" var ErrInvalidQuantity = errors.New("quantity must be greater than zero") func ValidateQuantity(quantity int) error { if quantity <= 0 { return ErrInvalidQuantity } return nil }
💡Idiom Go

Zero value sering dimanfaatkan agar struct bisa langsung dipakai setelah deklarasi tanpa constructor. Tetapi aturan bisnis tetap harus memvalidasi apakah nilai itu masuk akal sebelum disimpan atau diproses.

05

Konversi Tipe Eksplisit

Tidak ada implicit coercion seperti JavaScript

Go tidak mengubah tipe numeric secara diam-diam. Setiap perpindahan tipe harus kamu tulis eksplisit, agar pembaca dan compiler sama-sama sadar.

JavaScript sangat permisif. Ekspresi "10" + 2 menghasilkan string, sementara "10" - 2 menghasilkan angka. Di backend, perilaku seperti ini berbahaya karena nilai dari request, database, dan kalkulasi bisnis bisa tercampur tanpa sadar. Go memilih pendekatan eksplisit: int, int64, dan float64 adalah tipe berbeda, dan kamu harus mengubahnya secara sadar.

flowchart LR
  A["Nilai bertipe int"] -->|"butuh float64(x)"| B["Nilai bertipe float64"]
  B -->|"butuh int64(y)"| A
  C["int * float64 tanpa konversi"] --> D["compile error: mismatched types"]

Gambar 3. int dan float64 adalah dua pulau tipe terpisah. Satu-satunya jembatan adalah konversi eksplisit; operasi langsung antar pulau ditolak compiler.

Contoh nyata di toko skincare adalah menghitung rating rata-rata. Total bintang dan jumlah review keduanya int, tetapi hasilnya harus pecahan, jadi kita konversi dulu.

cmd/playground/rating.go
package main import "fmt" func main() { totalStars := 22 reviewCount := 5 naive := totalStars / reviewCount // 4, pembagian int membuang sisa avg := float64(totalStars) / float64(reviewCount) // 4.4 fmt.Println(naive) fmt.Printf("%.1f\n", avg) }

Kode berikut tidak valid karena reviewCount bertipe int, sedangkan avgRating bertipe float64.

cmd/playground/invalid_conversion.go
package main func main() { reviewCount := 5 avgRating := 4.4 _ = reviewCount * avgRating // tidak valid }
contoh error compiler
invalid operation: reviewCount * avgRating (mismatched types int and float64)
🌉Jembatan: coercion JS vs conversion Go

JavaScript sering melakukan coercion implisit yang sulit dilacak. Go memaksa kamu menulis float64(reviewCount) agar perubahan tipe terlihat jelas di kode, bukan terjadi diam-diam.

Harga tetap int64, konversi hanya untuk hitung persen

Karena uang disimpan int64, perhitungan diskon persen harus mampir sebentar ke float64, lalu kembali ke int64. Di sinilah konversi eksplisit benar-benar terpakai di domain.

internal/pricing/discount.go
package pricing import "math" // ApplyPercentDiscount memotong harga (rupiah) sebesar persen tertentu. // Uang tetap int64; float64 hanya dipakai sesaat untuk hitung persen, lalu dibulatkan. func ApplyPercentDiscount(priceRupiah int64, percent float64) int64 { discounted := float64(priceRupiah) * (1 - percent/100) return int64(math.Round(discounted)) }
⚠️Konversi float ke int memotong, bukan membulatkan

int64(2.9) menghasilkan 2, bukan 3. Konversi numeric ke integer di Go selalu membuang bagian desimal. Bila kamu memang ingin membulatkan harga diskon, pakai math.Round sebelum konversi seperti contoh di atas.

Konversi numeric ke string butuh package khusus

Untuk mengubah angka menjadi teks tampilan, pakai package strconv atau fmt. Jangan memakai string(65) untuk membuat teks angka, karena itu mengubah kode karakter menjadi rune.

cmd/playground/strconv.go
package main import ( "fmt" "strconv" ) func main() { productID := int64(42) quantity := 3 idText := strconv.FormatInt(productID, 10) qtyText := strconv.Itoa(quantity) bad := string(65) // "A", bukan "65": go vet akan memperingatkan ini fmt.Println(idText, qtyText, bad) }
🌉Jembatan: String() / Number() vs strconv

Di JS kamu memakai String(x) dan Number(x) atau parseInt. Di Go, arah angka ke teks memakai strconv.Itoa atau strconv.FormatInt, sedangkan teks ke angka memakai strconv.Atoi atau strconv.ParseInt, yang sama-sama mengembalikan error bila format gagal. Pola ini akan sering kamu pakai saat membaca query param di modul API.

06

Custom Type dan Type Alias

Membedakan domain type dari sekadar tipe bawaan

Go memungkinkan kamu membuat tipe baru agar aturan domain lebih ekspresif dan lebih sulit tertukar.

Custom type dibuat dengan bentuk type Nama TipeDasar. Hasilnya tipe baru yang berbeda dari tipe dasarnya, walaupun representasi nilainya sama. Alias dibuat dengan bentuk type Nama = TipeDasar, dengan tanda sama dengan, dan ia hanya nama lain untuk tipe yang persis sama.

flowchart TB
  subgraph c["type ProductStatus string  (custom type)"]
    A["ProductStatus"] -->|"tipe BARU, beda dari string"| B["butuh konversi: ProductStatus(s) atau string(ps)"]
  end
  subgraph al["type UserID = int64  (alias)"]
    C["UserID"] -->|"nama lain, tipe SAMA"| D["bebas dipertukarkan dengan int64"]
  end

Gambar 4. Tanpa tanda sama dengan, type membuat tipe baru yang butuh konversi. Dengan tanda sama dengan, type hanya memberi nama lain untuk tipe yang sama.

internal/product/status.go
package product type ProductStatus string const ( ProductStatusDraft ProductStatus = "draft" ProductStatusActive ProductStatus = "active" ProductStatusArchived ProductStatus = "archived" ProductStatusOutOfStock ProductStatus = "out_of_stock" ) func (s ProductStatus) IsSellable() bool { return s == ProductStatusActive }

Custom type seperti ProductStatus membuat kode domain lebih jelas. Fungsi yang menerima ProductStatus tidak bisa sembarangan diberi string lain tanpa konversi eksplisit, sehingga status acak tidak gampang menyelinap masuk.

internal/product/check.go
package product func CanShowInCatalog(status ProductStatus, isActive bool) bool { return isActive && status.IsSellable() }
🌉Jembatan: TS structural vs Go nominal

Di TypeScript, type UserId = number hanya alias struktural, jadi number apa pun bisa masuk. Di Go, type UserID int64 tanpa tanda sama dengan membuat tipe nominal yang benar-benar baru, mirip branded type yang harus kamu ciptakan manual di TS. Inilah cara Go mencegah ID pengguna tertukar dengan ID produk.

Enum rapi dengan iota

Saat status tidak perlu disimpan sebagai teks, iota memberi cara ringkas membuat enum integer berurutan. Cocok untuk konsep berjenjang seperti tier membership pelanggan.

internal/customer/tier.go
package customer type MembershipTier int const ( TierBronze MembershipTier = iota // 0 TierSilver // 1 TierGold // 2 TierPlatinum // 3 ) func (t MembershipTier) FreeShipping() bool { return t >= TierGold }
⚠️Hati-hati iota untuk data yang disimpan

Nilai iota bergantung pada urutan baris, jadi menyisipkan tier baru di tengah dapat menggeser angka lama dan merusak data yang sudah tersimpan. Untuk enum yang ditulis ke database atau JSON, lebih aman memakai string seperti ProductStatus atau menetapkan angka eksplisit.

Type alias dan kapan memakainya

Alias tidak membuat tipe baru. Ia berguna saat kamu ingin memberi nama domain tanpa mengubah kompatibilitas tipe dasar, atau saat memindahkan tipe antar package secara bertahap tanpa memecah kode lama sekaligus.

internal/identity/types.go
package identity type UserID = int64
💡Kapan pilih custom type vs alias

Pilih custom type untuk konsep domain yang punya aturan sendiri, seperti ProductStatus, OrderStatus, atau MembershipTier. Pilih alias hanya untuk transisi, kompatibilitas, atau memperjelas pembacaan. Di kode aplikasi, custom type jauh lebih sering tepat daripada alias.

⚠️Rapikan format dengan gofmt

Setelah menulis const block atau struct, jalankan gofmt. Formatter Go merapikan alignment dan spacing secara otomatis, jadi tidak perlu berdebat soal gaya manual di code review.

07

Model Domain Online Shop Skincare

Dari tipe dasar menuju model produk yang nyata

Sekarang kita rangkai semua yang dipelajari menjadi model produk skincare yang sederhana tetapi aman.

Untuk online shop skincare, data produk minimal punya ID, nama, harga, stok, status, dan flag aktif. Kita mulai dari model ringan. Nanti, saat masuk roadmap domain dan database, model ini dipisah lebih rapi sesuai layer.

Struktur latihan variabel dan tipe
  • cmd/
  • playground/
  • main.go menjalankan contoh kecil
  • internal/
  • product/
  • product.go model produk sederhana
  • status.go custom type status produk
internal/product/status.go
package product type ProductStatus string const ( ProductStatusDraft ProductStatus = "draft" ProductStatusActive ProductStatus = "active" ProductStatusArchived ProductStatus = "archived" ProductStatusOutOfStock ProductStatus = "out_of_stock" ) func (s ProductStatus) IsSellable() bool { return s == ProductStatusActive }
internal/product/product.go
package product type Product struct { ID int64 Name string PriceRupiah int64 Quantity int Status ProductStatus IsActive bool } func (p Product) IsAvailable() bool { return p.IsActive && p.Quantity > 0 && p.Status.IsSellable() }
classDiagram
  class Product {
    +int64 ID
    +string Name
    +int64 PriceRupiah
    +int Quantity
    +ProductStatus Status
    +bool IsActive
    +IsAvailable() bool
  }
  class ProductStatus {
    <<string>>
    +IsSellable() bool
  }
  Product --> ProductStatus : Status

Gambar 5. Semua konsep modul ini berkumpul: tipe dasar untuk field, custom type ProductStatus dengan method-nya sendiri, dan int64 untuk harga agar uang tetap presisi.

cmd/playground/main.go
package main import ( "fmt" "github.com/kamu/skincare-backend/internal/product" ) func main() { serum := product.Product{ ID: 1001, Name: "Niacinamide Serum", PriceRupiah: 129000, Quantity: 12, Status: product.ProductStatusActive, IsActive: true, } fmt.Println(serum.Name) fmt.Println(serum.IsAvailable()) }
📝Kenapa model ini belum final

Model ini sengaja sederhana agar fokus ke tipe. Di modul berikutnya kita mulai bicara control flow, function, dan validasi yang lebih kaya, lalu memisahkan domain model dari DTO JSON saat masuk struct dan API.

08

Hands-on Ringan

Latihan kecil untuk menempelkan kebiasaan tipe eksplisit

Latihan ini membuat kamu merasakan langsung bedanya deklarasi, zero value, konversi, dan custom type dalam satu project kecil.

Siapkan folder latihan

Pakai project github.com/kamu/skincare-backend, lalu tambahkan folder cmd/playground dan internal/product.

Tulis custom type

Buat ProductStatus agar status produk tidak lagi sekadar string bebas.

Jalankan contoh

Pakai go run ./cmd/playground untuk melihat output struct produk sederhana.

Ubah nilai dan amati

Ganti Quantity menjadi 0, lalu lihat bagaimana IsAvailable berubah karena aturan domain, bukan karena tipe.

Terminal
mkdir -p cmd/playground internal/product go run ./cmd/playground

Tambahkan fungsi kecil untuk menghitung subtotal. Karena harga int64, konversi yang terpaksa kita lakukan adalah pada quantity, bukan pada uangnya.

internal/product/pricing.go
package product func CalculateSubtotal(priceRupiah int64, quantity int) int64 { return priceRupiah * int64(quantity) }

Lalu panggil dari main.go.

cmd/playground/main.go
package main import ( "fmt" "github.com/kamu/skincare-backend/internal/product" ) func main() { serum := product.Product{ ID: 1001, Name: "Niacinamide Serum", PriceRupiah: 129000, Quantity: 2, Status: product.ProductStatusActive, IsActive: true, } subtotal := product.CalculateSubtotal(serum.PriceRupiah, serum.Quantity) fmt.Println(serum.Name) fmt.Println(serum.IsAvailable()) fmt.Println(subtotal) // 258000 }
💡Cara belajar efektif

Saat compiler menolak kode, baca pesannya pelan-pelan. Di Go, error compiler sering menjadi guru terbaik untuk memahami tipe, terutama pesan mismatched types yang menunjukkan persis di mana konversi kurang.

09

Jebakan Umum dari JS dan PHP

Kesalahan kecil yang sering muncul saat pindah ke Go

Sebagian besar bug awal di Go muncul karena membawa kebiasaan bahasa dinamis ke bahasa yang lebih eksplisit.

Mengira := bisa di mana saja

:= hanya berlaku di dalam fungsi. Untuk package-level, pakai var, const, type, atau func.

Mengira string kosong sama dengan belum ada

"" adalah nilai valid. Bila perlu membedakan belum diisi, pakai desain yang jelas seperti pointer atau tipe nullable saat masuk database.

Mengandalkan coercion

Go tidak akan mengalikan int dengan float64 tanpa konversi. Ini disengaja agar perubahan tipe terlihat.

Lupa pembagian int membuang sisa

22 / 5 menghasilkan 4, bukan 4.4. Konversi ke float64 dulu bila kamu memang butuh hasil pecahan.

Memakai float untuk uang

Floating point tidak presisi. Simpan harga sebagai int64 rupiah dan konversi hanya sesaat untuk hitung persen.

Status sebagai string bebas

Custom type seperti ProductStatus membuat domain lebih aman daripada menyebar string literal di banyak tempat.

⚠️Jebakan shadowing

:= bisa membuat variabel baru di scope dalam dengan nama yang sama. Ini valid, tetapi kadang membuat kamu mengubah variabel yang salah tanpa sadar.

cmd/playground/shadowing.go
package main import "fmt" func main() { status := "active" if true { status := "draft" // variabel BARU, hanya hidup di dalam blok ini fmt.Println(status) } fmt.Println(status) // status luar tidak berubah }
output
draft active

Contoh di atas benar secara sintaks, tetapi bisa menyesatkan. Di service layer nanti, shadowing seperti ini bisa membuat hasil validasi atau assignment terlihat benar padahal nilai di luar blok tidak pernah berubah.

10

Ringkasan & Poin Penting

Variabel dan tipe adalah fondasi kecil yang menentukan seberapa aman model domain kita saat proyek membesar.

Yang Wajib Menempel

  • var bisa dipakai di level package dan fungsi, sedangkan := hanya untuk deklarasi lokal di dalam fungsi.
  • const Go adalah nilai compile-time untuk string, boolean, rune, dan numeric, bukan object atau array seperti pola JavaScript.
  • Konstanta untyped menyesuaikan diri ke konteks tipe, sedangkan konstanta typed mengunci tipenya dan butuh konversi.
  • Zero value membuat variabel Go selalu punya nilai awal: numeric 0, string kosong, bool false, dan pointer, slice, map nil.
  • Go tidak melakukan coercion implisit antar tipe numeric. Konversi seperti float64(quantity) harus ditulis eksplisit, dan konversi float ke int memotong desimal.
  • Custom type seperti ProductStatus dan MembershipTier membuat domain lebih aman, sedangkan alias seperti UserID = int64 hanya memberi nama lain untuk tipe yang sama.
  • Uang disimpan sebagai int64 rupiah, bukan float64, agar total tetap presisi.

Pemetaan ke proyek online shop skincare

Model domain

Product, ProductStatus, Quantity, PriceRupiah, dan IsActive menjadi fondasi entitas katalog yang akan tumbuh menjadi cart, order, dan inventory.

Lapisan berikutnya

Tipe yang aman ini menjadi bahan untuk validasi stok, aturan diskon, handler API, query PostgreSQL, dan DTO JSON di modul lanjutan.

🌉Langkah berikutnya

Setelah data punya tipe yang jelas, modul berikutnya membahas control flow: if, switch, for, dan range, lalu gaya early return dan guard clause, agar kita bisa mulai menulis aturan bisnis seperti validasi stok dan aturan diskon.

Checkpoint sebelum lanjut

Pastikan kamu bisa menjelaskan beda var dan :=, menyebut zero value tiap tipe dasar, menulis konversi float64(x) dan int64(y) dengan benar, serta membuat satu custom type berikut method-nya.

Progress disimpan lokal di browser ini.