2026'da Go Hata Yönetimi: Kalıplar, Sarmalama ve Teknik Mülakat Soruları

Go hata yönetimi kalıplarının kapsamlı rehberi: error arayüzü, sentinel hatalar, %w ile sarmalama, errors.Is, errors.As ve teknik mülakatlarda karşılaşılan sorular.

Go error handling patterns and best practices in 2026

Go'da hata yönetimi, bu dili diğer popüler alternatiflerden ayıran temel özelliklerden biridir. İstisna (exception) mekanizmaları ve try/catch blokları yerine Go, hataları birer değer olarak ele alır — fonksiyonlardan açıkça döndürülür ve her çağrı noktasında kontrol edilir. Bu bilinçli tasarım kararı, hata yönetimini ön plana çıkararak hata yollarını görünür ve test edilebilir kılar.

Hata Değerleri, İstisnalar Değil

Go'da istisna mekanizması yoktur. Başarısız olabilecek her fonksiyon, son dönüş değeri olarak bir hata döndürür. Bu kalıp, hataları görmezden gelmenin maliyetini kaynak kodu düzeyinde görünür kılar ve hata akışlarını kolayca test edilebilir hale getirir.

Error Arayüzü ve Neden Önemli Olduğu

Go'nun tüm hata sistemi, standart kütüphanede tanımlanan tek bir arayüze dayanır:

builtin.gogo
type error interface {
    Error() string
}

Error() string metodunu uygulayan herhangi bir tip bu arayüzü karşılar. Bu basitlik, bileşenlenebilirliği destekler: hatalar struct, sarmalayıcı veya başka herhangi bir şey olabilir — yeter ki bir string temsili üretsinler.

Temel bir özel hata tipi bu kalıbı gösterir:

apperror.gogo
type NotFoundError struct {
    Resource string
    ID       string
}

func (e *NotFoundError) Error() string {
    return fmt.Sprintf("%s with ID %s not found", e.Resource, e.ID)
}

Bu NotFoundError tipi yapılandırılmış veri taşır. Çağıranlar, bir string'i ayrıştırmak yerine kaynak adını veya kimliğini programatik olarak çıkarabilir — HTTP API'leri veya CLI araçları oluştururken hataları belirli yanıtlara eşleyen kritik bir avantajdır.

Bilinen Durumlar İçin Sentinel Hatalar

Sentinel hatalar, belirli ve iyi bilinen hata koşullarını temsil eden paket düzeyinde değişkenlerdir. Standart kütüphane bu kalıbı yaygın şekilde kullanır: io.EOF, sql.ErrNoRows, os.ErrNotExist.

errors.gogo
var (
    ErrNotFound     = errors.New("record not found")
    ErrUnauthorized = errors.New("unauthorized access")
    ErrConflict     = errors.New("resource conflict")
)

Sentinel hatalar, çağıranın yalnızca neyin başarısız olduğunu bilmesi gerektiğinde, ayrıntılı neden bilgisine ihtiyaç duymadığında en iyi şekilde çalışır. Ek bağlam taşımadan bir durumu işaret ederler. Go geleneğine göre Err ön eki ile adlandırılır ve Go Code Review Comments kılavuzuna uygun olarak mesajlar küçük harfle, noktalama işareti olmadan yazılır.

Sentinel Hatalar Ne Zaman Sorun Olur

Sentinel hataların dışa aktarılması bir genel API sözleşmesi oluşturur. Bağımlı paketler bunları errors.Is ile eşleştirecektir, bu nedenle bir sentinel hatanın yeniden adlandırılması veya kaldırılması geriye dönük uyumluluğu bozan bir değişikliktir. Bunları tedbirli kullanmak gerekir — yalnızca çağıranların gerçekten dallanma yapması gereken koşullar için.

fmt.Errorf ve %w Fiili ile Hata Sarmalama

Go 1.13'te tanıtılan hata sarmalama, orijinali bir zincirde korurken hataya bağlam ekler. fmt.Errorf içindeki %w fiili bu zinciri oluşturur:

