69 puan yazan xguru 2024-07-15 | 6 yorum | WhatsApp'ta paylaş

Sürekli kendi ayağına sıkıyorsan, silahı düzelt

  • Ekipte sistemle ilgili sık yapılan hatalar olur, ama çoğu zaman bu hataları nasıl azaltabileceğimiz üzerine düşünülmez
  • Böyle durumlarda hataları azaltmak için sistemi iyileştirmek önemlidir
  • Deneyim:
    • iOS geliştirirken CoreData kullanıldığında, UI güncellemeleri yalnızca ana thread'de yapılabilir
    • Abonelik callback'leri hem ana thread'de hem de arka plan thread'inde çalıştığı için sık sık sorun çıkıyordu
    • Mevcut ekip üyeleri bunun farkındaydı ve iyi yönetiyordu, ama yeni ekip üyelerinin review'larında sık sık gündeme geliyordu
    • Ara sıra hata olduğunda crash raporuna bakıp DispatchQueue.main.async ekliyorduk
    • Bunu çözmek için abonelik katmanını güncelleyip subscriber'ların ana thread'de çağrılmasını sağladık. Tam olarak 10 dakika sürdü.
    • Bütün bir crash sınıfını ve bir miktar zihinsel yükü ortadan kaldırdı
  • Birkaç dakika düşünüldüğünde herkes için bariz bir sorun olurdu
  • Ama bu tür sorunları çözmek için doğal bir zaman hiç oluşmadığından, tuhaf biçimde uzun süre varlığını sürdürür
    • Yani bu tür sorunlar ekipte uzun süre kalındığında arka plana karışıp görünmez hale gelir
  • Zihniyette bir değişim gerekir
    • Böyle sorunlar olduğunda, kendinin ve ekibinin hayatını kolaylaştırabileceğini zaman zaman kendine hatırlatman gerekir

Kalite ile hız arasında denge kurmak

  • Uygulama hızı ile doğruluğa duyulan güven arasında her zaman bir trade-off vardır
    • İçinde bulunduğun durumda bug'lı bir sürüm çıkarmanın ne kadar kabul edilebilir olduğunu kendine sormalısın
    • Buna verdiğin yanıt çalışma şeklini etkilemiyorsa, gereğinden fazla katısın demektir
  • İlk işimde veri işleme projeleri yaparken, verileri geriye dönük olarak yeniden işleyebilen iyi bir sistemimiz vardı
    • Bug yayınlamanın etkisi çok küçüktü; böyle bir ortamda guardrail'lere belli ölçüde güvenip daha hızlı ilerleyebilirsin
    • %100 test coverage ya da kapsamlı QA süreçleri yalnızca geliştirme hızını düşürür
  • İkinci işimde ise on milyonlarca kişinin kullandığı bir üründe yüksek değerli finansal veriler ve kişisel tanımlayıcı bilgilerle çalışıyordum; burada bug'lar yıkıcı olabiliyordu
    • Küçük bir bug bile postmortem gerektiriyordu
    • Özellikleri çok yavaş bir tempoda yayınladık ama o yıl sıfır bug çıkardığımızı düşünüyorum
  • Çoğu durumda ikinci şirketteki kadar kritik bir ortamda olmazsın
    • Bug'ların ölümcül olmadığı durumlarda (ör. web uygulamalarının %99'u), hızlı yayınlamak ve bug'ları hızlı düzeltmek daha iyidir
    • Başından beri kusursuz bir özellik çıkarmaya zaman harcamaktan daha fazla ilerleme sağlayabilirsin

Testereyi bilemek için harcanan zaman neredeyse her zaman değerlidir

  • Araçları iyi kullanmak önemlidir
  • Kodu hızlı yazabilmeli, temel kısayolları bilmeli, işletim sistemi ve shell konusunda yetkin olmalısın
    • Sık sık yeniden adlandırma, type definition'a gitme, referans bulma gibi işler yapacaksın
    • Editöründeki tüm temel kısayolları bilmeli, kendinden emin ve hızlı yazabilmelisin
    • Tarayıcı geliştirici araçlarını etkili kullanmak da önemlidir
  • Araçları iyi seçmek ve onlara ustaca hakim olmak büyük bir avantajdır
  • Yeni bir mühendiste görülen en büyük olumlu işaretlerden biri, araç seçimi ve onları ustalıkla kullanmaya ilgi duymasıdır

Bir zorluğu basitçe açıklayamıyorsan, muhtemelen bu tesadüfi karmaşıklıktır ve çözmeye değerdir

  • En sevdiğim yöneticilerden birinin, bir implementasyonun zor olduğunu söylediğimde sürekli üsteleme alışkanlığı vardı
    • Çoğu zaman cevabı, genelde "Bu sadece X'i Y yaparken göndermek değil mi?" ya da "Bu birkaç ay önce yaptığımız Z gibi değil mi?" olurdu
    • Bu itirazlar çok yüksek seviyedendi; benim anlatmaya çalıştığım gerçek fonksiyon ve sınıf seviyesinde değildi
  • Bir yöneticinin bu şekilde basitleştirmesi genelde sadece sinir bozucu kabul edilir
  • Ama şaşırtıcı şekilde, yöneticim sormaya devam ettiğinde anlattığım karmaşıklığın büyük kısmının tesadüfi karmaşıklık olduğunu sık sık fark ettim
  • Ve gerçekten de önce onu çözerek problemi yöneticinin söylediği kadar önemsiz hale getirebiliyordum
  • Bu yaklaşım gelecekteki değişiklikleri de daha kolay hale getirme eğilimindedir

