Open Source Pakete Yazdığım Kod Golang Projelerini Nasıl Patlattı, Neler Öğrendim
--
Merhabalar, bugün sizlere popüler bir open-source projeye yaptığım geliştirmenin, dünya çapında projeyi kullanan kişilerin nasıl hata almasına sebep olduğunu ve bu süreçten neler öğrendiğimi anlatacağım.
İçerik
— Swaggo Projesi
— İhtiyaç ve Yaptığım Katkı
— Yaşanılan Sorun
— Sorunun Sebebi
— Body Barındırmayan Function Tanımları (go:linkname)
— Sorunun Çözümü
— Öğrendiklerim
SWAGGO Projesi
Go dilinde geliştirdiğiniz API projelerinize swagger dökümanı oluşturmak, Java, C# gibi dillerden biraz farklı. Genellikle yazdığınız kodun içerisinde yorum satırlarında Swagger direktifleri ekleyerek oluşturabiliyorsunuz ve bunun için çok fazla alternatifiniz yok.
swaggo/swag projesi en popüler ve yaygın olarak kullanılan çözümlerden birisi.
Bu projenin yaptığı temel işlem, Go dosyalarını gezip yapılan tanımlamalardaki yorum satırlarını işleyerek swagger dökümanını oluşturuyor. Bu işlemleri de Abstract Syntax Tree (AST) oluşturup gezerek yapıyor.
İhtiyaç ve Yaptığım Katkı
Ekip arkadaşımla beraber bir projemiz için swagger dökümanı oluşturmaya çalışırken swaggo/swag aracını kullanarak istediğimiz tanımlayı yapamadığımızı fark ettik.
Yapmak istediğimiz şey, request ve response objelerini oluşturduğumuz handler fonksiyonunun içerisinde tanımlamak ve sadece fonksiyon içerisinden erişilmesini sağlamaktı. Fakat bunu yaptığımız zaman, ilgili request ve response objelerine swaggo/swag parser paketi erişemiyordu.
Ben de bu sorunu aşmak amacıyla swaggo/swag kütüphanesine bir katkı yapmak istedim.
Öncelikle kütüphaneye issue açıp böyle bir ihtiyacımız olduğunu ve bu geliştirmeyi yaparak katkı sağlamak istediğimi sordum.
Cevap olarak, private tanımlamaları parse etmenin mümkün olmadığını ve nasıl yapacağımı sorduklarında, sunduğum öneriyi merak ettiklerini ve geliştirmeyi beklediklerini söylediler.
Ben de kolları sıvadım. İlk fırsatta geliştirmeyi yaptım ve projeye PR açtım.
Yaptığım geliştirme sonucu request/response objeleri fonksiyon içerisinde tanımlanabilir hale geldi. 💪
Yaşanılan Sorun
Bir cuma akşamı… Mesai bitti bitecek, dakikalar var… Haftasonuna girmeyi iple çekiyorum… Veee… Ekip arkadaşımdan, projelerimizden birinin hata aldığına dair mesaj geldi 😞
Aslında tam konuşmayı paylaşmak istiyorum:
Hemen projenin Github sayfasına gittik ve issue kısmına baktık. Ve gördüğümüz şey beni biraz tedirgin etti.
Birçok swaggo/swag paketi kullanıcısı hata yaşıyordu ve hepsi de benim yaptığım geliştirme sebebiyle CI/CD pipeline’larında veya manuel olarak swagger generate etme işleminde hata alıyordu.
Açıkçası bu duruma şaşırmıştım çünkü yaptığım geliştirme için unit testler yazmıştım ve kod doğru çalışıyordu. Hatta önceki hiçbir geliştirmeyi de bozmadığını mevcut unit testlerin başarılı bir şekilde geçmesi ispat ediyordu.
Peki sorun ne olabilirdi? 🤔
Sorunun Sebebi
swaggo/swag için yaptığım geliştirmeden bir ekran görüntüsü ekliyorum.
Hata mesajlarında da gördüğümüz üzere hataparseFunctionScopedTypesFromFile
methodunda alınıyor. Hatta tam olarak hata alınan satır da 168.
Şimdi yazdığım bu methoda daha yakından bakalım.
168. satırda AST geziliyor ve içerisindeki tanımlamalar arasında FunctionDecleration var ise tanımlanan fonskiyonun içerisinde neler yazılmış bunu bir for döngüsü ile geziyor.
Peki bir fonksiyon tanımının var olduğundan eminsek, neden bu fonksiyon tanımını for döngüsü ile gezmeye çalışırken hata alıyoruz?
Nil pointer hatası neyden kaynaklı?
functionDeclaration.Body
değişkenine baktığımızda pointer tipinde olduğunu görüyoruz.
Burada bir nil kontrolü yapmamıştım çünkü eğer bir fonksiyon tanımlandıysa o fonksiyonun body tanımı olacağını varsaymıştım.
Fakat gerçekte öyle olmayabilir.👇
Body Barındırmayan Function Tanımları
Bu tabir size yabancı geliyor mu? Bir fonksiyonu body yazmadan tanımlayabilir miyiz?
Aşağıdaki kodu ele alalım, sizce bu kod Go dilinde geçerli bir tanım mıdır?
Çalıştırmayı denersek hata alır mıyız?
Evet tahmin ettiğiniz üzere bu kodu çalıştıramayız, hatta compile edemeyiz çünkü geçerli bir tanımlama değildir.
Peki ya aşağıdaki kodu çalıştırabilir miyiz?
Burada ise bir öncekinden farklı olarak cevap evet çalıştırabiliriz olacak.
Go’nun az bilinen bir özelliği olan go:linkname
direktifi ile bir fonksiyon tanımını başka paketlerin fonksiyon tanımlarına linkleyebiliyor olmamız.
Böylelikle body tanımı yapmadan fonksiyon tanımlayabiliyoruz.
Ve hatta dikkat ettiyseniz bu özellik, Go runtime paketi altında tanımlanan private bir fonksiyonu kullanabilmemi sağladı.
Projelerimizde kullandığımız harici bağımlılıklar bu gibi fonksiyon tanımlamalarına sahip olabilir. Ve swag --parseDependency
komutu ile swagger dökümanı oluşturmaya çalıştığımızda, bağımlılıkları da parse ettiği için bu tanıma sahip fonksiyonlar yaptığım geliştirmede panic yaşanmasına sebep oluyordu.
Sorunun Çözümü
Hemen sorunu çözmek için geliştirme yapacaktım fakat open-source topluluğun güzel yanı bu ki bir başka geliştirici benden önce hatayı fixleyen kodu yazıp PR oluşturmuş.
Öğrendiklerim
Bu yaşanan sorunla birlikte kendime bazı dersler çıkarttım.
✅ Asla nil pointer kontrolünü varsayımla atlama
✅ Unit testler her zaman yeterli olmayabilir. End to end test adımını atlama
✅ Go ile go:linkname
kullanarak body tanımı olmayan fonksiyon yazabilir, private paketlerdeki fonksiyonları import edebiliriz
✅ Asla paket bağımlılıklarını latest version yapma, çalıştığına emin olduğun versionu belirt
Umarım sizler için eğlenceli ve faydalı bir içerik olmuştur. Kendize iyi bakın bugsız kodlar dilerim 🙏