Mimari ve Dağıtık Sistemler
Projeleri Go'ya Taşımanın Gizli Zorlukları
The Hidden Challenges of Moving Projects to Go
Go'ya geçişte asıl zorluk sözdizimi değil; hata yönetimi, eşzamanlılık modeli, kütüphane kültürü ve ekip alışkanlıklarının sessizce çatışması. Ekim 2026'da güncel sürüm Go 1.27; eski operasyon tavsiyelerinin çoğu artık geçersiz.
Go'ya geçişin maliyeti sözdiziminde değil zihniyettedir; açık hata dönüşü, gerçek paralellik ve az sihirli kütüphane kültürü öğrenilmeden çevrilen kod, Go kılığında eski dil olur.
Go'nun resmi inceleme rehberi ve büyük port örnekleri bu farkı vurguluyor; tartışma çoğu servisin geçişe gerçekten ihtiyaç duyup duymadığı üzerine.
Zihinsel model
Go'ya geçmek yeni bir klavyeye değil, ters yönden akan bir trafiğe geçmektir: eski refleksler en tanıdık kavşakta kazaya yol açar.
Neden
- Go'da hata bir değerdir; bağlamla sarılmazsa stack trace'in sağladığı bilgi loglardan tamamen kaybolur.
- Goroutine'ler event loop'tan farklı olarak gerçekten paralel çalışır; korumasız paylaşılan durum çalışma zamanında servisi çökertir.
- AI çevirisi eski dilin sınıf hiyerarşisini, DI alışkanlığını ve panic'li hata yönetimini Go sözdizimine birebir taşır.
Kanıt
Microsoft TypeScript derleyicisini yeniden tasarım yerine yapıyı birebir koruyan bir port ile Go'ya taşıdı ve davranış uyumunu hızdan önce tuttu.
Microsoft TypeScript Blog, 2026Eski Go sürümleri konteynerde CPU limitini değil makinenin çekirdek sayısını kullanıyor, bu da throttling ve kuyruk gecikmesi üretiyordu.
Go Blog: Container-aware GOMAXPROCS, 2025Eski encoding/json v1 API'si alan adlarını büyük-küçük harf duyarsız eşler ve bilinmeyen alanları sessizce yok sayar.
Go Blog: A new experimental Go API for JSON, 2025Karşı görüş
Çoğu web servisinde darboğaz I/O'dadır; profil çıkarmadan yapılan geçiş, zihniyet öğrenilse bile beklenen kazancı getirmez. Modern .NET ya da Node'u optimize etmek çoğu zaman daha ucuz yoldur.
Kavramlar
Pratikte
- Geçişten önce gecikmenin ne kadarının uygulama kodunda, ne kadarının I/O beklemesinde geçtiğini profille ölç.
- Testleri -race ile çalıştır ve incelemede her goroutine'in nasıl biteceğini sor.
- AI çevirisini sözleşme testleri, gölge trafik ve deneyimli bir Go incelemesiyle doğrula.
C#/.NET ya da Node.js ile yazılmış projeleri Go'ya taşıyan ekiplerin asıl sorunları benchmark tablolarında görünmez. Go'nun küçük dili ve tek binary dağıtımı geçişi kolay gösterir; asıl maliyet exception yerine açık hata dönüşü, async/await yerine goroutine ve context, ORM ve DI container yerine elle kurulan yapı gibi zihniyet farklarında çıkar. Konu şimdi önemli çünkü AI asistanlarıyla kod çevirisi çok ucuzladı: bir servisi satır satır Go'ya çevirmek birkaç günlük iş gibi görünüyor, ama çevrilen kod çoğu zaman 'Go sözdizimiyle yazılmış C#' oluyor. Ekim 2026 itibarıyla güncel sürüm Ağustos 2026'da çıkan Go 1.27; Go 1.25'teki konteyner farkındalıklı GOMAXPROCS, Go 1.26'da varsayılan olan Green Tea çöp toplayıcısı ve Go 1.27'de varsayılan hâle gelen encoding/json/v2 altyapısı, operasyon ve sözleşme tarafındaki eski tuzakların bir kısmını değiştirdi. Microsoft'un TypeScript derleyicisini Go'ya taşıyarak Temmuz 2026'da TypeScript 7.0'ı yayımlaması da bu tür geçişlerin en görünür örneği oldu.
Çıkarımlar
- Geçişten önce profil çıkar: gecikmenin çoğu I/O beklemesindeyse, dil değişimi beklediğin kazancı getirmez.
- Go'da hata bir değerdir; hataları %w ile sarmazsan stack trace'in yerini hiçbir şey almaz.
- Node'dan gelenler için bir numaralı tuzak gerçek paralellik: testleri -race ile çalıştır, her goroutine'in nasıl biteceğini sor.
- Eski dilin alışkanlıklarını Go'da yeniden kurma: DI container, derin arayüz hiyerarşisi ve float64 ile para anti-pattern'dir.
- Go 1.25 konteyner farkındalıklı GOMAXPROCS, Go 1.26 varsayılan Green Tea GC, Go 1.27 json/v2 altyapısı ve goroutineleak profili getirdi; eski operasyon tavsiyelerini sürümüne göre güncelle.
- TypeScript 7.0 örneği: büyük bir kod tabanını Go'ya taşırken önce davranış uyumunu koru, deyimsel yeniden tasarımı sonraya bırak.
- Strangler fig ve gölge trafik ile parça parça taşı; AI çevirisini mutlaka bir Go deyimleri incelemesinden geçir.
Alt başlıklar
Neden Go'ya geçilir, neden geçiş pişmanlıkla biter
Ekiplerin Go'ya geçiş gerekçeleri genelde üç başlıkta toplanır: düşük bellek ayak izi, tek statik binary ile basit dağıtım ve öngörülebilir gecikme. Bunlar gerçek kazanımlar. Örneğin Node.js'te bir servis için node_modules ve runtime ile birlikte yüzlerce megabaytlık bir imaj taşınırken, Go'da çoğu servis onlarca megabaytlık tek bir binary ile dağıtılabilir.
Pişmanlık ise genelde yanlış problemi çözmekten gelir. Darboğaz veritabanı sorgularındaysa, dili değiştirmek gecikmeyi düşürmez; sadece aynı yavaş sorguyu başka bir dilde bekletir. Anti-pattern: profil çıkarmadan 'Go daha hızlı' diye karar vermek.
Somut bir eşik önerisi: geçişten önce mevcut serviste p95 gecikmenin ne kadarının uygulama kodunda, ne kadarının I/O beklemesinde geçtiğini ölç. Sürenin büyük çoğunluğu I/O beklemesindeyse, dil değişimi beklediğin kazancı büyük ihtimalle getirmez. Önce sorgu, indeks ve önbellek tarafına bak.
Ekim 2026 itibarıyla durum
Go'nun güncel sürümü 19 Ağustos 2026'da çıkan Go 1.27; destek politikası gereği yalnızca 1.26 ve 1.27 destekleniyor, yani 1.25 ve öncesinde kalan servisler artık güvenlik yaması almıyor. Son 12 ayda geçiş yapan ekipleri doğrudan etkileyen üç değişiklik oldu. Birincisi Go 1.26'da Green Tea çöp toplayıcısının varsayılan olması ve new fonksiyonunun ifade kabul etmesi. İkincisi Go 1.27'de generic metotların gelmesi, goroutineleak profilinin varsayılan olması ve encoding/json'ın v2 altyapısına taşınması. Üçüncüsü araç tarafı: go fix artık onlarca modernizer barındıran bir yeniden yazım aracı.
Büyük ölçekli bir geçiş örneği de bu dönemde tamamlandı. Microsoft, TypeScript derleyicisini Go'ya taşıdı ve Temmuz 2026'da TypeScript 7.0'ı yayımladı; ekibin ölçümlerine göre tam derlemeler 8 ile 12 kat hızlandı. Dikkat çekici nokta, bunun yeniden tasarım değil, eski kodun yapısını birebir koruyan bir port olması: davranış uyumu hızdan önce geldi. Programatik API'nin 7.0'da henüz olmaması da geçişin ekosistem tarafındaki maliyetini gösteriyor.
Exception'dan açık hata dönüşüne: en büyük kültür şoku
C# ve JavaScript'te hata, yığında yukarı doğru fırlar ve bir yerde try/catch ile yakalanır. Go'da hata bir değerdir: fonksiyon (sonuç, error) döndürür ve çağıran her seferinde kontrol eder. İlk haftalarda ekipler 'if err != nil' satırlarının kalabalığından şikâyet eder.
Asıl tuzak gürültü değil, bağlam kaybıdır. C#'ta stack trace bedavaya gelir; Go'da hatayı fmt.Errorf ile %w kullanarak sarmazsan, loglarda sadece 'connection refused' görürsün ve hangi çağrıdan geldiğini bilemezsin. Hataları sarmak, errors.Is ve errors.As ile türüne göre ayırmak gerekir. Go 1.26 ile gelen errors.AsType, generic yapısıyla errors.As'ı daha tip güvenli ve okunur hâle getiriyor.
Bir diğer anti-pattern: panic'i exception gibi kullanmak. panic, programın devam edemeyeceği durumlar içindir. HTTP handler'da panic ile hata yönetmek, bir isteğin tüm servisi çökertme riskini doğurur.
async/await ve event loop'tan goroutine ve context'e
Node.js geliştiricisi tek iş parçacıklı event loop'a alışkındır; aynı anda iki fonksiyonun aynı değişkene yazması pratikte sorun olmaz. Go'da goroutine'ler gerçekten paralel çalışır ve korumasız bir map'e iki goroutine'den yazmak programı çalışma zamanında çökertebilir. Bu, Node'dan gelen ekiplerin en sık düştüğü tuzaktır.
İkinci kavram context.Context. C#'taki CancellationToken'a benzer, ama Go'da kültür olarak her I/O fonksiyonunun ilk parametresi olarak taşınır. Context'i yaymayı unutmak, kullanıcı isteği iptal ettiği hâlde arka planda veritabanı sorgusunun dakikalarca çalışmaya devam etmesi demektir.
Üçüncü tuzak goroutine sızıntısı: kimsenin okumadığı bir kanala yazmaya çalışan goroutine sonsuza kadar bekler ve bellek yavaşça büyür. Go 1.26'da deneysel olarak gelen goroutineleak profili Go 1.27 ile varsayılan hâle geldi; /debug/pprof/goroutineleak üzerinden asla uyanamayacak goroutine'leri gösteriyor. Pratik kural: testleri -race bayrağıyla çalıştır ve başlattığın her goroutine'in nasıl biteceğini kod incelemesinde sor.
Kütüphane kültürü: ORM, DI container ve LINQ yokluğu
.NET ekipleri Entity Framework, ASP.NET Core'un yerleşik dependency injection'ı ve LINQ ile çalışmaya alışkındır. Go ekosistemi bilinçli olarak daha az sihir tercih eder. Bağımlılıklar genelde main fonksiyonunda elle bağlanır; veritabanı erişimi için GORM gibi ORM'ler olsa da birçok ekip SQL yazıp ondan tip güvenli kod üreten sqlc ya da doğrudan pgx kullanmayı seçer.
Zorluk, eski alışkanlığı yeni dilde yeniden kurma isteğidir. Go'da reflection tabanlı bir DI container yazmak ya da her şeyi arayüzlerin arkasına saklamak, kodu hem yavaşlatır hem de okunmaz kılar. Go'daki yaygın tavsiye şudur: arayüzü kullanan tarafta, ihtiyaç duyulduğunda tanımla.
LINQ'in yerine geçen tek bir şey yok. Go 1.18 ile gelen generics, 1.23 ile gelen iterator'lar ve Go 1.27'de eklenen generic metotlar slices ve maps paketleriyle birçok boşluğu dolduruyor, ama zincirleme sorgu sözdizimi yok. Çoğu durumda düz bir for döngüsü en okunur çözüm olur.
Tip sistemi tuzakları: nil arayüz, sıfır değer, JSON ve para
Go'da her tipin bir sıfır değeri vardır: int için 0, string için boş metin, pointer için nil. C#'ta null olabilen bir alanın Go karşılığını seçmek önemli bir karardır. JSON'da 'yaş belirtilmedi' ile 'yaş sıfır' ayrımını yapmak için pointer kullanmak gerekir; Go 1.26 ile new fonksiyonunun ifade kabul etmesi bu kalıbı kısalttı.
Ünlü bir tuzak: içinde nil pointer taşıyan bir arayüz değeri nil'e eşit değildir. Bir fonksiyon tipli bir nil hatayı error arayüzü olarak döndürürse, çağıran 'err != nil' kontrolünde hata varmış gibi davranır.
encoding/json'ın v1 API'si de farklı davranır: alan adlarını büyük-küçük harf duyarsız eşler ve bilinmeyen alanları sessizce yok sayar. Node'dan gelen ekipler bunu bilmeden yanlış sözleşmeyle çalışabilir. Go 1.25'te deneysel olarak gelen encoding/json/v2, Go 1.27 ile standart kütüphanenin parçası oldu: geçersiz UTF-8'i ve tekrarlanan alan adlarını varsayılan olarak reddediyor, v1 API'si de artık v2 altyapısı üzerinde çalışıyor. Bu yüzden Go 1.27'ye geçerken hata mesajlarına bağlı testler kırılabilir.
Para için Go'da yerleşik decimal tipi yok. C#'taki decimal'ın karşılığı olarak tamsayı kuruş ya da shopspring/decimal gibi bir kütüphane seçmek gerekir; float64 kullanmak anti-pattern'dir.
Operasyon farkları: GOMAXPROCS, bellek limiti ve çöp toplayıcı
Kubernetes'te 2 CPU limitli bir pod, 64 çekirdekli bir makinede çalışırken eski Go sürümleri GOMAXPROCS'u 64'e ayarlıyordu. Sonuç, CPU kotasının hızla tükenmesi ve throttling yüzünden kuyruk gecikmesinin artmasıydı. Birçok ekip bunu go.uber.org/automaxprocs kütüphanesiyle çözüyordu. Go 1.25 ile Linux'ta varsayılan GOMAXPROCS cgroup CPU limitine göre ayarlanıyor ve limit değişirse periyodik olarak güncelleniyor.
Bellek tarafında GOMEMLIMIT ayarı, Go 1.19'dan beri çalışma zamanına bir yumuşak bellek tavanı verir. Bunu konteyner limitinin biraz altına ayarlamamak, OOM ile öldürülen pod'lara yol açabilir.
Go 1.26'da Green Tea çöp toplayıcısı varsayılan oldu; Go ekibi GC'yi yoğun kullanan gerçek programlarda yüzde 10 ile 40 arası GC yükü azalması bekliyor. Bu GC Go 1.25'te yalnızca deneysel bir bayrakla açılabiliyordu. Bu, .NET'in nesil tabanlı GC'sinden farklı bir model. Geçiş yapan ekip için kural: her ortamda pprof ile CPU ve heap profili alınabilir olsun.
Göç stratejisi: strangler fig, sözleşme testleri ve AI ile çeviri
Büyük bir monoliti bir seferde yeniden yazmak en riskli yoldur. Strangler fig deseni, yani eski sistemin önüne bir yönlendirme katmanı koyup uç noktaları tek tek yeni servise taşımak, geri dönüşü mümkün kılar. Örneğin önce okuma ağırlıklı, iş kuralı az bir uç noktayı Go'ya taşıyıp trafiğin yüzde 5'ini oraya yönlendirebilirsin.
İki sistemin aynı davranışı gösterdiğinden emin olmak için sözleşme testleri ve gölge trafik kullanılır: aynı istek iki servise gönderilir, yanıtlar karşılaştırılır. JSON alan sırası, tarih formatı ya da null ile boş dizi farkı gibi küçük farklar burada ortaya çıkar.
AI ile kod çevirisi bu süreçte işe yarar ama tuzağı var: model C# sınıf hiyerarşisini birebir Go struct'larına ve gereksiz arayüzlere çevirir. Çeviriden sonra bir Go uzmanı incelemesi ve go vet, staticcheck gibi araçlar şart. Go 1.26 ile yeniden yazılan ve Go 1.27'de yeni modernizer'lar eklenen go fix de eski kalıpları güncel deyimlere çekmeye yardım eder; Go 1.27'nin go vet'i ayrıca pointer tipli bir hatanın %w ile sarılmasını işaretliyor, çünkü bu errors.Is kontrolünü bozuyor.
Son gelişmeler
Ekim 2026 itibarıyla
Önce kendi cevabını düşün, sonra aç.
Go'ya geçmek yerine .NET'i ya da Node'u optimize etmek daha mantıklı değil mi?
Çoğu zaman evet. Modern .NET performans tarafında çok güçlü ve Node'da da darboğaz genelde I/O'da. Go'ya geçiş, ekibin operasyon basitliği, bellek maliyeti ya da işe alım gibi somut bir gerekçesi varsa anlamlı. Profil verisi olmadan alınan geçiş kararı genelde pişmanlıkla biter.
Go'daki if err != nil kalabalığı verimliliği düşürmüyor mu?
Yazarken birkaç satır fazla ekliyor, ama okurken hatanın nerede ve nasıl ele alındığını açıkça gösteriyor. Asıl maliyet, hataları bağlamsız döndürmekten gelir. Hata sarmalama alışkanlığı oturduğunda, üretimde hata ayıklama süresi genelde kısalır.
AI ile bir C# projesini Go'ya otomatik çevirmek mümkün mü?
Çalışan bir ilk taslak çıkarmak mümkün, ama çıkan kod çoğu zaman C# mimarisini Go sözdizimiyle tekrar eder. Gereksiz arayüzler, panic ile hata yönetimi ve context yayılmaması sık görülür. Çeviriyi sözleşme testleri ve deneyimli bir Go incelemesiyle birleştirmek gerekir.
Go'da generics var mı, yoksa her şeyi interface{} ile mi yazacağız?
Go 1.18'den beri generics var; Go 1.27 ile metotlar da kendi tip parametrelerini tanımlayabiliyor, ancak arayüz metotları hâlâ tanımlayamıyor. C#'taki kadar zengin değil. Generics'i koleksiyon yardımcıları ve tekrar eden algoritmalar için kullanmak, iş mantığında ise somut tiplerle kalmak genelde daha okunur.
Ekip Go'yu öğrenirken üretim kalitesini nasıl koruruz?
İlk taşınacak servisi düşük riskli seç, golangci-lint gibi bir linter ve -race testlerini CI'da zorunlu yap. Ekipten en az bir kişinin Go deneyimi olsun ya da dışarıdan bir incelemeci al. İlk iki servis bittikten sonra ekip için kısa bir Go stil rehberi yazın.
TypeScript ekibi derleyiciyi Go'ya taşıdı. Bu, Node ekiplerinin de Go'ya geçmesi gerektiği anlamına mı geliyor?
Hayır. TypeScript derleyicisi CPU yoğun, paylaşımlı bellek paralelliğinden doğrudan yararlanan bir iş; çoğu web servisinin darboğazı ise I/O. Bu örneğin asıl dersi yöntemde: ekip yeniden tasarım yerine yapıyı koruyan bir port yaptı ve davranış uyumunu öncelikli tuttu. Kendi geçişinde de önce profil, sonra küçük ve ölçülebilir bir parça.
Notun kaynakları
Sunum stili
12 slayt. Tıklarsan sunum o slayttan başlar.
Slayt notları
C#/.NET ve Node.js'ten Go'ya geçen ekiplerin benchmark'ta görünmeyen maliyetlerini anlatacağım. Örnekler Ekim 2026'daki güncel sürüm Go 1.27'ye göre.
Pişmanlıkla biten geçişlerin çoğu burada başlar. 'Go daha hızlı' cümlesi ölçüm değildir.
Sürenin çoğu I/O beklemesindeyse dil değişimi beklenen kazancı getirmez. Ölçüm olmadan karar verme.
En büyük kültür şoku hata yönetimi: sarmazsan loglarda sadece 'connection refused' görürsün. panic exception değildir.
Tipli nil hatayı error olarak döndürmek, çağıranda 'err != nil' kontrolünü kandırır. JSON'da 'yaş sıfır' ile 'yaş yok' ayrımı için pointer gerekir.
Node'dan gelenler için bir numaralı tuzak gerçek paralellik: korumasız map'e iki goroutine yazarsa program çöker. Go 1.27'de goroutineleak profili varsayılan oldu.
Eski sürümlerde GOMAXPROCS 64 olur, kota tükenir, throttling kuyruk gecikmesini patlatır. GOMEMLIMIT yoksa OOM ile ölen pod'lar görürsün.
İnternetteki operasyon tavsiyelerinin çoğu eski sürümlere ait. automaxprocs gibi çözümler artık gerekmeyebilir; 1.25 ve öncesi destek dışında.
Gölge trafikte JSON alan sırası, tarih formatı, null ile boş dizi farkı gibi küçük farklar ortaya çıkar. Big bang yeniden yazım en riskli yoldur.
Tüm sistemi taşımak yerine CPU yoğun ama kuralı az bir katmanı ayrı Go servisi olarak çıkarmak çok daha düşük riskli bir deneydir.
Model sınıf hiyerarşisini birebir struct'lara ve gereksiz arayüzlere çevirir. Çeviriden sonra Go uzmanı incelemesi, go vet, staticcheck ve go fix şart.
Son mesaj: geçiş kararını ölçümle, uygulamayı deyimle, yayılımı strangler fig ile yapın.