Go: Dasar-Dasar untuk Developer Java/Python di 2026

Pelajari Go dengan cepat menggunakan pengalaman Java atau Python. Goroutine, channel, interface, dan pola-pola penting untuk transisi yang lancar.

Panduan Go untuk developer Java dan Python

Go (atau Golang) telah memantapkan posisinya sebagai bahasa pilihan untuk microservice, tool CLI, dan sistem terdistribusi. Diciptakan oleh Google pada tahun 2009, Go menggabungkan kesederhanaan Python dengan performa setara C. Bagi developer yang beralih dari Java atau Python, transisi ke Go terasa sangat mulus begitu konsep-konsep intinya dipahami.

Mengapa Go di 2026?

Go menjadi fondasi Docker, Kubernetes, Terraform, dan banyak infrastruktur cloud lainnya. Kompilasi cepat, dukungan concurrency bawaan, dan deployment sebagai binary tunggal menjadikan Go ideal untuk pengembangan backend modern.

Instalasi dan konfigurasi Go

Menginstal Go mudah dan konsisten di semua platform. Tool go mengelola kompilasi, dependensi, dan pengujian.

bash
# install.sh
# Installation on macOS with Homebrew
brew install go

# Installation on Linux (Ubuntu/Debian)
sudo apt update && sudo apt install golang-go

# Verify installation
go version
# go version go1.22.0 linux/amd64

Struktur proyek Go mengikuti konvensi yang ketat namun sederhana. File go.mod mendefinisikan modul dan dependensinya.

bash
# project-setup.sh
# Create a new project
mkdir my-project && cd my-project
go mod init github.com/user/my-project

# Generated structure:
# my-project/
# ├── go.mod    # Module manifest
# └── main.go   # Entry point

# Essential commands
go build          # Compile the project
go run main.go    # Compile and execute
go test ./...     # Run all tests
go fmt ./...      # Format code automatically

Program Go pertama

Berikut program sederhana yang mengilustrasikan sintaks dasar Go. Perbandingan dengan Java dan Python membantu memvisualisasikan perbedaannya.

main.gogo
package main

import "fmt"

// Program entry point
func main() {
    // Declaration with type inference
    message := "Hello, Go!"
    fmt.Println(message)

    // Explicit declaration
    var count int = 42
    fmt.Printf("Count: %d\n", count)
}

Hal yang langsung terlihat: tanpa titik koma, tanpa tanda kurung di sekitar kondisi, dan inferensi tipe dengan :=. Go mengutamakan keringkasan tanpa mengorbankan keterbacaan.

Variabel dan tipe fundamental

Go memiliki tipe statis tetapi menawarkan inferensi tipe yang sangat baik. Tipe dasar mencakup sebagian besar kasus penggunaan.

types.gogo
package main

import "fmt"

func main() {
    // Short declaration (inside functions only)
    name := "Alice"        // string
    age := 30              // int
    height := 1.75         // float64
    active := true         // bool

    // Explicit declaration
    var score int = 100
    var rate float64 = 3.14

    // Multiple declaration
    var (
        firstName string = "Bob"
        lastName  string = "Smith"
        points    int    = 0
    )

    // Zero values (default values)
    var count int      // 0
    var text string    // "" (empty string)
    var flag bool      // false
    var ptr *int       // nil

    fmt.Println(name, age, height, active)
}
Zero value di Go

Berbeda dengan Java atau Python, Go secara otomatis menginisialisasi variabel ke "zero value"-nya: 0 untuk angka, "" untuk string, false untuk bool, nil untuk pointer dan slice.

Fungsi dan multiple return

Go memungkinkan pengembalian beberapa nilai sekaligus, fitur yang digunakan secara luas untuk penanganan error.

functions.gogo
package main

import (
    "errors"
    "fmt"
)

// Simple function with typed parameters
func add(a, b int) int {
    return a + b
}

// Multiple returns (idiomatic pattern for errors)
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

