1 puan yazan GN⁺ 2025-06-04 | 1 yorum | WhatsApp'ta paylaş
  • Go dilinde hata işlemenin ayrıntılı ve uzun yazılması uzun süredir kullanıcı şikayetlerinin üst sıralarında yer alıyor
  • Çeşitli sözdizimsel iyileştirme önerileri (check/handle, try, ? operatörü vb.) tartışıldı ve denendi, ancak toplulukta yeterli uzlaşı oluşmadığı için hepsi reddedildi
  • Dil değişikliklerinin kod, araçlar, belgeler vb. üzerinde geniş kapsamlı etkisi ve Go’nun kendine özgü sadelik ilkesini koruma yaklaşımı başlıca değerlendirme noktaları oldu
  • Mevcut yaklaşımın açıklığı, hata ayıklama kolaylığı ve bazı kullanıcıların bunu tercih etmesi nedeniyle, sözdizimsel bir değişiklik getirmek için güçlü bir gerekçe bulunmuyor
  • Öngörülebilir gelecekte hata işleme sözdiziminde bir değişiklik planlanmıyor ve ilgili önerilerin tamamı ek araştırma olmadan kapatılacak

Go’da hata işleme ayrıntılılığı sorununun gündeme gelişi

  • Go ile ilgili eski şikayetlerden biri, hata işleme sözdiziminin gereğinden fazla uzun ve ayrıntılı olması
  • Özellikle if err != nil gibi kalıplar kod içinde tekrar tekrar görülüyor
  • Birden fazla API çağrısı gerektiren programlarda bu durum daha belirgin hale geliyor ve kimi zaman gerçek mantıktan çok hata işleme kodu yazılıyor
  • Yıllık kullanıcı anketlerinde bu şikayet sürekli üst sıralarda yer alıyor

Toplulukla istişare ve ilk öneriler

  • Go ekibi topluluk geri bildirimine önem verdiği için hata işleme iyileştirmelerini uzun süredir araştırıyor
  • 2018’deki Go 2 proje tartışmalarında Russ Cox, hata işleme sorununun özünü resmî olarak ortaya koydu
    • Marcel van Lohuizen tarafından önerilen check ve handle mekanizması gündeme geldi
    • Benzer dillerle karşılaştırmalar ve çeşitli alternatiflerin değerlendirilmesi de buna dahildi
  • Bu yaklaşım kodu gerçekten daha kısa hale getiriyordu, ancak artan karmaşıklık nedeniyle benimsenmedi

try önerisi ve sonrası

  • 2019’da çok daha sade bir try yerleşik işlevi önerildi
    • check işlevini kodla sağlıyor, handle kısmını dışarıda bırakıyordu
    • Bu öneri kontrol akışını gizlediği gerekçesiyle eleştirildi ve topluluktan gelen tepkiyle geri çekildi
  • Bu deneyim, yeterli geri bildirim alınmadan hazırlanmış olgun önerilerin riskli olduğunu gösterdi
    • Büyük ölçekli değişiklik önerilerinde tasarımın erken aşamalarında daha geniş görüş toplamanın önemli olduğu anlaşıldı

Ek girişimler ve çeşitli öneriler

  • Toplulukta çok sayıda varyasyon ve alternatif hata işleme yöntemi önerilmeye devam etti
    • Ian Lance Taylor’ın umbrella issue’su ile durum özetlendi; Go Wiki ve bloglarda örnekler toplanmayı sürdürdü
  • 2024’te Rust’tan alınan ? operatörünün uygulanması önerildi
    • Küçük ölçekli kullanılabilirlik testlerinde sezgisel bulunduğuna dair geri bildirim alındı, ancak yine farklı görüşler nedeniyle uzlaşı sağlanamadı

Tartışmanın tıkanması ve sonuç

  • Resmî ve gayriresmî olarak 3’ten fazla öneri, topluluk tarafında ise yüzlerce öneri olmasına rağmen yeterli ortak anlayış/uzlaşı oluşmadığı için hepsi reddedildi
  • Go içindeki mimar grubu bile yön konusunda fikir birliğine sahip değil
  • Koşullar değişene ya da güçlü bir ortak görüş oluşana kadar hata işleme sözdizimini değiştirme girişimlerinin durdurulmasına karar verildi