Bug'ları bir katman daha derinde çözmeye çalış

  • Bir bug'ı yüzeysel olarak düzeltmek yerine, kök nedenini bulup çözmek önemlidir
  • Dashboard'da, mevcut giriş yapmış kullanıcının state'inden alınan bir User nesnesiyle çalışan bir React component'in olduğunu düşün
    • Sentry'de render sırasında user'ın null olduğuna dair bir bug raporu gelir
      • Hızlıca if (!user) return null ekleyebilirsin
    • Ya da biraz daha araştırırsan, logout fonksiyonunun iki ayrı state update yaptığını görebilirsin
      • İlki kullanıcıyı null yapar, ikincisi ise ana sayfaya redirect eder
    • Bu ikisinin sırasını değiştirirsen, artık hiçbir component bu bug'ı bir daha yaşamaz
    • Çünkü dashboard içinde kullanıcı nesnesi hiçbir zaman null olmamalıdır
  • İlk tür bug düzeltmelerini yapmaya devam edersen sistem dağınık hale gelir,
    ikinci tür düzeltmeleri yapmaya devam edersen temiz bir sistem ve invariant'lara dair derin bir anlayış kazanırsın

Bug araştırırken geçmişi kurcalamanın değerini küçümseme

  • println ve debugger gibi genel araçlarla garip sorunları debug etme konusunda oldukça iyiydim
  • Bu yüzden bir bug'ın geçmişini anlamak için git'e çok bakmazdım, ama bazı bug'larda bu çok önemlidir
  • Yakın zamanda bir sunucuda belleğin sürekli sızdığı, OOM nedeniyle kapanıp yeniden başladığı bir sorun vardı
    • Olası tüm nedenler elenmişti ve bunu local'de yeniden üretemiyorduk
    • Gözleri bağlı dart atmak gibi hissettiriyordu
    • Commit geçmişine bakınca, Play Store ödeme desteği eklendikten sonra başlamış olduğunu gördük
    • Bu sadece birkaç HTTP isteği gibi göründüğünden, normalde milyon yıl düşünsem bakacağım bir yer değildi
    • Meğer ilk access token süresi dolduktan sonra access token almaya çalışan sonsuz bir loop'a giriyormuş
    • Her istek belleğe belki yalnızca 1kB ekliyordu, ama birden fazla thread'de her 10ms'de bir retry edilince hızla birikiyordu
    • Normalde bu bir stack overflow yaratırdı, ama Rust'ta asynchronous recursion kullandığımız için stack overflow oluşmadı
    • Bu asla aklıma gelmezdi, ama soruna neden olduğu açık olan belirli koda bakınca teori bir anda oluştu
  • Bu yaklaşımı ne zaman kullanman gerektiğine dair kesin bir kural yoktur
    • Daha çok sezgiye dayanır; bug raporuna karşı duyulan başka türden bir "ha?" hissi bu tür bir araştırmayı tetikler
    • Zamanla bu sezgiyi geliştirebilirsin, ama bazen bunun çok değerli olabileceğini bilmek bile yeterlidir
  • Uygunsa git bisect dene
    • Elinde kötü olduğunu bildiğin bir commit ve iyi olduğunu bildiğin bir commit varsa

Kötü kod geri bildirim üretir, mükemmel kod üretmez. Hata yapacaksan kötü kod yazma yönünde yap

  • Berbat kod yazmak gerçekten çok kolaydır
  • Ama tüm best practice'lere eksiksiz uyan kod yazmak da gerçekten çok kolaydır
    • Unit, integration, fuzz ve mutation testlerinin hepsinden geçmesi gerekir; startup ise o zamana kadar parasını bitirmiş olur
  • Programlamanın büyük kısmı denge bulmaktır
  • Hızlı kod yazma yönünde hata yaparsan...
    • Bazen kötü teknik borç yüzünden zor durumda kalırsın
    • "Veri işleme için harika testler eklemem gerekiyor" dersin
      • Çünkü bunu sonradan düzeltmek çoğu zaman imkansızdır
    • "Tablo tasarımını gerçekten iyi düşünmem gerekiyor" dersin
      • Çünkü bunu kesinti olmadan değiştirmek çok zor olabilir
  • Mükemmel kod yazma yönünde hata yaparsan...
    • Hiç geri bildirim almazsın
    • Her şey evrensel olarak uzun sürer
    • Zamanını nerede iyi harcadığını, nerede boşa harcadığını bilemezsin
    • Geri bildirim mekanizması öğrenmek için kritiktir, ama sen bundan mahrum kalırsın
  • "Kötü" kodun ne anlama geldiğini netleştirelim
    • Bu, "HashMap oluşturma söz dizimini hatırlayamadım, o yüzden iç döngüyü iki kez kullandım" demek değildir
    • Daha çok şuna benzer:
      • Belirli bir state'in ifade edilmesini imkansız kılmak için veri toplama yapısını baştan yazmak yerine, birkaç kilit kontrol noktasına invariant assertion'ları eklemek
      • Sunucu modeli yazılacak DTO ile tamamen aynıysa, tüm boilerplate'i yazmak yerine şimdilik onu doğrudan serialize etmek; gerekirse DTO'yu sonra yazmak
      • Bu component'ler önemsiz ve bug'lı olsa da büyük sorun çıkarmayacaksa test yazmayı atlamak