// Named returns
func getUser(id int) (name string, age int, err error) {
    if id <= 0 {
        err = errors.New("invalid ID")
        return
    }
    name = "Alice"
    age = 30
    return
}

// Variadic function
func sum(numbers ...int) int {
    total := 0
    for _, n := range numbers {
        total += n
    }
    return total
}

func main() {
    // Simple call
    result := add(5, 3)
    fmt.Println("5 + 3 =", result)

    // Explicit error handling
    quotient, err := divide(10, 3)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Printf("10 / 3 = %.2f\n", quotient)

    // Ignore a returned value with _
    name, _, _ := getUser(1)
    fmt.Println("User:", name)

    // Variadic call
    total := sum(1, 2, 3, 4, 5)
    fmt.Println("Sum:", total)
}

Struct dan method

Struct adalah blok pembangun untuk tipe kustom di Go. Method diikatkan ke tipe melalui receiver.

structs.gogo
package main

import "fmt"

// Struct definition
type User struct {
    ID       int
    Username string
    Email    string
    Active   bool
}

// Constructor (convention: NewTypeName)
func NewUser(id int, username, email string) *User {
    return &User{
        ID:       id,
        Username: username,
        Email:    email,
        Active:   true,
    }
}

// Method with value receiver (copy)
func (u User) FullInfo() string {
    status := "inactive"
    if u.Active {
        status = "active"
    }
    return fmt.Sprintf("%s <%s> (%s)", u.Username, u.Email, status)
}

// Method with pointer receiver (modification possible)
func (u *User) Deactivate() {
    u.Active = false
}

// Method with pointer receiver for modification
func (u *User) UpdateEmail(newEmail string) {
    u.Email = newEmail
}

func main() {
    // Create with constructor
    user := NewUser(1, "alice", "alice@example.com")
    fmt.Println(user.FullInfo())

    // Modify via method
    user.Deactivate()
    fmt.Println(user.FullInfo())

    // Direct creation
    user2 := User{
        ID:       2,
        Username: "bob",
        Email:    "bob@example.com",
    }
    fmt.Println(user2.FullInfo())
}
Value vs Pointer Receiver

Gunakan pointer receiver (*User) ketika method mengubah state atau ketika struct berukuran besar. Gunakan value receiver (User) untuk method read-only pada struct ringan.

Interface: polimorfisme implisit

Interface di Go diimplementasikan secara implisit. Sebuah tipe memenuhi interface jika mengimplementasikan semua method-nya, tanpa deklarasi eksplisit.

interfaces.gogo
package main

import (
    "fmt"
    "math"
)

// Interface definition
type Shape interface {
    Area() float64
    Perimeter() float64
}

// Rectangle implements Shape implicitly
type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

// Circle also implements Shape
type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * math.Pi * c.Radius
}

// Function accepting the interface
func PrintShapeInfo(s Shape) {
    fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}
    circle := Circle{Radius: 7}

    // Polymorphism via interface
    PrintShapeInfo(rect)
    PrintShapeInfo(circle)

    // Slice of interfaces
    shapes := []Shape{rect, circle}
    for _, shape := range shapes {
        PrintShapeInfo(shape)
    }
}

Pendekatan ini berbeda secara radikal dari Java di mana implements bersifat wajib. Di Go, kesesuaian bersifat struktural, bukan nominal.

Siap menguasai wawancara Go Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

Slice dan map: koleksi dinamis

Slice adalah tampilan dinamis atas array, dan map adalah hash table bawaan.

collections.gogo
package main

import "fmt"

