Göktay Logo

Go Standart Kütüphanesi: "Batteries Included" Ne Demek?

6 dk okuma

Node.js dünyasından gelen bir geliştiriciye Go’yu anlattığımda ilk şaşırdıkları şey node_modules klasörünün olmayışı olur. “Peki HTTP sunucusunu neyle yazıyorsun? JSON’ı neyle parse ediyorsun?” diye sorarlar. Cevap hep aynı: standart kütüphaneyle.

Go’nun en sevdiğim yanı dilin sadeliği değil; kutudan çıkan (batteries included) o devasa, üretim kalitesinde standart kütüphanesidir. Neden Go’yu tercih ettiğimi Neden Golang yazımda anlatmıştım; bu yazıda ise o tercihi günlük hayatta değerli kılan asıl kahramanı, yani stdlib’i tanıtacağım.

Neden Standart Kütüphane Bu Kadar Önemli?

Modern JavaScript ekosisteminde basit bir tarih formatlama işlemi için bile npm install yapıp üçüncü parti bir pakete bağımlı hale gelirsiniz. Bu paketin bir gün terk edilmesi (unmaintained), bir güvenlik açığı barındırması veya left-pad vakasındaki gibi bir sabah repodan silinmesi sizin sorununuzdur.

Go’da ise durum farklıdır:

Production Notu: “Bunun için bir kütüphane var mı?” diye sormadan önce her zaman “Bunu standart kütüphaneyle yapabilir miyim?” diye sorun. Cevap şaşırtıcı sıklıkla “evet”tir. Her eklenen bağımlılık, gelecekteki bir bakım borcudur.

Şimdi, yıllardır neredeyse her Go projesinde açtığım o paketleri tek tek gezelim.

fmt: Formatlama ve Hata Üretimi

En temel paket. Ekrana yazı basmaktan çok daha fazlasını yapar; özellikle fmt.Errorf ile hata sarmalama (error wrapping) modern Go’nun kalbindedir.

package main

import (
    "errors"
    "fmt"
)

var ErrNotFound = errors.New("kayıt bulunamadı")

func findUser(id int) error {
    // %w ile alttaki hatayı "sarmalıyoruz" (wrap)
    return fmt.Errorf("kullanıcı %d getirilemedi: %w", id, ErrNotFound)
}

func main() {
    err := findUser(42)
    // Sarmalanmış hatanın kökenini errors.Is ile kontrol ederiz
    if errors.Is(err, ErrNotFound) {
        fmt.Println("İşlem: 404 dönülecek")
    }
    fmt.Println(err) // kullanıcı 42 getirilemedi: kayıt bulunamadı
}

net/http: Bir Web Sunucusu İçin Başka Hiçbir Şeye İhtiyacınız Yok

Go’nun belki de en etkileyici paketi. Express, Fastify veya benzeri bir framework kurmadan, tam donanımlı bir HTTP sunucusunu ve istemcisini doğrudan burada bulursunuz. Üstelik Go 1.22 ile birlikte gelen yeni router sayesinde metot bazlı yönlendirme artık kutudan çıkıyor:

package main

import (
    "net/http"
)

func main() {
    mux := http.NewServeMux()

    // Go 1.22+: metot ve path parametreleri artık standart mux'ta
    mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
        id := r.PathValue("id")
        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte(`{"id": "` + id + `"}`))
    })

    http.ListenAndServe(":8080", mux)
}

Aynı paket, bir HTTP istemcisi olarak da çalışır. Dışarıdaki bir API’ye istek atmak için ek bir kütüphaneye gerek yoktur:

resp, err := http.Get("https://api.example.com/status")
if err != nil {
    return err
}
defer resp.Body.Close()

Production Notu: Gerçek sistemlerde http.Get gibi paket seviyesindeki varsayılan istemciyi doğrudan kullanmayın. Varsayılan istemcinin timeout süresi yoktur; yavaş bir sunucu tüm goroutine’lerinizi süresiz kilitleyebilir. Her zaman &http.Client{Timeout: 10 * time.Second} şeklinde kendi istemcinizi tanımlayın.

encoding/json: JSON Artık Bir Problem Değil

Backend geliştirmenin ekmek-su işi olan JSON dönüşümleri, struct tag’leri sayesinde bildirimsel (declarative) ve temizdir.

type User struct {
    ID        int64     `json:"id"`
    Email     string    `json:"email"`
    Password  string    `json:"-"`          // asla serialize etme
    CreatedAt time.Time `json:"created_at"`
}

data, _ := json.Marshal(User{ID: 1, Email: "[email protected]"})
// {"id":1,"email":"[email protected]","created_at":"0001-01-01T00:00:00Z"}

Password alanındaki json:"-" etiketine dikkat edin: hassas alanları yanıttan dışlamanın en temiz yolu budur.

Mühendislik Notu: Karmaşık bir JSON çıktısını inceleyip ondan doğrudan Go struct’ları (doğru tag’lerle) üretmek istediğinizde, bu işi elle yapmak yerine hazırladığım Advanced JSON Formatter aracını kullanabilirsiniz.

time: Go’nun Meşhur “2006-01-02” Muamması