repository.gogo
func (r *UserRepo) FindByID(ctx context.Context, id string) (*User, error) {
    user, err := r.db.QueryContext(ctx, "SELECT * FROM users WHERE id = $1", id)
    if err != nil {
        return nil, fmt.Errorf("UserRepo.FindByID(%s): %w", id, err)
    }
    return user, nil
}

Sarmalanmış hata tam zinciri korur. Üç katman yukarıdaki bir çağıran, orijinal sentinel'i hala eşleştirebilir veya orijinal tipi çıkarabilir. Her katman, neyin olduğunu gizlemeden hatanın nerede oluştuğu hakkında bağlam ekler.

Kritik bir ayrım: %w sarmalama yapar (zinciri korur), %v ise hatayı bir string olarak biçimlendirir ve zinciri kırar. %v, orijinal hatanın soyutlama sınırından sızmaması gerektiğinde bilinçli olarak kullanılmalıdır — örneğin bir repository, servis katmanının asla incelememesi gereken bir veritabanı sürücü hatasını sardığında.

errors.Is ve errors.As ile Hata İnceleme

errors paketi, doğrudan karşılaştırma ve tip dönüştürmenin yerini alan, sarmalanmış hata zincirlerini incelemek için iki fonksiyon sunar.

errors.Is zincirde belirli bir hata değerini arayarak ilerler:

handler.gogo
func handleGetUser(w http.ResponseWriter, r *http.Request) {
    user, err := userService.GetByID(r.Context(), chi.URLParam(r, "id"))
    if errors.Is(err, ErrNotFound) {
        // Matches even if ErrNotFound was wrapped multiple times
        http.Error(w, "User not found", http.StatusNotFound)
        return
    }
    if err != nil {
        http.Error(w, "Internal error", http.StatusInternalServerError)
        return
    }
    json.NewEncoder(w).Encode(user)
}

errors.As zincirden belirli bir hata tipini çıkarır:

middleware.gogo
func errorMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if r := recover(); r != nil {
                http.Error(w, "Internal error", http.StatusInternalServerError)
            }
        }()
        next.ServeHTTP(w, r)
    })
}

func mapErrorToHTTP(err error) int {
    var notFound *NotFoundError
    if errors.As(err, &notFound) {
        // notFound.Resource and notFound.ID are available
        return http.StatusNotFound
    }
    var validationErr *ValidationError
    if errors.As(err, &validationErr) {
        return http.StatusBadRequest
    }
    return http.StatusInternalServerError
}

Tip dönüştürmelere göre temel avantaj: her iki fonksiyon da sarmalanmış zincirin tamamını dolaşır. fmt.Errorf ve %w ile üç kez sarmalanmış bir NotFoundError hala eşleşir.

Katmanlı Uygulamalarda Yapılandırılmış Hata Yönetimi

Üretim Go uygulamaları tipik olarak hataları üç katmanda organize eder: alan (domain) hataları iş düzeyinde koşulları tanımlar, servis hataları operasyonel bağlam ekler, handler veya taşıma katmanı hataları ise bunları dış yanıtlara eşler.

domain/errors.gogo
type DomainError struct {
    Code    string // machine-readable: "USER_NOT_FOUND", "INVALID_INPUT"
    Message string // human-readable description
    Err     error  // original cause
}

func (e *DomainError) Error() string {
    if e.Err != nil {
        return fmt.Sprintf("%s: %s: %v", e.Code, e.Message, e.Err)
    }
    return fmt.Sprintf("%s: %s", e.Code, e.Message)
}

func (e *DomainError) Unwrap() error {
    return e.Err
}

Unwrap metodu, errors.Is ve errors.As fonksiyonlarının zincir boyunca çalışmasını sağlayan yapıdır. Başka bir hatayı saran herhangi bir özel hata tipi bu metodu uygulamalıdır.