func main() {
    // Slice: dynamic array
    numbers := []int{1, 2, 3, 4, 5}

    // Append elements
    numbers = append(numbers, 6, 7)

    // Slicing (similar to Python)
    subset := numbers[1:4]  // [2, 3, 4]
    fmt.Println("Subset:", subset)

    // Create slice with make
    scores := make([]int, 0, 10)  // len=0, cap=10
    scores = append(scores, 100, 95, 88)

    // Iteration with range
    for index, value := range numbers {
        fmt.Printf("numbers[%d] = %d\n", index, value)
    }

    // Map: hash table
    users := map[string]int{
        "alice": 30,
        "bob":   25,
    }

    // Add/Update
    users["charlie"] = 35

    // Check existence
    age, exists := users["alice"]
    if exists {
        fmt.Println("Alice's age:", age)
    }

    // Delete
    delete(users, "bob")

    // Map iteration
    for name, age := range users {
        fmt.Printf("%s is %d years old\n", name, age)
    }
}

Penanganan error idiomatik

Go tidak memiliki exception. Error adalah nilai yang dikembalikan secara eksplisit, memaksa penanganan yang ketat.

errors.gogo
package main

import (
    "errors"
    "fmt"
    "os"
)

// Sentinel error (for comparison)
var ErrNotFound = errors.New("resource not found")
var ErrInvalidInput = errors.New("invalid input")

// Custom error with context
type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
}

// Function returning different error types
func GetUser(id int) (string, error) {
    if id <= 0 {
        return "", &ValidationError{
            Field:   "id",
            Message: "must be positive",
        }
    }
    if id > 1000 {
        return "", ErrNotFound
    }
    return "Alice", nil
}

// Error wrapping (Go 1.13+)
func ReadConfig(path string) ([]byte, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("reading config %s: %w", path, err)
    }
    return data, nil
}

func main() {
    // Basic pattern
    user, err := GetUser(-1)
    if err != nil {
        fmt.Println("Error:", err)

        // Type assertion for custom error
        var valErr *ValidationError
        if errors.As(err, &valErr) {
            fmt.Printf("Field: %s\n", valErr.Field)
        }

        // Comparison with sentinel error
        if errors.Is(err, ErrNotFound) {
            fmt.Println("User not found")
        }
    } else {
        fmt.Println("User:", user)
    }
}
errors.Is dan errors.As

Sejak Go 1.13, errors.Is() digunakan untuk membandingkan dengan sentinel error dan errors.As() untuk mengekstrak tipe error tertentu dari rantai error yang dibungkus.

Goroutine: concurrency ringan

Goroutine adalah thread ringan yang dikelola oleh runtime Go. Memulai goroutine hanya membutuhkan beberapa KB memori.

goroutines.gogo
package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()  // Decrement counter when done

    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    // Launch 5 goroutines
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, &wg)  // 'go' prefix launches the goroutine
    }

    // Wait for all goroutines to complete
    wg.Wait()
    fmt.Println("All workers completed")
}

sync.WaitGroup memungkinkan menunggu penyelesaian beberapa goroutine sekaligus. Ini adalah pola dasar untuk paralelisme di Go.

Channel: komunikasi antar goroutine

Channel adalah saluran bertipe untuk komunikasi antar goroutine. Channel memungkinkan sinkronisasi yang aman dan pertukaran data.

channels.gogo
package main

import (
    "fmt"
    "time"
)

func producer(ch chan<- int) {
    for i := 1; i <= 5; i++ {
        fmt.Println("Producing:", i)
        ch <- i  // Send on channel
        time.Sleep(100 * time.Millisecond)
    }
    close(ch)  // Close channel when done
}

func consumer(ch <-chan int, done chan<- bool) {
    for value := range ch {  // Iterate until closed
        fmt.Println("Consuming:", value)
    }
    done <- true
}

func main() {
    ch := make(chan int)     // Unbuffered channel
    done := make(chan bool)

    go producer(ch)
    go consumer(ch, done)

    <-done  // Wait for consumer to finish
    fmt.Println("All done")
}

Buffered channel dan select

channels_advanced.gogo
package main

import (
    "fmt"
    "time"
)