Mevcut yaklaşımın korunmasını savunan başlıca gerekçeler

  • Dilin ilk tasarımında sözdizimsel şeker eklenmiş olsaydı bugün bu kadar tartışma olmayabilirdi, ancak şu anda 15 yıldır kullanılan bir yaklaşım etrafında oluşmuş bir ekosistem var
  • Yeni bir sözdizimi getirmek kaçınılmaz olarak eski ve yeni kullanıcılar arasında kod stili farkı ve tutarlılığın bozulması riskini taşıyor
  • Bu durum Go’nun tasarım felsefesiyle (aynı işi birden çok yolla yapmamak) ve sadelik/tutarlılık önceliğiyle de uyumlu
    • Kısa değişken tanımında (:=) yeniden bildirim izni de hata işleme nedeniyle ortaya çıkan ikincil bir değişimdi
  • Açık hata işleme sözdizimi (if üzerinden) kod okumada, hata ayıklamada ve breakpoint yerleştirmede sezgisel avantajlar sağlıyor
  • Dil değişiklikleri, gerçek değişiklik kapsamı (kod, belgeler, araçlar vb.) ve maliyet açısından da büyük yük oluşturuyor

Alternatif iyileştirmeler ve gelecekteki yön

  • Standart kütüphanenin yeteneklerini artırmak (örneğin cmp.Or eklenmesi) bazı tekrar eden kodları azaltabilir
  • IDE ve geliştirme araçlarındaki kod katlama, otomatik tamamlama, LLM kullanımı vb. sayesinde bu ayrıntılılık pratikte bir ölçüde aşılabiliyor
  • Başlıca Go kullanıcı gruplarında (örneğin Google Cloud Next etkinliğine katılanlar) dil değişikliği gerekliliğine olumsuz bakan görüşler daha baskın
    • Go kullanımı arttıkça ayrıntılılık sorunu pratikte daha az hissediliyor

Sözdizimsel iyileştirme gereğini destekleyen gerekçeler

  • Kullanıcı geri bildirimlerine bakıldığında hâlâ hata işleme sözdiziminin iyileştirilmesi talebi var
  • Yalnızca karakter sayısını azaltmakla kalmayıp açıklığı artıran bir hata işleme sözdizimi, kod kalitesi ve güvenliğine katkı sağlayabilir
  • Basit hata kontrolünden ziyade gerçekten işlevsel hata işleme biçimleri üzerine daha ayrıntılı araştırma yapılması gerekiyor

Nihai sonuç ve bundan sonraki politika

  • Şu ana kadar anlamlı bir uzlaşı veya somut bir değişiklik çıkmadığı kabul edilerek, öngörülebilir gelecekte hata işleme için sözdizimsel dil değişikliklerine ilişkin tüm tartışma ve önerilerin durdurulduğu ilan edildi
  • Önceki tartışma ve araştırma süreci, dolaylı olarak Go ekosistemi ve süreçlerinin gelişimine katkı sağladı
  • İleride daha net bir problem tanımı ve uzlaşı oluşursa tartışma yeniden başlayabilir
  • Şimdilik odak noktası, yeni denemelerden çok Go’nun sağlamlığını ve sadeliğini korumak olacak