Servis katmanı, alan hatalarını operasyonel bağlamla sarar:

service/user.gogo
func (s *UserService) Deactivate(ctx context.Context, userID string) error {
    user, err := s.repo.FindByID(ctx, userID)
    if err != nil {
        return fmt.Errorf("deactivating user %s: %w", userID, err)
    }
    if user.Status == StatusInactive {
        return &DomainError{
            Code:    "ALREADY_INACTIVE",
            Message: fmt.Sprintf("user %s is already inactive", userID),
        }
    }
    return s.repo.UpdateStatus(ctx, userID, StatusInactive)
}

Go mülakatlarında başarılı olmaya hazır mısın?

İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.

Go Hata Yönetimi Üzerine Yaygın Mülakat Soruları

Go pozisyonları için yapılan teknik mülakatlarda hata yönetimi bilgisi sıklıkla test edilir. Aşağıdaki sorular eleme turlarında düzenli olarak karşılaşılır.

Go neden istisnalar yerine hata değerleri kullanır?

İstisnalar görünmez kontrol akışı oluşturur. İstisna fırlatan bir fonksiyon, kontrolü bilinmeyen bir catch noktasına aktarır — potansiyel olarak birçok yığın çerçevesi uzağa. Go hata değerleri, her hata yolunu fonksiyon imzasında açıkça belirtir. Çağıran, hatayı nasıl ele alacağına anında karar verir — yeniden deneme, sarmalama, loglama veya yayma. Bu, "sürpriz fırlatma" sorununu ortadan kaldırır ve hata akışlarını araç desteği olmadan okunabilir kılar.

fmt.Errorf'ta %w ile %v arasındaki fark nedir?

%w hatayı sararak errors.Is ve errors.As için zinciri korur. %v hatayı bir string olarak biçimlendirir ve orijinale geri bağlantısı olmayan yeni bir hata üretir. Çağıranlar nedeni inceleyebilmeli ise %w; neden soyutlama sınırından sızmaması gereken bir uygulama detayı ise %v kullanılmalıdır.

Bir fonksiyon ne zaman error döndürmeli, ne zaman panic çağırmalıdır?

Panic, gerçekten kurtarılamaz durumlar için ayrılmıştır: dizin sınırı aşımı, nil gösterici başvurusu veya bir bug'a işaret eden ihlal edilmiş değişmezler gibi programlama hataları. Kurtarılabilir hatalar — ağ zaman aşımları, eksik kayıtlar, geçersiz girdi — error döndürür. Yararlı bir kural: koşul normal çalışma sırasında oluşabiliyorsa error döndürülür. Programın bir bug'ı olduğu anlamına geliyorsa panic çağrılır.

Unwrap metodu hata zinciri incelemesini nasıl etkiler?

Özel bir hata tipi Unwrap() error uyguladığında, errors.Is ve errors.As fonksiyonları zinciri dolaşmak için bu metodu takip eder. Unwrap olmadan zincir o hatada durur. Go 1.20'den itibaren hatalar, birden fazla sarmalanmış hata döndüren Unwrap() []error da uygulayabilir — bu, doğrulama hatalarını toplama gibi durumlar için ağaç yapılı hata zincirleri sağlar.

Kaçınılması Gereken Hata Yönetimi Anti-Kalıpları

Go kod tabanlarında ve kod inceleme oturumlarında birkaç anti-kalıp sıklıkla görülür:

Hataları sessizce görmezden gelmek. Boş tanımlayıcı bunu sözdizimsel olarak kolay ama anlamsal olarak tehlikeli kılar:

go
// anti-pattern: silent error swallowing
result, _ := riskyOperation()

Her görmezden gelinen hata bilinçli ve belgelenmiş bir karar olmalıdır — asla bir kısayol değil.

Hem loglama hem döndürme. Bu kalıp yinelenen log kayıtları üretir ve hatayı kendisi de ele alan çağıranın kafasını karıştırır:

go
// anti-pattern: double handling
if err != nil {
    log.Printf("operation failed: %v", err) // logged here
    return err                               // AND returned to caller who may log again
}

Çözüm: hatayı ya ele almak (loglamak, varsayılan değer döndürmek, yeniden denemek) ya da yaymaktır. İkisi birden değil.

Hata sınıflandırması için string eşleştirme. err.Error() çıktısını strings.Contains ile kontrol etmek kırılgandır. Hata mesajları API sözleşmesi değildir — kütüphane sürümleri arasında değişebilir. Sentinel değerler için errors.Is, tipler için errors.As kullanılmalıdır.

Goroutine sınırlarını geçen hataları senkronizasyon olmadan sarmalamak. Birden fazla goroutine eşzamanlı olarak hata ürettiğinde, golang.org/x/sync paketinden errgroup.Group veya benzer bir senkronizasyon mekanizması kullanılmalıdır. Mutex olmadan paylaşılan bir slice'a doğrudan ekleme yapmak veri yarışları oluşturur.

Eşzamanlı İşlemler İçin errgroup ile Hata Yönetimi

golang.org/x/sync'ten errgroup paketi, eşzamanlı goroutine'lerden hata toplamak için temiz bir kalıp sunar:

batch.gogo
func (s *OrderService) ProcessBatch(ctx context.Context, orderIDs []string) error {
    g, ctx := errgroup.WithContext(ctx)
    g.SetLimit(10) // limit concurrent goroutines

    for _, id := range orderIDs {
        g.Go(func() error {
            if err := s.processOrder(ctx, id); err != nil {
                return fmt.Errorf("processing order %s: %w", id, err)
            }
            return nil
        })
    }

    // Returns the first non-nil error and cancels remaining work
    return g.Wait()
}

errgroup.WithContext, herhangi bir goroutine hata döndürdüğünde türetilmiş context'i iptal eder ve diğerlerine durma sinyali gönderir. Bu, boşa harcanan işi önler ve çağırana tek bir sarmalanmış hata sunar.

Go 1.24 ve Hata Yönetimi

Go 1.24 (Şubat 2025) errors paketinde değişiklik yapmadı. Hata yönetimi API'si Go 1.13'ten bu yana kararlıdır; Go 1.20'de Unwrap() []error ile çoklu hata açma desteği eklenmiştir. Geliştirilmiş hata yönetimi sözdizimi için mevcut teklifler Go issue tracker'da tartışılmaya devam etmektedir.

Sonuç

  • Hataları değer olarak ele almak gerekir: gizli istisna mekanizmalarına güvenmek yerine bunları açıkça döndürüp her çağrı noktasında ele almak esastır
  • Sentinel hatalar (var ErrX = errors.New(...)) yalnızca çağıranların dallanma yapması gereken koşullar için kullanılmalı ve genel API olarak ele alınmalıdır
  • Hatalar fmt.Errorf ve %w ile sarmalanarak zincir korunurken bağlam eklenir; soyutlama sınırında zinciri bilinçli olarak kırmak için %v tercih edilir
  • == yerine errors.Is, tip dönüştürme yerine errors.As tercih edilmelidir — her ikisi de sarmalanmış zincirin tamamını dolaşır
  • Özel hata tiplerinde Unwrap() error uygulanmalıdır, böylece zincir incelemesi doğru çalışır
  • Hata ya ele alınmalı ya da yayılmalıdır — asla her ikisi birden. Bir hatayı loglamak ve döndürmek yinelenen gürültü üretir
  • Eşzamanlı hata toplama için manuel goroutine senkronizasyonu yerine errgroup kullanılmalıdır
  • Hata mesajları Go geleneklerine uygun olarak küçük harfle ve noktalama işareti olmadan yazılmalıdır

Pratik yapmaya başla!

Mülakat simülatörleri ve teknik testlerle bilgini test et.

Etiketler

#go
#error-handling
#best-practices
#interview

Paylaş

İlgili makaleler