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:
- Kararlılık: Standart kütüphane, Go 1 uyumluluk garantisi altındadır. Bugün yazdığınız
net/httpkodu on yıl sonra da derlenir. - Kalite: Bu paketler, dünyanın en iyi mühendisleri tarafından yazılıp milyonlarca
productionsisteminde test edilmiştir. - Bağımlılık hijyeni: Ne kadar az dış bağımlılık, o kadar az güvenlik yüzeyi ve o kadar küçük
binary. Bu felsefeyi Clean Architecture yazımdaki “framework bir detaydır” prensibiyle birlikte düşünün.
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.Getgibi paket seviyesindeki varsayılan istemciyi doğrudan kullanmayın. Varsayılan istemcinintimeoutsüresi yoktur; yavaş bir sunucu tümgoroutine’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ıcacontext.Valuemekanizmasını isteğe bağlı iş verisi taşımak için değil, yalnızca istek kapsamındakitrace IDgibi 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:
errors:errors.Isveerrors.Asile hata zincirlerini güvenle kontrol etme.sync:WaitGroupilegoroutine’leri bekleme,Mutexile ortak belleği koruma,sync.Onceile tek seferlik başlatma.database/sql: Veritabanı sürücülerinden bağımsız, standart bir SQL arayüzü. (SQL şemalarınızı Go modellerine çevirmek için SQL to GORM Model Converter işinizi hızlandırır.)strconv/strings: String dönüşümleri ve manipülasyonu için hızlı, tahsissiz (allocation-free) yardımcılar.regexp: Google’ın RE2 motoruyla güvenli düzenli ifadeler. Bir Go regex’ini derlemeden önce Go RE2 Regex Tester ile test edebilirsiniz.testing: Harici bir test framework’üne gerek kalmadan, tablo bazlı (table-driven) testler vebenchmark’lar.
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.