Go hâlâ iyi değil
(blog.habets.se)- Go dilinin tasarımındaki çeşitli kararlar gereksiz biçimde alındı ya da mevcut deneyim görmezden gelinerek verildi
- Hata değişkenlerinin kapsamını yönetme sorunu, kodun okunabilirliğini ve hata ayıklamayı zorlaştırıyor
nil'in ikili doğası, bellek kullanımı, kod taşınabilirliği gibi birçok alanda sezgisel olmayan ve gerçek dünyaya uymayan tasarımlar görülüyordeferdeyiminin sınırlamaları ve standart kütüphanenin istisna işleme yaklaşımı, istisna güvenliğini sağlamayı zorlaştırıyor- Bellek yönetimi ve UTF-8 işleme yetersizlikleri gibi biriken sorunlar, Go kod tabanlarının kalitesini uzun vadede olumsuz etkiliyor
Go diline yönelik uzun vadeli eleştiri
- Daha önceki yazılarda (Why Go is not my favourite language, Go programs are not portable) belirttiğim gibi, 10 yılı aşkın süredir Go dilinin çeşitli sorunlarına dikkat çekiyorum
- Özellikle, zaten bilinen iyi örnekleri görmezden gelen gereksiz tasarım kararları zamanla daha da hayal kırıklığı yaratıyor
Hata değişkeni kapsamının sezgisel olmaması
- Go sözdizimi, hata değişkeninin (
err) kapsamını gereksiz yere genişleterek hata olasılığını artırıyor- Örnek kodda
errdeğişkeni tüm fonksiyon boyunca hayatta kalıyor ve yeniden kullanılıyor; bu da kodun okunabilirliğini ve bakımını kötüleştiriyor - Deneyimli geliştiriciler, bu kapsam sorunu nedeniyle hata ayıklarken yanlış yönlendirme ve zaman kaybı yaşayabiliyor
- Değişkenleri uygun biçimde daha yerel sınırlara çekmenin sözdizimsel olarak izin verilen bir yolu yok
- Örnek kodda
nil'in iki farklı biçimi
- Go'da
interfacetürü ile işaretçi türündenil'in farklı davranması kafa karıştırıcı bir durum yaratıyor- Aşağıdaki örnekte olduğu gibi,
s'ye (işaretçi) vei'ye (interface)nilatansa biles==ifarklı değerlendirilebiliyor; yani tutarsız davranış sergiliyor - Bu,
nullişlemede normalde kaçınılmak istenen bir problem ve tasarımda yeterince düşünülmediğinin izini taşıyor
- Aşağıdaki örnekte olduğu gibi,
Kod taşınabilirliğinin sınırları
- Koşullu derleme için yorum kullanmak, bakım ve taşınabilirlik açısından belirgin biçimde verimsiz
- Gerçekten taşınabilir yazılım geliştirmiş biriyseniz, bu yaklaşımın ne kadar uğraştırıcı ve hata üretmeye açık olduğunu bilirsiniz
- Tarihsel olarak birikmiş deneyim (kod taşınabilirliği, pratik örnekler) göz ardı ediliyor
- Ayrıntılar için Go programs are not portable yazısına bakılabilir
append sahipliğinin belirsizliği
appendfonksiyonu ile slice sahipliği arasındaki ilişki net olmadığı için kodun davranışını öngörmek zorlaşıyor- Örneklerde,
foofonksiyonunda bir slice'aappendyapıldığında bunun özgün veri üzerinde gerçekte nasıl bir etkisi olacağını önceden anlamak zor - Dilde bilinmesi gereken bu tür 'quirk'lerin çoğalması hatalara yol açıyor
- Örneklerde,
defer deyiminin yetersiz tasarımı
- RAII (Resource Acquisition Is Initialization) ilkesindeki gibi, kaynak serbest bırakmayı açık biçimde desteklemiyor
- Java ve Python'daki yapısal kaynak yönetimi sözdizimlerine kıyasla, Go'da hangi kaynağın
deferile serbest bırakılması gerektiği açık değil - Örnekte olduğu gibi dosya işlemlerinde,
double-closesorununu bile doğrudan ele almak gerekiyor; doğru serbest bırakma sırası ve yöntemi belirsiz kalıyor
- Java ve Python'daki yapısal kaynak yönetimi sözdizimlerine kıyasla, Go'da hangi kaynağın
Standart kütüphanede istisna işleme
- Go açık istisna (
exception) desteği sunmayan bir yapıya sahip olsa da,panicgibi istisnai durumlar yine de ortaya çıkıyor- Bazı durumlarda
panic, programı tamamen sonlandırmak yerine sinsice yayılabiliyor - Standart kütüphanede (
fmt.Print, HTTP sunucusu vb.) istisnaları yok sayan örüntüler bulunduğundan, gerçek istisna güvenliğini garanti etmek mümkün değil - Sonuç olarak istisna güvenli kod yazmak zorunlu, fakat istisnaları doğrudan kullanmak mümkün değil
- Bazı durumlarda
UTF-8 işleme ve string'ler
stringtürüne keyfi ikili veri konsa bile Go özel bir doğrulama yapmadan çalışıyor- Geçmişte UTF-8 kodlamasından önce oluşturulmuş dosya adları gibi örneklerde, verilerin sessizce atlanmasıyla karşılaşabilirsiniz
- Yedekleme gibi senaryolarda önemli veriler kaybolabilir; bu da gerçek çalışma koşullarını yansıtmayan aşırı basit bir yaklaşım
Bellek yönetiminin sınırları
- RAM kullanımını doğrudan denetlemek zor ve GC'nin (garbage collection) güvenilirliğinin de sınırları var
- Go'nun bellek kullanımı artarak uzun vadede maliyet ve performans sorunlarına dönüşebiliyor
- Birden çok instance ve container ortamında maliyet ve ölçeklenebilirlik sorunları pratikte gerçekten yaşanıyor
Sonuç: Daha iyi bir yol vardı
- Halihazırda etkililiği kanıtlanmış dil tasarımları varken, Go birçok noktada bunlara sırt çevirdi
- Java'nın ilk taslaklarındaki sorunlardan farklı olarak, Go piyasaya çıktığında daha iyi yaklaşımlar zaten mevcuttu
Kaynaklar
- Uber: Data race patterns in Go
- FasterThanLime: Lies we tell ourselves to keep using Golang
- FasterThanLime: I want off Mr Golang’s wild ride
1 yorum
Hacker News yorumu
Go’yu pre-1.0 döneminden beri neredeyse tüm tam zamanlı işlerimde kullandım. Ekip arkadaşlarının temelleri öğrenmesi için basit ve genel olarak istikrarlı çalışıyor. Go’nun en yeni sürümüne geçerken neredeyse hiç endişe etmiyorsunuz ve çoğu faydalı özellik zaten varsayılan olarak geliyor. Derleme hızının yüksek olması cazip. Eşzamanlılık biraz uğraştırıcı olabiliyor ama zaman ayırınca veri akışını ifade etmek için iyi hale geliyor. Tip sistemi çoğunlukla kullanışlı ama bazen lafı uzatıyor. Genel olarak güvenilir bir araç. Ama yazıda değinilen birçok eleştiriye de katılıyorum. Go’da eski kuşak geliştiricilerin ilkelere fazla takılıp pratik kolaylıkları kaçırdığı yerler olduğu açık. Tabii bu benim hissim ve tüm eksikler giderilseydi şu ankinden daha kötü de olabilirdi diye düşünüyorum. Son birkaç yılda küçük tuhaflıkları düzeltmeye daha açık bir hava olduğunu da söylemek isterim. Bir zamanlar generics ya da custom iterator’ların ekleneceğini hayal bile etmezdim. RAM ve taşınabilirlik hakkındaki eleştiriler bana biraz kişisel şikayet gibi geliyor. İyileşirse güzel olur ama GC’nin çoğu programda ciddi sorun çıkarması son derece nadir, debug etmek de zor değil. Ayrıca Go neredeyse tüm önemli platformları destekliyor. Yine de error ve nil işleme biçimi bana hâlâ rahatsız geliyor.
Result[Ok, Err],Optional[T]gibi sözdizimlerini sık sık özlüyorumBen aksine Go’nun ilkelere saplanmadığını, o anda göz önünde olan problemi hızlıca çözme kolaylığına saplandığını düşünüyorum. Sorunu kökten analiz edip doğru çözmek yerine, “Not Invented Here” tavrını bırakıp elde ne varsa onunla apar topar çözülmüş gibi. Go’nun dosya sistemi API’si bunun tipik örneği. Dosya açma fonksiyonu gerekiyorsa basitçe
func Open(name string) (*File, error)diye yapıp geçiyorlar. Peki dosya adı UTF-8 değilse? 5 yıl boyunca bu sorun çıkmıyorsa umursamıyorlarGo’nun tasarım ilkelerinin sık sık “derleyiciyi kolay yapalım ve derlemeyi hızlı tutalım” hedefine fazlasıyla odaklandığını hissediyorum. Geliştirici ergonomisinden çok derleyiciye ve derleme sürecine odaklı bir yapı
Derlenen bir dili 20 yıl sonra ilk kez yeni işimde Go ile gerçekten kullandım. Kişisel zevk meselesi olabilir ama dürüst olmak gerekirse kullanırken rahatsızlık bile hissettim. Varsayılan argüman yok, error handling hoşuma gitmiyor, production’da düzgün stack trace yok. Nesne yönelimli sözdizimi de her fonksiyona tuhaf bir referans eklemeyi gerektirdiği için çirkin duruyor. Pointer’lar da yük gibi. Sonuçta eski C/C++ tekniklerine geri dönmüşüm gibi hissettirdi. Üniversitede 1999 civarında yaptığım programlamanın havası aynen duruyor
Go, eşzamanlılık açısından, benim deneyimime göre çok çekirdekli CPU ortamında paralelliği doğal biçimde ele alabilen tek dil. CSP tarzı goroutine/channel modeli sayesinde paralel işleme mantığı sezgisel biçimde ifade edilebiliyor. Python’da GIL ve karmaşık async kütüphaneleri baş ağrıtıyor. C, C++, Java gibi dillerde ise dil dışı ek kütüphaneler gerektiğinden dil seviyesinde paralelliği akıl yürütmek kolay değil. Bu yüzden HTTP sunucuları ya da servisler için Go’nun biçilmiş kaftan olduğunu düşünüyorum. Deneyimime göre buna yaklaşan başka bir seçenek yok
Geliştirici açısından ergonomi, yani standardizasyon ve tutarlılık bakımından kusursuza çok yakındı. Birden fazla mikroservis kod tabanında da stil farklı mı diye endişelenmiyorsunuz, format tartışmasına da gerek kalmıyor. Ama Go kendi standart yolunu seçerken biraz fazla eski tarzda ısrar etmiş gibi. Günümüz geliştiricileri
map/filtergibi fonksiyonel yöntemleri daha çok bekliyor ama Go yalnızca indeks hatasına açık döngüler sunuyor. TypeScript seviyesinde akıllı bir tip sistemi de değil. Error handling de rahatsız edici. Bu özellikler eklenirse “yaratıcı ama kötü kullanım” biçimlerinin artacağı endişesini anlıyorum ama JS kuşağına Go’yu anlatmanın zor olduğunu da hissediyorum5 yıldan uzun süredir büyük bir Golang projesine odaklandım ve bellek kullanımını en aza indirmesi gereken bileşenler yazarken Go’nun gevşek kalan taraflarıyla sık sık karşılaşıyorum. GC yeterince hızlı temizlemiyor ya da heap parçalanması ciddi hale geliyor (Go sıkıştırmalı bir garbage collector kullanmadığı için). Bu yüzden allocation’dan tamamen kaçınmaya çalışıyoruz ama bu da hataya çok açık. Debug süreci de aşırı zor. Heap profiline baktığınızda yalnızca hayatta kalan nesneleri görüyorsunuz; biriken gerçek çöp ya da parçalanma görünmediği için tahmine kalıyorsunuz. Örneğin X fonksiyonu yalnızca 1KB heap allocation yapmış gibi görünüyor ama bir döngü içinde sürekli çağrılırsa onlarca MB çöp üretiyor. Bu yüzden önceden statik buffer ayırıp yeniden kullanıyoruz ama ownership sorunları karmaşıklaşıyor ve
appendgibi gedikler açılıyor. Hatta bazen standart kütüphaneyi bile baştan yazmanız gerekiyor. Bizim durumumuzun genel olmadığını biliyorum ama gerçekten dille kavga ediyormuşsunuz gibi hissettiriyorBu gibi durumlarda belleği heap dışına taşımak aslında daha az acı verici olabilir. Tabii GC dili olduğu için kolay değil ama C++/Rust tarzı kodu Go’da zorla kurmaya çalışmak yerine, o kısmı doğrudan o dillere taşımak daha iyi olur
Böyle bir durumda go dilini seçmek baştan dil seçimi hatasıydı diye düşünüyorum. C/C++/Rust/Zig daha uygun olurdu
Yeni "Green Tea" garbage collector yardımcı olabilir deniyor. Bu, bellek odaklı olmasa da bellekte komşu nesneleri daha iyi ele alan paralel bir mark algoritması. İlgili bilgiye buradan bakılabilir
arena deneyi sürüyordu ama şu an durmuş durumda. Yine de ilginç olabilir
Yardımcı olmayan bir şey söylemiş olacağım, kusura bakma ama mevcut tabloya bakınca dil seçiminin tamamen yanlış olduğunu düşünüyorum. Muhtemelen şirket içi resmi dil politikası yüzünden zorla Go kullanıyorsunuz. Büyük şirketlerde production için yalnızca yaygın kullanılan dillerin onaylandığı durumlar sık görülüyor
Go’daki
defer’ın neden yalnızca fonksiyon kapsamına çalışıp sözcüksel kapsama uygulanmadığını hâlâ anlayamıyorum. Bunu fark etme nedenim de bir döngü içinde dosya işlerken dosya listesi büyüyüncedefer’ın tutamaçları fonksiyon sonuna kadar kapatmaması ve bunun crash’e yol açmasıydı. Çevremdeki Go geliştiricileri döngü gövdesini anonim bir fonksiyonla sarmamı söyledi. Bunun dışında birkaç küçük nokta haricinde Go bana keyifli geliyor; verimli bir sözdizimi var ve gereksiz “gösteriş” kültürünü de engelliyor. Büyük bir C# projesini Go’ya yeniden yazdım; özellikler onda biri kadar olmasına rağmen kod daha azdı. GC allocation zorlaması yerine iyi performans veren varsayılanlara yönlendiriyor ve serialization gibi işlerde yerleşik kod üretimi kullanışlı oluyor. ORM gibi her şeyi dilin içine çekmeye çalışan C# sözdiziminin aksine, Go’da SQL’i doğrudan SQL olarak yazıyor, gRPC’yi de protobuf spesifikasyonuyla ele alıyorsunuzBazen sözcüksel kapsamlı
defergerekir, bazen de fonksiyon kapsamlı olan gerekir. Örneğin bir döngüde birden fazla dosya açıp fonksiyon sonuna kadar hepsini açık tutmak istiyorsanız fonksiyon kapsamı gerekir. Şu anda fonksiyon kapsamı var ama sözcüksel kapsam gerektiğindefuncile sarabiliyorsunuz. Eğer yalnızca sözcüksel kapsam olsaydı ve fonksiyon kapsamına ihtiyaç duysaydınız ne yapacağınız belirsiz olurduSarmalayıcı bir fonksiyon olmadan bir seviye girintiyi azaltması, davranışın call stack ya da stack unwinding ile ilişkili olması ve C’deki
goto failstilinden bakınca doğal görünmesi gibi avantajları var. Tabii döngü içindedeferkullanırken ayrıca fonksiyona sarmak biraz can sıkıcıHem blok seviyeli dilleri hem de fonksiyon seviyeli
deferkullandım; bazen koşul bloklarının içinde de fonksiyon seviyelideferkullanabilmeyi istediğim oluyorBunun arkasında özellikle derin bir neden yokmuş gibi geliyor; gerçekten önemli mi ondan da emin değilim
C#’ta da SQL ya da protobuf spesifikasyonlarıyla çalışabilirsiniz. Fark yalnızca başka seçeneklerin de olması
Go’nun birçok kusuru var ama sunucu tarafı dilleri arasında bu kadar dengeli başka bir dil yok gibi geliyor. Node ya da Python’dan daha hızlı ve tip sisteminin de daha iyi olduğunu düşünüyorum. Rust’a göre giriş eşiği daha düşük, standart kütüphanesi ve araçları da harika. Basit sözdizimi ve tek bir yolu dayatması da hoşuma gidiyor. Error handling sorunlu ama Node’daki gibi
catchiçine her şeyin düşmesinden yine de iyi. Tüm bu ölçütleri karşılayan daha iyi bir dil var mı gerçekten merak ediyorum. Kendimi Go fanatiği saymam; kariyerim boyunca backend’de çoğunlukla Node kullandım ama son dönemde Go’yu deneysel olarak deniyorumAslında bu avantajların hepsi Java ya da C# için de aynı şekilde söylenebilir
'Node'a bir programlama dili denmesi biraz rahatsız ediyor. Node bir JavaScript runtime’ı ve bugün Node üzerinde çalışan projelerin önemli kısmı TypeScript ile yazılıyor. Yani Node dediğinizde aslında hangi dilin kullanıldığı bile net değil. TypeScript’i baz alırsanız, Go’nun tip sisteminden daha üretken olduğunu bile söyleyebilirim. Rust’la karşılaştırırken de aynı iddia kurulabilir
Çoğu dilin kendine özgü can sıkıcı yanları var. Go’nun performansı, taşınabilirliği ve runtime/ekosistemi çok iyi. Buna karşılık nil pointer, zero value, destructor olmaması, macro olmaması gibi eksileri de var (Go’da macro olmayışını aşmak için code generation aşırı kullanılıyor). Daha iyi diller de var (ör. Rust) ama onlar da Go’dan epey daha karmaşık. Bunun nedeni Go’nun yaratıcılarının sadeliği her şeyin üstünde tutması
Python’un tip sistemindeki son gelişmeleri düşününce Go’nun çok ilerisinde olduğunu söyleyebilirim. Özellikle structural typing açısından Python daha etkileyici
Go’nun tip sisteminin epey yetersiz olduğunu düşünüyorum
Go ile yazılmış bir static site generator’ı genişletmiştim; kod çok net ve okunabilir olsa da dilin sınırlamaları yüzünden genişletilebilirliği düşüktü. Basit bir değişiklik bile kodun birçok yerine zor müdahale etmeyi gerektiriyordu. Farklı seviyelerde kapsülleme ve soyutlama kurmak zor, “basitlik” uğruna soyutlama feda edilmiş. Oysa soyutlama, genişletilebilir kod yazmanın en önemli yolu. Go, genişletilebilirlik yerine basitliği seçmiş gibi. Çoğu Go programı bana “genişletilemeyen basitlik” gibi geliyor. İnsanlar Go’nun zaten böyle bir dil olduğunu söylüyor ama benim deneyimim bunu ikna edici bulmuyor. En azından “geliştirici deneyimi” kötü değildi
Go hakkındaki tartışmalar bana hep tuhaf geliyor. Eleştirince çoğu zaman “o dil zaten böyle” denip kabullenmeniz bekleniyor. Basitliği güçlü yanı diye anlatılıyor ama bir
map’in anahtar listesini almak için döngüyü elle yazmak gerçekten daha mı basit, emin değilimGo’yu kısa süre kullanıp bu tür bir eleştiriyi ne kadar kolay yapabildiğini sormak isterim. Ben 2015’ten beri milyonlarca satırlık çok sayıda büyük Go kod tabanında çalıştım, farklı ekiplerde bulundum. Go’nun genişletilebilirliği C, C# ve Java’ya kıyasla özellikle daha kötü değil. Go, ifade gücü yerine açıklığı seçiyor. Bu yüzden daha az soyutlama katmanı var ve insanı daha somut, daha açık yazmaya itiyor. Ama bunun genişletilemez olduğu sonucuna katılmıyorum. Modüler ve genişletilebilir tasarım, dilden çok geliştiricinin öğrenip uyguladığı bir alan. Senin baktığın kod muhtemelen kötü tasarlanmıştı; bu Go dilinin sınırı değil
Go’yu birkaç yıl kullandım; küçük şeyler hızlı yapılabiliyor ama ölçek büyüdükçe çok sayıda küçük rahatsızlık birikiyor. Özellikle debug tam bir kâbus; kullanılmayan bir X varsa (debug sırasında bir kısmı yorum satırına aldığınızda sürekli olur) kod hiç derlenmiyor. Gereksiz biçimcilik, özel dosya adları ve ayrılmış alan adları da yorucu. Standart kütüphanenin içine gizlenmiş
panic’ler ve beklenmedik heap kopyaları da yavaş ve sinir bozucu. Go’daki “büyülü” tarafların çoğu, mevcut mekanizmaların (özel dosya adları, büyük/küçük harf vb.) zorla başka amaçlara çekilmesinden doğan yan etkiler. Gerçektenpublicgibi bir görünürlük gerekseydipubyazmak mümkün olabilirdi ama nedense tuhaf bir inat var. Bugünlerde yapay zeka o kadar iyi ki Rust’ta bir tip ya da borrow checker sorunu çıkınca doğrudan yapay zekaya sorup hızlıca çözebiliyorum; bu yüzden çok daha keyifli. Eskisi gibi belge ya da SO içinde vakit kaybetmiyorumSon dönemde Rust’a ciddi biçimde girmedim ama geçen aralıkta biraz denediğimde yapay zekanın Rust’ta aşırı iyi olduğunu görüp şaşırdım. Ayrıntılı sözdizimi ve açık tip bilgisi çok olduğu için bazı şeyleri insandan bile daha iyi çözüyor
Go’da debug ederken derleme hatası çıkmasından şikayet edince Go tarafında genelde “araçları düzgün kullan” diye azar işitiyorsunuz. İlkeler aşırı uçta uygulanınca rahatsız edici oluyor
Bu debug rahatsızlığını Go’nun kurucusuna da söylemiştim ama o bile sorunu anlayamadı. Bu bana fazla amatörce geldi ve hayal kırıklığı yarattı. Bu arada yapay zeka Go konusunda aslında pek iyi değil. Dil daha basit olmasına rağmen ChatGPT Java, C# ve Python’ı daha iyi destekliyor
Ben şahsen Go’yu sevmiyorum ve çok sayıda belirgin eksik görüyorum ama neden hâlâ popüler olduğu açık. Go nispeten hızlı ve goroutine’ler sayesinde çoklu iş parçacığına doğrudan girmeden kararlı, güvenilir ve yüksek eşzamanlı servisler yazmayı kolaylaştırıyor. Google Go’yu çıkardığında benzer derecede ana akım, statik ve derlenen dil neredeyse yoktu. Bugün bile benzer konumdaki tek rakip Java gibi görünüyor (artık virtual thread desteği de var). async/await destekli diller benzer vaatlerde bulunuyor ama pratikte çok fazla karmaşıklık getiriyorlar (asenkron görevlerde blocking’den kaçınmak, function coloring vb.). Erlang da başka bir kategori. Sonuçta eksikleri çok olsa da goroutine’ler ve Google projesi olmasının getirdiği isim değeriyle popüler kalıyor
JVM, Go ile arasındaki farkı yavaş yavaş kapatıyor. virtual threads, zgc, lilliput, Leyden, Valhalla gibi projelerle sürekli gelişiyor. Java 8’den 25’e gelen değişim muazzam. Bundan sonra daha da kullanışlı hale gelecek gibi
Go’nun açıklığı ve sadeliği, LLM destekli programlama için çok uygun. Eski Go 1.x kodları bile en yeni sürümlerde aynen çalışıyor
Google içinde de aslında virtual threads kullanan Java, Go’dan çok daha sık tercih ediliyor
Yeni projeler için en uygun “modern dilin” hangisi olduğunu merak ediyorum
Go’yu 1.0 çıkmadan önce de seviyordum ama “hâlâ olmamış” değerlendirmesine katılmıyorum. Elbette eksikleri ve şikayet edilecek yanları var ama kurucular projeden ayrıldığında merkezi vizyonu korumak zorlaşıyor ve dilin kötüye gitme riski artıyor diye düşünüyorum. Yalnızca “sunucu dili” olarak konumlandırılması da sonunda insanları Rust ya da Python gibi dillere iter diye görüyorum. Visual Basic’le de dalga geçilen dönemler vardı ama sonuçta ihtiyacı olanlar onu gayet iyi kullanıyordu
Go’nun eksiklerini konu alan eleştiri yazıları dikkatle incelendiğinde aslında büyük meseleler değil. Çoğu teknik olarak doğru ama önemsiz ayrıntılar. Buna karşılık gerçekten ciddi dil tasarımı sorunları zero value, constructor desteğinin olmaması, null işleme yetersizliği, varsayılan mutability, generics düşünülmeden tasarlanmış tip sistemi, keyfi hassasiyet desteklemeyen
int, ownership’i belirsizsliceyapısı gibi şeyler (ilgili konu 1, ilgili konu 2). Sum type olmaması, string interpolation desteğinin olmaması da eksiler arasındaGo hakkında kitap yazmış biri olarak taraflı olabilirim ama 10 yıldan uzun süredir Go kullanan biri olarak başta gerçekten taze bir his verdiğini söyleyebilirim. Java’dan daha az boilerplate var, öğrenmesi kolay ve performansı da yeterince iyi. En iyi diye tek bir dil yok; her kullanım için en iyi seçim farklıdır ama tipik backend işleri için pişman olunmayacak bir tercih