func main() {
    // Buffered channel (capacity 3)
    buffered := make(chan int, 3)
    buffered <- 1
    buffered <- 2
    buffered <- 3
    // buffered <- 4  // Would block since buffer is full

    fmt.Println(<-buffered)  // 1

    // Select: channel multiplexing
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(100 * time.Millisecond)
        ch1 <- "from ch1"
    }()

    go func() {
        time.Sleep(200 * time.Millisecond)
        ch2 <- "from ch2"
    }()

    // Wait for first available message
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println("Received:", msg1)
        case msg2 := <-ch2:
            fmt.Println("Received:", msg2)
        case <-time.After(500 * time.Millisecond):
            fmt.Println("Timeout!")
        }
    }
}
Filosofi Go: CSP

Go mengikuti model CSP (Communicating Sequential Processes): "Jangan berkomunikasi dengan berbagi memori; bagikan memori dengan berkomunikasi." Channel mencegah race condition.

Testing di Go

Go menyertakan framework testing minimalis namun efektif. File test diakhiri dengan _test.go.

calculator.gogo
package calculator

func Add(a, b int) int {
    return a + b
}

func Divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}
calculator_test.gogo
package calculator

import (
    "testing"
)

// Basic test
func TestAdd(t *testing.T) {
    result := Add(2, 3)
    expected := 5

    if result != expected {
        t.Errorf("Add(2, 3) = %d; want %d", result, expected)
    }
}

// Table-driven tests (recommended pattern)
func TestAddTableDriven(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive numbers", 2, 3, 5},
        {"negative numbers", -2, -3, -5},
        {"zero", 0, 0, 0},
        {"mixed", -5, 10, 5},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := Add(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("Add(%d, %d) = %d; want %d",
                    tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

// Error test
func TestDivideByZero(t *testing.T) {
    _, err := Divide(10, 0)
    if err == nil {
        t.Error("Expected error for division by zero")
    }
}

// Benchmark
func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(100, 200)
    }
}

Test dijalankan dengan go test ./... dan benchmark dengan go test -bench=..

HTTP: server web minimalis

Go unggul dalam membuat server HTTP berperforma tinggi menggunakan library standarnya.

server.gogo
package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func main() {
    // Simple route
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello, Go!"))
    })

    // JSON route
    http.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
        users := []User{
            {ID: 1, Name: "Alice"},
            {ID: 2, Name: "Bob"},
        }

        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(users)
    })

    // Route with method
    http.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) {
        switch r.Method {
        case "GET":
            w.Write([]byte("Get user"))
        case "POST":
            w.Write([]byte("Create user"))
        default:
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        }
    })

    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Server ini mampu menangani ribuan koneksi bersamaan berkat goroutine. Setiap request secara otomatis diproses dalam goroutine-nya sendiri.

Kesimpulan

Go menawarkan pendekatan pragmatis untuk pengembangan backend: sintaks sederhana, kompilasi cepat, concurrency bawaan, dan tooling yang sangat baik. Bagi developer Java atau Python, transisi memerlukan penerimaan beberapa konvensi yang berbeda (penanganan error eksplisit, generics terbatas sebelum Go 1.18), tetapi manfaat dalam performa dan kemudahan pemeliharaan langsung terasa.

Checklist untuk memulai

  • ✅ Instal Go dari situs resmi atau package manager
  • ✅ Kuasai perintah go build, go run, go test, go fmt
  • ✅ Pahami perbedaan antara slice dan array
  • ✅ Adopsi pola if err != nil untuk penanganan error
  • ✅ Gunakan goroutine dan channel untuk concurrency
  • ✅ Tulis table-driven test dengan package testing

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Ekosistem Go sudah matang, dengan framework populer seperti Gin, Echo, dan Fiber untuk pengembangan web, serta tool seperti Cobra untuk CLI. Dengan fondasi yang kuat ini, mengeksplorasi topik lanjutan seperti generics (Go 1.18+), package context, dan pola-pola concurrency menjadi lebih mudah.

Tag

#go
#golang
#concurrency
#goroutines
#backend

Bagikan

Artikel terkait