Debug etmeyi kolaylaştır

  • Yıllar içinde yazılımı debug etmeyi kolaylaştıran pek çok küçük hile öğrendim
    • Debug etmeyi kolaylaştırmak için çaba göstermezsen, yazılım giderek daha karmaşık hale geldikçe her bir sorunu debug etmek için muazzam zaman harcarsın
    • Değişiklik yapmaktan korkmaya başlarsın. Çünkü birkaç yeni bug'ı anlamak bir haftanı alabilir
  • Debug süresinde kurulum, yeniden üretim ve sonrasında temizlik için harcanan zamanı dikkatle izle
    • Eğer bu oran %50'yi geçiyorsa, bu sefer biraz daha uzun sürse bile işi kolaylaştırmanın bir yolunu bulmalısın
    • Diğer her şey eşitse, bug düzeltmek zamanla daha kolay hale gelmelidir

Bir ekipte çalışırken her zaman soru sor

  • "Her şeyi kendi başıma çözmeye çalışmak" ile "önemsiz sorularla iş arkadaşlarını rahatsız etmek" arasında bir spektrum vardır
    • Kariyerinin başındaki çoğu kişinin ilk tarafa fazla kaydığını düşünüyorum
  • Çevrende her zaman codebase'de senden daha uzun süredir bulunan, teknoloji X'i senden çok daha iyi bilen, ürünü daha iyi tanıyan ya da sadece daha deneyimli bir mühendis vardır
  • Bir yerde çalışmaya başladığın ilk 6 ay boyunca, çoğu zaman bir şeyi anlamaya bir saatten fazla harcarsın; oysa cevabı birkaç dakikada alabilirsin
  • Soru sor. Soru sormanın birini rahatsız ettiği tek durum, cevabı birkaç dakika içinde kendi başına bulabileceğinin açık olmasıdır

Dağıtım döngüsü çok önemlidir. Hızlı ve sık dağıtım yapmanın yollarını dikkatle düşünmelisin

  • Startup'ların runway'i sınırlıdır ve projelerin son teslim tarihleri vardır
  • İşinden ayrılıp bağımsız çalışmaya geçtiğinde, biriktirdiğin para yalnızca birkaç ay yeter
  • İdeal durumda, proje hızı zaman içinde bileşik şekilde artar ve hayal ettiğinden daha hızlı özellik yayınlamaya başlarsın
  • Hızlı dağıtım yapabilmek için pek çok şey gerekir
    • Bug'lara açık olmayan sistemler
    • Ekipler arasında hızlı turnaround süresi
    • Yeni bir özelliğin %10'unu kesip atma isteği (ama mühendislik zamanının %50'sini yiyecek kısmı) ve bunun hangi kısım olduğunu görecek içgörü
    • Yeni ekranlar/özellikler/endpoint'ler için birleştirilebilir, tutarlı ve yeniden kullanılabilir pattern'ler
    • Hızlı ve kolay dağıtım
    • Hızı düşürmeyen süreçler (istikrarsız testler, yavaş CI, aşırı titiz linter'lar, yavaş PR review'ları, din gibi görülen JIRA vb.)
    • Ve bunun gibi milyonlarca başka şey
  • Yavaş dağıtım yapmak da production'ı bozmak kadar postmortem gerektirmelidir
    • Sektörümüz böyle işlemiyor olabilir, ama bu senin kişisel olarak hızlı dağıtımı kuzey yıldızın yapmana engel değildir

6 yorum

 
carnoxen 2024-07-19

"Ayağına kurşun sıkmak" = kendi kendine zarar vermek
anlamına mı geliyor?

 
yunghn 2024-07-25

Eğer sorun, yanlış bir koddan (bozuk bir silah) kaynaklanıyorsa (ayağına ateş etmek), sana söylenen şey silahı tamir etmendir.

 
gargoyle92 2024-07-16

Bu, sanki kafamın içindekiler olduğu gibi dışarı dökülmüş gibi bir şoktu, vay canına...

 
cbbatte 2024-07-16

Çok beğendim!!

 
hannah0su 2024-07-15

Keyifle okudum.

 
arfwene 2024-07-15

Geliştirici değilim ama yine de katıldığım çok nokta var gibi görünüyor.