1 yorum

 
GN⁺ 2025-06-04
Hacker News yorumu
  • Go ekibinin kolayca başka alternatifler seçebileceğini öne sürmek istiyorsanız, lütfen Go2ErrorHandlingFeedback wiki ve GitHub issue araması bağlantılarına mutlaka bakın. Önerilen fikirlerin neredeyse tamamı zaten ciddi biçimde tartışıldı ve Go ekibinin şeffaf yaklaşımını takdir eden biri olarak her gün Go kullanmaktan büyük keyif alıyorum

    • Taslak tasarım belgesi C++, Rust ve Swift’ten bahsediyor ama benim aradığım Haskell/Scala/OCaml gibi fonksiyonel dillerdeki do-notation/for-comprehensions/monadic-let tarzı yaklaşımları bulmak zor. Go ekibi dil tasarımının ustaları gibi görünüyor ama iş hata işlemeye gelince, parametreli çok biçimliliği olmayan Java benzeri statik tip sınırlarına çarpıp çözüm üretemiyor gibiler. Bence bu, dilin temel tasarımından kaynaklanan bir sorun

    • Akıllı ve deneyimli insanların yazdığı belgeler olmasına rağmen, Haskell’in Maybe/Either monad’ı ve bind operatörü (do-notation) gibi çözümlerin hiçbir yerde anılmaması çok şaşırtıcı. Oysa bunlar ne zor ne de ukalaca şeyler; hataları güvenli biçimde iletmek için son derece zarif ve kendini kanıtlamış yöntemler. Go topluluğunun bunu neden benimsemediğini bilmiyorum. Bu sayfanın varlığına minnettarım ama bu kadar bilinen bir çözümün atlanmasını anlamak zor

    • Neredeyse her dil daha iyi çeşitli yaklaşımlar sunuyor; bu yüzden neden sadece Go’da bu sorun bu kadar öne çıkıyor diye merak ediyorum. Mesele sadece uzlaşma eksikliği mi, yoksa Go’ya özgü başka dillerdeki çözümleri uygunsuz kılan bir özellik mi var, bunu merak ediyorum

    • Go eleştirilerinde sık görülen bir şey, nispeten uzman olmayan kişilerin Go geliştiricilerinin dillerden kendilerinden daha az anladığını varsayma eğilimi. Oysa Go geliştiricileri çoğu durumda çok daha deneyimli ve çok daha bilgili. Uzman olmayanlar, daha çok özelliği olan dillerin otomatik olarak daha iyi olduğunu düşünüyor ama asıl önemli olanın genel dengeyi iyi kurmak olduğunu gözden kaçırıyor

  • Go’nun yeni dil özellikleri ekleme konusunda temkinli, muhafazakâr yaklaşımının kullanıcılara fayda sağladığını düşünüyorum. Swift tarafında özellik değişimi o kadar fazla ki öğrenmesi zorlaşıyor ve en yeni Mac’lerde bile bazen tek bir basit proje dahi derlenmiyor. Anahtar kelimeler sürekli artıp değiştiği için Swift’in sürekliliği zayıf; buna karşılık Go’nun gücü istikrarı

  • Bir keresinde bir Go fonksiyonunda, içteki fonksiyondan hata gelmesi beklenen istisnai bir durum vardı; içteki fonksiyon hata vermezse bu kez dış fonksiyonun hata döndürmesi gerekiyordu. Bu alışılmadık yapıda if err == nil ile dallanmak zorundaydım ama alışkanlıkla if err != nil yazdım ve sürekli kullandığım kalıba fazla alışmış olduğum için hatayı bulmam uzun sürdü. Sık kullanılan if err != nil ile nadir kullanılan if err == nil arasındaki sözdizimsel fark dil düzeyinde desteklenseydi, bu tür hatalar azalabilirdi diye düşünüyorum

    • if err == nil yazdığım her yere kalıbı vurgulamak için // inverted yorumu ekliyorum. Dilde bunun otomatik ele alınması güzel olurdu ama şimdilik bu şekilde ayrımı daha görünür kılabiliyorum
    • Aslında bu, sözdizimi değişikliğine karşı bir görüş. Sık görülen if err == nil { return ... } kalıbı kodda daha da tuhaf görünebilir. Mevcut Go hata işleme tarzının açık ve okunması kolay olduğu için birçok kişinin bunu tercih ettiğini düşünüyorum
    • Aynı karışıklık if fruit != "Apple" gibi kalıplarda da ortaya çıkabildiğinden, bunun özünde sadece hata işlemeye değil, genel durum dallanmasına ilişkin bir mesele olarak görülmesi gerektiği söyleniyor. Sonuçta hata da diğer durum değerleri gibi ele alınıyor
    • IDE veya yazı tipi ayarlarında if err != nil ifadesini özel bir sembol gibi render edip arka planda doğal biçimde kaybolacak şekilde daha az görünür kılmak, buna karşılık farklı yazılmış if err == nil ifadesini öne çıkarmak suretiyle editör düzeyinde hataların önüne geçmek mümkün olabilir
    • Editörde if err … { gibi bir kalıbı kısaltılmış göstererek okunabilirliği artırmak da bir öneri olabilir
  • Go’nun açık hata işleme yaklaşımını seviyorum. Bir fonksiyonu ya her zaman başarılı olan (minimal error) ya da başarısız olabilen bir yapı olarak basitçe anlıyorum. Başarısız olabilecek bir fonksiyonun, bir sonraki adıma geçmeden önce mutlaka ele alınması gerekir. Birçok dilde exception mekanizması yüzünden hata oluştuğunda catch edilene kadar çağrı yığınında fırlatılıyor; bu da çoğu zaman yalnızca hatanın nerede oluştuğunu söylüyor ama gerçek anlamda pek ipucu vermiyor. Go’da şu seçenekler açıkça mevcut: 1) hatayı yok saymak 2) hata oluşunca hemen dönmek 3) hatayı wrap ederek faydalı bilgi eklemek 4) belirli bir hatayı yorumlayıp ona göre dallanmak (ör. 404’e çevirmek). Go2’de Result<Value, Failure> tipi ya da daha somut ve numaralanabilir hata tipleri görmek isterdim. Go 1 ile geriye dönük uyumluluk için bunun Go 2’de gelmesi daha uygun olur diye düşünüyorum

    • Deneyimime göre hata işleme politikası mutlaka çağıran tarafından belirlenmeli; alt katmanlarda ele alınması iyi bir yaklaşım değil. Sonunda bu iş kolayca hatayı wrap edip üst katmana aktarmaktan ibaret tekrarlı bir işe dönüşüyor
    • “Go’nun hata işlemesi” aslında JavaScript ve Python dışında, fonksiyonel dillerden Rust ve Java’ya kadar çoğu dilin zaten sunduğu bir şey. Sonuçta yalnızca generics varsa Go tarzı hata işlemesini hemen her dilde uygulayabilirsiniz. Kıyas JS veya Python’da kalıyorsa bu olsa olsa yaygın bir desen olur
    • “Fonksiyon başarısız olursa mutlaka ele alınmalı” düşüncesinin tam da Go’nun zayıf noktası olduğu söyleniyor. Çünkü Go’da aslında hatayı tamamen görmezden gelmek mümkün; bu yüzden gerçekten dayanıklı yazılım yapmak istiyorsanız Go’nun yaklaşımı bir zayıflık olabilir
    • Go2’nin sonunda “asla yayımlanmayacak bir laboratuvar” olarak kalacağına dair buruk bir görüş de var
  • Başta Go’nun hata işleme tarzını pek sevmiyordum ama errors-are-values blog yazısını okuyup panic(err) kullanımını yerinde uygulamaya başladıktan sonra aksine bundan çok memnun kalmaya başladım. Üst kodun doğrudan ele almaması gereken anormal durumlarda panic kullanmak, kod içindeki dağınık hata dallarını büyük ölçüde azaltmamı sağladı. Bu hata yönetimi yaklaşımı iş hayatında gerçekten çok yardımcı oldu

    • Buna karşı argüman olarak, bu mantığın Go’nun zayıf hata işlemesini savunamadığı ve iyileştirilse bile mevcut avantajların kaybolmayacağı söyleniyor
    • PHP’de de seviye bazlı hata işleme ya da @ operatörüyle çağrı noktasında hata bastırma mümkün; bash’ta da -e gibi hata yönetim teknikleri var deniyor
    • C#’ta try/catch/finally akışını ilk gördüğümde çok yenilikçi gelmişti ama artık Go’daki gibi daha basit mantığı tercih ediyorum. Kod satırı sayısının yüksek olması bile, akışın daha açık olması açısından avantaj olabilir
    • Rust’ın sum type tabanlı hata yaklaşımının da “errors are values” paradigmasına girdiği belirtiliyor
  • Gerçek hataları işleyince ayrıntı fazlalığının hemen arka plana düştüğü söyleniyor ama elle stack trace üretmenin gerçekten “işleme” sayılıp sayılmayacağı sorgulanıyor. Go’nun tanımına göre exception da bir işleme biçimi değil mi? diye esprili bir itiraz var

    • Onlarca satırlık stack trace’in gerçekten net bilgi sağlayıp sağlamadığı da sorgulanıyor. Bana göre tek satırlık bir wrap error çok daha verimli ve logları düzenli tutmak açısından daha faydalı. 10 yılı aşkın Go kullanımımda, runtime fonksiyonlarını da içeren bu kadar ayrıntılı stack bilgisini hiç ihtiyaç duyduğumu hatırlamıyorum
  • Bu yazının Go hata işlemedeki sorunu sadece “sözdizimi çok ayrıntılı” diye ele almasını sevmiyorum. Bence asıl sorunlar şunlar: 1) hataların sessizce atlanması ya da yanlışlıkla yok sayılmasının kolay olması 2) fonksiyon sonuçlarını değer gibi kolayca aktarma veya saklamanın mümkün olmaması 3) errors.Is gibi iç içe hataların tip sistemiyle uyumsuz görünmesi 4) hataya göre switch yapmanın zor olması 5) standart kütüphanede sentinel value kullanımının yaygın olması 6) generics ile uyumsuzluk yüzünden paket ihtiyacı doğması

    • Go’da profesyonel programcıların %90’ı, her hata dönüş dalı için test case yazarak coverage tamamlıyor; exception tabanlı dillerdeyse buna gerek yok deniyor
    • Bu yazının ana sorunu “It’s too verbose” olarak sunduğu iddiasının doğru olmadığı, sözdizimi değişse bile özde büyük bir iyileşme getirmeyeceği düşünülüyor
    • Go’nun değişim hızının çok yavaş olması (generics’in bile uzun sürmesi) bazılarına göre aslında bir avantaj
    • Bir Googler olarak Go ekibinin kararından bir kez daha hayal kırıklığına uğradım
  • Elixir’de (ve Erlang’da) fonksiyonlar genellikle {:ok, result} veya {:error, description} tuple’ı döndürür. Elixir’nin with sözdizimi sayesinde hata işleme blok sonunda toplanabildiği için okunabilirlik çok daha iyi olur. Go’ya da with benzeri bir yapı gelirse, yalnızca hata nil olduğunda yürütmenin sürmesi ve en altta bir hata işleyici bloğu bulunması sayesinde kod daha okunur hale gelebilir

    • Go, topluluk uzlaşması sorunları nedeniyle sum type, hata işleme, paket yönetimi gibi en temel ve değerli özellikleri bile çok yavaş ekliyor. Generics için 13 yıl, hata işleme için 16 yıl, paket yönetimi için 9 yıl geçmesi bunun göstergesi. Temkinlilik önemli ama mükemmeli beklemek kararların sürekli ertelenmesine yol açıyor gibi
    • Go’nun çoklu dönüş deseni bazı bakış açılarına göre anormal de görülebilir. Birden fazla tip döndüren fonksiyonlarla fiilen yapılabilen tek şeyin değişken ataması olması eleştiriliyor
  • Rust tarzının neden doğrudan izlenmediğini anlamıyorum. Özellikle artık generics varken benzer bir şeyin hızla uygulanabilmesi gerekir. Rust’ın ? operatörü kullanışlı olsa da bunun hata görmezden gelmeyi teşvik ettiğini söyleyen eleştiriye katılmıyorum. Gerçekte Go’da hata dönüş değerlerini yok saymak çoğu zaman derleyici hatası bile üretmiyor. Rust tarzında olduğu gibi Result tipi dönüşünü zorunlu kılmak ancak hataları önleyebilir. Eğer mesele kolaylık adına tartışmalı olmaktaysa, o zaman panic de yasaklanmalı değil mi diyen sert bir görüş var

    • Go’nun Result getirememesinin nedeni olarak, sum type olmaması ve her tip için zero value gerektiren tuhaf tasarımı gösteriliyor
    • ? operatörü gibi kolaylık sağlayan bir özelliğin “artık wrap edilmiş hata kullanmayacağız” anlamına geldiği iddiasına karşı, tam tersine böyle bir özelliğin wrapping’i teşvik edecek şekilde de tasarlanabileceği söyleniyor
    • Kolaylık odaklı özelliklerin (Rust tarzı) bir dezavantajı olarak, dallanma akışını tek satırda gizlemesi, debug breakpoint koymayı zorlaştırması ve enrich/handling’den çok bubbling’e aşırı odaklanması gösteriliyor; Go’nun bu nedenle bazı sözdizimlerini (ör. üçlü operatör) reddettiği açıklanıyor
    • Rust tarzını birebir kıyaslasak bile, Go’da buna tam olarak neyin denk düştüğünün açık olmadığına dair teknik bir soru da var
    • Generics geldikten sonra Rust tarzında tam olarak neyin uygulandığını gösteren bir kod örneği isteniyor
  • Rust’taki gibi kutucuk işaretlercesine özellik benimsemeyi tartışmak yerine, bir dilin bütünsel tutarlılık içinde tasarlanması gerektiğini düşünüyorum. Bir özellik listesindeki maddeleri tek tek tamamlamak, o özelliğin gerçekten dilin doğasına uyduğu anlamına gelmez

    • Rust’ın committee tarzı tasarımla ilerlemesi nedeniyle sözdiziminin okunmasının zorlaştığı ve tutarlılığın azaldığı yönünde bir algı var
    • “Mükemmel çözüm” diye bir şey olmadığı söyleniyor
    • Anket sonuçlarında Go’nun tek ölümcül sorununun hata işleme olduğunu söyleyenlerin oranı yalnızca %13; mevcut durumu tercih eden kullanıcılar da az değil. Anket sonuçlarına bakın