Diğer dillerden gelenlerin ilk takıldığı yer Go’nun tarih formatlama mantığıdır. Go, YYYY-MM-DD gibi soyut harfler yerine, referans bir tarih kullanır: Mon Jan 2 15:04:05 MST 2006. Yani “2006-01-02” yazmak, “yıl-ay-gün” demektir.

now := time.Now()
fmt.Println(now.Format("2006-01-02 15:04:05")) // 2026-08-18 14:30:00

// Süre (Duration) aritmetiği okunabilirdir
deadline := now.Add(48 * time.Hour)
if time.Until(deadline) < 2*time.Hour {
    // son iki saat...
}

Bu referans tarih mantığı başta kafa karıştırıcıdır ve elle format yazarken hata yapmak çok kolaydır. Formatı canlı test etmek için Golang Time Formatter aracını yıllardır bu yüzden kullanıyorum.

context: Eşzamanlılığın Trafik Polisi

Yüksek trafikli sistemlerde bir isteğin iptal edilmesi, zaman aşımına uğraması veya isteğe özel değerlerin (örneğin trace ID) taşınması gerekir. context paketi bu işin standardıdır ve neredeyse tüm G/Ç (I/O) fonksiyonlarının ilk parametresidir.

func fetchData(ctx context.Context) error {
    // 3 saniyeden uzun sürerse otomatik iptal
    ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
    defer cancel()

    req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
    _, err := http.DefaultClient.Do(req)
    return err // süre dolarsa context.DeadlineExceeded döner
}

Production Notu: context.Context’i bir struct alanında saklamayın; her zaman fonksiyonun ilk argümanı olarak açıkça geçirin. Ayrıca context.Value mekanizmasını isteğe bağlı iş verisi taşımak için değil, yalnızca istek kapsamındaki trace ID gibi meta veriler için kullanın.

log/slog: Yapılandırılmış Loglama Artık Standart

Go 1.21 ile gelen log/slog, benim için son yılların en değerli eklemelerinden biri. Artık yapılandırılmış (JSON) log üretmek için üçüncü parti bir kütüphane (zap, logrus) kurmak zorunda değilsiniz.

logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

logger.Info("kullanıcı giriş yaptı",
    "user_id", 42,
    "ip", "10.0.0.1",
)
// {"time":"...","level":"INFO","msg":"kullanıcı giriş yaptı","user_id":42,"ip":"10.0.0.1"}

Bu JSON çıktısı, Grafana Loki veya ELK gibi log platformlarında doğrudan sorgulanabilir. Yüksek trafikli bir sistemde printf ile loglama yapmak ile yapılandırılmış loglama yapmak arasındaki fark, gece yarısı bir incident’ı 5 dakikada mı yoksa 2 saatte mi çözeceğinizi belirler.

io ve os: Veri Akışlarının Dili

Go’nun en zarif soyutlamaları io.Reader ve io.Writer arayüzleridir. Bir dosya, bir HTTP yanıtı, bir ağ bağlantısı veya bellekteki bir tampon — hepsi aynı arayüzü konuşur. Bu sayede 1 GB’lık bir dosyayı belleğe hiç almadan, akış (streaming) halinde işleyebilirsiniz.

// Bir dosyayı, belleği şişirmeden doğrudan başka bir yere kopyala
src, _ := os.Open("buyuk-dosya.csv")
defer src.Close()

dst, _ := os.Create("kopya.csv")
defer dst.Close()

io.Copy(dst, src) // 2 KB'lık tamponlarla akış halinde kopyalar

Bu Reader/Writer felsefesi, araç kutumdaki VCF ve JSON dönüştürücüleri gibi büyük dosyaları tarayıcıyı kilitlemeden işleyen sistemlerin de temelini oluşturur.

Ve Diğerleri: Cephaneliğin Geri Kalanı

Her gün olmasa da sık sık başvurduğum diğer önemli paketler:

Peki Ne Zaman Dışarı Çıkmalı?

Standart kütüphaneyi savunuyorum ama fanatik değilim. Bazı durumlarda dış bağımlılık mantıklıdır: bir ORM (GORM), gelişmiş bir router (chi, echo) veya bir gözlemlenebilirlik (observability) kütüphanesi. Buradaki kritik nokta bilinçli seçim yapmaktır.

Kararınızı verirken şu soruyu sorun: “Bu bağımlılığı eklediğimde kazandığım zaman, gelecekte onu güncelleme, güvenlik açıklarını takip etme ve olası kırılmalarla uğraşma maliyetine değer mi?” Çoğu zaman standart kütüphanenin biraz daha fazla kod yazdıran ama sıfır bakım borcu olan çözümü kazanır.

Sonuç

Go’yu güçlü kılan şey, dilin az sayıda anahtar kelimesi kadar, arkasında duran o olgun standart kütüphanedir. net/http ile sunucu yazabiliyor, encoding/json ile veri işleyebiliyor, context ile eşzamanlılığı yönetebiliyor ve slog ile production’ı izleyebiliyorsanız — üstelik hiçbir dış bağımlılık olmadan — elinizde son derece sağlam bir temel var demektir.

Bir sonraki projenizde bir kütüphane aramaya koşmadan önce, standart kütüphanenin dokümantasyonunda birkaç dakika geçirin. Muhtemelen aradığınız şey, çoktan kutunun içinde sizi bekliyordur.