Kod yazmak kolaydır
Sürdürülebilir kod yazmak zordur
Bir ekibin güvenebileceği kod yazmak yazılım mühendisliğidir
Sadece söz dizimi değil
Sadece “temiz kod” değil
Sadece GitHub mekanikleri değil
Bu ders mühendislik disiplini hakkındadır
Bir proje nadiren insanların hiç kod yazamamasından dolayı başarısız olur.
Bir proje çoğu zaman kodun şu hale gelmesinden dolayı başarısız olur:
Anlaşılmaz
Test edilemez
İncelenmesi imkansız
Değiştirilmesi zor
“Çalışıyor, yeterince iyi.”
“Sonra temizleriz.”
“Testler çok fazla zaman alıyor.”
“Küçük değişiklikler için inceleme gerekmez.”
“Ekip arkadaşlarım anlayacaktır.”
Sonra genellikle hiç gelmez
Küçük kötü alışkanlıklar ekip kültürü olur
Ekip kültürü ürün kalitesine dönüşür
Profesyonel bir mühendis şunu sorar:
Başka bir kişi bunu hızlıca anlayabilir mi?
Bu daha sonra güvenle değiştirilebilir mi?
Bu test edilebilir mi?
Bu iyi bir şekilde incelenebilir mi?
Bu ekibi hızlandırır mı yoksa yavaşlatır mı?
Kötü kod şunlara neden olur:
Daha fazla hata
Daha yavaş uyum süreci
Zorlu hata ayıklama
Tekrarlanan iş
Değişikliklerden sonra bozulan özellikler
Kod tabanına dokunma korkusu
İyi kod şunları sağlar:
Daha hızlı geliştirme
Daha kolay iş birliği
Daha güvenli yeniden düzenleme
Daha iyi incelemeler
Daha fazla güven
Daha tutarlı ürünler
“Kodunuz sadece bilgisayar için değildir. Kodunuz ekip arkadaşlarınız, gelecekteki kendiniz ve ürün içindir.”
Kodun çalışmasını sağlamak
Kodu güvenilir, sürdürülebilir, iş birliğine uygun ve üretime hazır hale getirmek
Doğru mantık
Açık yapı
Testler
İncelemeler
Sürüm kontrolü
Öngörülebilir dağıtım
Ekip anlayışı
Bir fonksiyonu bir kez yazarsınız
Ekip arkadaşlarınız onu düzinelerce kez okur
Gelecekteki siz, aylar sonra hiçbir şey hatırlamadan okur
Okunabilirlik isteğe bağlı değildir
Okunabilirlik doğruluğun bir parçasıdır
def f(x, y, z): if x: if y > 0: return z * 0.2 return 0
f nedir? Ne hesaplıyor?
x, y, z nedir?
0.2 neyi temsil ediyor?
Hiç kimse bunu güvenle inceleyemez, test edemez veya bakımını yapamaz
def calculate_discount(price, has_membership, loyalty_points): if not has_membership: return 0 if loyalty_points <= 0: return 0 return price * 0.2
Fonksiyon adı amacı açıklıyor
Parametreler anlamlı
Mantık bir cümle gibi okunuyor
İnceleyici doğruluğu hemen kontrol edebilir
Sadece şunu sormayın: “Çalışıyor mu?”
Ayrıca şunu da sorun:
Okunabilir mi?
Test edilebilir mi?
Değiştirmek güvenli mi?
Bunu bir PR'da onaylar mıydım?
Kötü isimlendirme kafa karışıklığı yaratır. İyi isimlendirme netlik yaratır.
İsimler şunları ortaya koymalıdır: bir şeyin ne olduğu, ne yaptığı, neden var olduğu.
def calc(a, b): return a * b
calc ne hesaplıyor?
a ve b nedir?
Alan, fiyat, herhangi bir şey olabilir - bilinmesi imkansız
İnceleyici doğruluğu doğrulayamaz
def calculate_total_price(unit_price, quantity): return unit_price * quantity
Amaç hemen anlaşılır
Parametreler kendilerini açıklar
İnceleyici doğrulayabilir: “Evet, toplam fiyat = birim fiyat × miktar”
Gelecekteki geliştiriciler bu fonksiyonu yanlış kullanmayacaktır
Tanıdık olmayan kodu okurken kafa karışıklığını azaltır
Fonksiyon davranışı hakkında yanlış varsayımları önler
Parametrelerin yanlış kullanımını önler
Yanlış anlamadan kaynaklanan gelecekteki hataları önler
Bir fonksiyon çok fazla şey yaptığında, test etmek zorlaşır, yeniden kullanım imkansız hale gelir ve hata ayıklama acı verici olur.
def register_user(user): validate_user(user) save_user_to_database(user) send_welcome_email(user) log_activity(user) notify_admin(user)
Bir fonksiyonda beş sorumluluk
E-posta göndermeden kayıt test edilemez
E-posta başarısız olursa, kullanıcı yine de kaydedilir mi?
Herhangi bir adımı yeniden kullanmak imkansız
def register_user(user): validate_user(user) save_user_to_database(user) def onboard_user(user): send_welcome_email(user) log_activity(user) def handle_registration(user): register_user(user) onboard_user(user) notify_admin(user)
Her fonksiyonun açık, tek bir amacı var
Her biri bağımsız olarak test edilebilir
Hata yönetimi kontrol edilebilir hale gelir
Tekrarlanan mantık, kaçırılan düzeltmeler, kod tabanında tutarsızlık ve maliyetli bakım demektir.
# In checkout.py final_price = price - price * 0.1 # In invoice.py final_price = price - price * 0.1 # In report.py final_price = price - price * 0.1
def apply_discount(price, discount_rate): return price * (1 - discount_rate) # Everywhere: final_price = apply_discount(price, 0.1)
Tek doğru kaynak
Bir kez değiştir, her yerde düzelt
Test etmesi kolay
Bulmak ve denetlemek kolay
Kodunuzu test etmek zorsa, muhtemelen kötü tasarlanmıştır.
Test edilebilir kod modüler, açık ve net giriş-çıkışlara sahiptir.
def process_order(order_id): order = db.get_order(order_id) # database call user = db.get_user(order.user_id) # database call send_email(user.email, order.summary) # side effect db.mark_processed(order_id) # database call
Gerçek bir veritabanı olmadan test edilemez
Gerçek e-posta göndermeden test edilemez
Tüm yan etkiler sıkı sıkıya bağlı
def process_order(order, user, email_sender, db): email_sender.send(user.email, order.summary) db.mark_processed(order.id)
Bağımlılıklar enjekte edilir - taklit etmesi kolay
Her bağımlılık bağımsız olarak test edilebilir
Mantık altyapıdan ayrılmış
Fonksiyon imzası tam olarak neye ihtiyacı olduğunu söyler
Sessiz başarısızlıklar en tehlikeli hata türüdür.
Bir şeyler ters gittiğinde, bunu yüksek sesli ve açık yapın.
def get_user_age(user): if user is None: return None if user.birthday is None: return None return calculate_age(user.birthday)
Çağıran taraf None alır ve nedenini bilmez
Hata gizlenir ve sessizce yayılır
Hata ayıklama bir tahmin oyununa dönüşür
def get_user_age(user): if user is None: raise ValueError("User must not be None") if user.birthday is None: raise ValueError("User birthday is missing") return calculate_age(user.birthday)
Hata hemen görünür
Mesaj tam olarak neyin yanlış olduğunu söyler
Hata erken yakalanır, ileriki aşamalarda değil
Karmaşık kod zeki değildir. Bakımı, incelemesi ve hata ayıklaması pahalıdır.
def get_price(user, product): if user.is_member: if product.on_sale: if user.loyalty_points > 100: return product.price * 0.7 else: return product.price * 0.8 else: return product.price * 0.9 else: if product.on_sale: return product.price * 0.95 else: return product.price
Derin iç içe geçmiş, takip etmesi zor, hata çıkarması kolay
def get_discount_rate(user, product): if not user.is_member and not product.on_sale: return 0 if not user.is_member and product.on_sale: return 0.05 if user.is_member and not product.on_sale: return 0.1 if user.loyalty_points > 100: return 0.3 return 0.2 def get_price(user, product): return product.price * (1 - get_discount_rate(user, product))
Düz, okunabilir, her durum açık ve test edilebilir
İncelemesi zor
Yüksek hata riski
Birleştirmesi acı verici
Kafa karıştıran git geçmişi
İncelemesi kolay
Düşük risk
Hızlı birleştirme
Açık geçmiş
Tercih edin:
Daha küçük fonksiyonlar
Daha küçük commit'ler
Daha küçük PR'lar
Daha küçük inceleme birimleri
İş akışı olmadan ekipler şunları oluşturur:
üzerine yazılan kod, incelenmemiş değişiklikler, bozuk main branch'ler, kafa karışıklığı.
Main her zaman dağıtılabilir olmalıdır
Doğrudan commit'ler incelemeyi atlar
Birleştirme çakışmaları yıkıcı hale gelir
Temiz bir şekilde geri alma yolu yok
Ekip kod tabanına olan güvenini kaybeder
fix my-branch test123 update
feature/user-registration fix/login-redirect-bug chore/update-dependencies refactor/split-order-service
Bir branch adı işin amacını tanımlamalıdır
fix update wip asdf changed stuff
fix: redirect to login after session timeout feat: add email validation to registration refactor: extract discount logic into helper test: add edge cases for price calculator docs: update API authentication section
Küçük ve tek bir şeye odaklanmış
Açık bir başlık ve açıklamaya sahip
Değişikliğin neden yapıldığını açıklar
Uygunsa testleri içerir
İlgisiz değişiklikleri karıştırmaz
Başlık: “güncellemeler”
Açıklama yok
47 dosya değişti
Özellik + yeniden düzenleme + stil düzeltmesini karıştırır
Test yok
Başlık: “feat: kayıta e-posta doğrulaması eklendi”
Açıklama yaklaşımı anlatır
4 dosya değişti
Tek bir özelliğe odaklanmış
Birim testleri içerir
Ciddi bir depo yapı, dokümantasyon, yapılandırma ve standartları içerir - sadece kaynak dosyaları değil.
project/ ├── src/ │ ├── models/ │ ├── services/ │ ├── routes/ │ └── utils/ ├── tests/ │ ├── unit/ │ └── integration/ ├── .github/ │ └── workflows/ │ └── ci.yml ├── .gitignore ├── README.md ├── requirements.txt ├── Makefile └── .env.example
Bir README yeni bir ekip üyesine şunları söylemelidir:
Projenin ne yaptığı
Nasıl kurulacağı
Nasıl çalıştırılacağı
Testlerin nasıl çalıştırılacağı
Nasıl katkıda bulunulacağı
Proje adı ve tek satırlık açıklama
Ön koşullar (Python sürümü, bağımlılıklar)
Kurulum adımları
Yerel olarak nasıl çalıştırılacağı
Testlerin nasıl çalıştırılacağı
Gerekli ortam değişkenleri
Proje yapısına genel bakış
Katkıda bulunma kılavuzları
“Testler için zamanımız yok”
“Benim bilgisayarımda çalışıyor”
“Testler bizi yavaşlatıyor”
Test yapmamak için zamanınız yok
Her bilgisayarda çalışması gerekiyor
Testler sizi sonradan yavaşlamaktan kurtarır
Kodu değiştirme güveni
Daha hızlı hata ayıklama - testler neyin nerede bozulduğunu gösterir
Beklenen davranışın dokümantasyonu
Yeniden düzenleme için güvenlik ağı
Kodunuzun çalıştığının kanıtı
def add(a, b): return a + b
Basit fonksiyon. Doğru çalıştığını nasıl bilebiliriz?
Bir test yazarız.
def test_add_returns_sum(): assert add(2, 3) == 5
Açık, okunabilir, çalıştırması hızlı
add bozulursa, bu test hemen yakalar
Bu testin neyi kontrol ettiğini herkes anlayabilir
Başka ne test etmeliyiz?
Negatif sayılar
Sıfır değerleri
Çok büyük sayılar
Tür uyumsuzlukları (uygunsa)
Sınır koşulları
Hata durumları
def test_add_positive_numbers(): assert add(2, 3) == 5 def test_add_negative_numbers(): assert add(-1, -2) == -3 def test_add_with_zero(): assert add(0, 5) == 5 assert add(5, 0) == 5 def test_add_mixed_signs(): assert add(-3, 5) == 2
Her testin ne doğruladığını anlatan açık bir adı var
Hatalar üretime ulaşır
Yeniden düzenleme korkutucu hale gelir
Bir değişikliğin bir şeyi bozup bozmadığını kimse bilmez
Manuel test yavaş ve güvenilmezdir
Regresyonlar sürekli geri gelir
Ekip koda olan güvenini kaybeder
Hatalar birleştirmeden önce yakalanır
Yeniden düzenleme güvenli ve hızlıdır
CI regresyonları otomatik olarak yakalar
Yeni ekip üyeleri beklenen davranışı anlar
Dağıtımlar güvenle yapılır
Ekip hızı zamanla artar
CI = Sürekli Entegrasyon (Continuous Integration)
Main branch'e ulaşmadan önce her değişikliği otomatik olarak doğrulayın.
Bozuk kodun main'e ulaşması
Test edilmemiş kodun birleştirilmesi
Stil tutarsızlıkları
“Benim bilgisayarımda çalışıyor” sorunları
Otomatik kalite kontrolleri
Her PR'da hızlı geri bildirim
Tutarlı standartlar
Ekip güveni
Tüm testleri çalıştır (birim + entegrasyon)
Kod biçimlendirmesini kontrol et (black, prettier, vb.)
Linter çalıştır (pylint, eslint, flake8)
Tür ek açıklamalarını kontrol et (mypy)
Test kapsamını ölç
Güvenlik taraması
Derleme kontrolü (proje derleniyor/oluşuyor mu?)
name: Python Checks on: pull_request: push: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.11' - run: pip install -r requirements.txt - run: pytest
Her PR otomatik olarak testleri çalıştırır
Bozuk PR'lar birleştirilemez (branch korumasıyla)
Ekibin testleri çalıştırmayı hatırlamasına gerek yok
Geri bildirim hızlı ve tutarlı
Test kapsamı, kodunuzun ne kadarının testler tarafından çalıştırıldığını ölçer.
Yüksek kapsam, hata olmadığı anlamına gelmez
Düşük kapsam, büyük test edilmemiş alanlar demektir
Kapsam bir kılavuzdur, garanti değildir
Çoğu proje için iyi hedefler:
Temel olarak %70-85 test kapsamı
Tüm kritik iş mantığı test edilmiş
Temel fonksiyonlar için tüm uç durumlar test edilmiş
Birleştirmeden önce CI geçmeli (istisna yok)
Her PR'da linter ve biçimlendirici kontrolleri
Bir formalite
Bir mühür basma işlemi
Bir güç gösterisi
Bir kalite sohbeti
Bir öğrenme fırsatı
Bir ekip savunma mekanizması
Hataları üretime ulaşmadan yakalayın
Ekip genelinde bilgi paylaşın
Tutarlı kod kalitesini koruyun
Kod tabanının sahipliğini yayın
Birbirinden öğrenin ve öğretin
Ekip güveni oluşturun
Diff'e 30 saniye göz at
“Onayla”ya tıkla
Yorum bırakma
Kodu çalıştırma
Değişen her satırı oku
Mantığı ve uç durumları kontrol et
Açıklayıcı sorular sor
Saygılı bir şekilde iyileştirmeler öner
Testlerin var olduğunu doğrula
Şunları kontrol edin:
Doğruluk - mantık yapması gerekeni yapıyor mu?
Okunabilirlik - sormadan anlayabiliyor musunuz?
İsimlendirme - isimler açık ve doğru mu?
Tekrarlama - gereksiz yere tekrarlanan bir şey var mı?
Hata yönetimi - uç durumlar kapsanmış mı?
Testler - yeni mantık için testler var mı?
Kapsam - PR odaklanmış mı kalıyor?
İnceleme sırasında şu soruları yanıtlayın:
Bu kodun ne yaptığını anlıyor muyum?
Bunun bakımını yapmaktan rahat olur muydum?
Sessizce bozulabilecek bir şey var mı?
Eksik testler var mı?
PR açıklaması doğru mu?
Yeni bir ekip üyesi bunu anlar mı?
Neyin yanlış olduğunu açıklamaz
Neden yanlış olduğunu açıklamaz
Bir iyileştirme önermez
Geri bildirim değil, saldırı gibi hissedilir
Yazarın zamanını boşa harcar
None döndürüyor, ancak 42. satırdaki çağıran taraf None kontrolü yapmıyor. Bu, çalışma zamanında bir AttributeError'a neden olabilir. Burada bir istisna fırlatabilir veya çağıran tarafa bir None kontrolü ekleyebilir miyiz?”
Sorunu açıklar
Etkisini gösterir
Somut bir düzeltme önerir
Yazara bir iş birlikçi olarak davranır
Komutlar yerine meraklı sorular kullanın:
“Bu girdi negatif olursa ne olur?”
“Bunu erken dönüşle basitleştirebilir miyiz?”
“Bunun için mevcut yardımcı fonksiyonu kullanmamamızın bir nedeni var mı?”
“Bunu ayrı bir fonksiyona çıkarmak daha açık olmaz mı?”
Güçlü bir inceleyici şunları fark eder:
Açıklamasız sihirli sayılar
Uç durumlar için eksik hata yönetimi
Çıkarılması gereken tekrarlanan mantık
Birden fazla iş yapan fonksiyonlar
Tutarsız isimlendirme kuralları
Eksik veya yetersiz testler
Güvenlik endişeleri (sabit kodlanmış gizli anahtarlar, SQL injection, vb.)
def calc(items): t = 0 for i in items: if i["type"] == "book": t += i["price"] * 0.9 else: t += i["price"] return t
İnceleyici olarak sorun:
calc ne hesaplıyor?
0.9 ne anlama geliyor? Neden özellikle kitaplar?
Bir öğenin “type” veya “price” anahtarı yoksa ne olur?
Testler nerede?
BOOK_DISCOUNT_RATE = 0.1 def get_item_price(item): price = item["price"] if item["type"] == "book": return price * (1 - BOOK_DISCOUNT_RATE) return price def calculate_cart_total(items): return sum(get_item_price(item) for item in items)
Sihirli sayı adlandırılmış sabitle değiştirildi
Fiyatlandırma mantığı kendi fonksiyonuna çıkarıldı
Her fonksiyon bağımsız olarak test edilebilir
Bunun için linter varken stil detaylarını eleştirmek
Yazarın kodunu kendi tarzlarında yeniden yazmak
Okumadan onaylamak
Kişisel tercih için (doğruluk değil) PR'ı engellemek
Küçümseyici veya umursamaz olmak
İletişim olmadan incelemeleri günlerce geciktirmek
“Kodu anlamıyorsanız, onaylamayın.”
Geri bildirimi kişisel almayın
Dinlemeden tartışmayın
Önerileri hemen reddetmeyin
Saldırıya uğramış hissetmeyin
Her yorumu dikkatlice okuyun
Gerekirse açıklama isteyin
İnceleyicilere zaman ayırdıkları için teşekkür edin
Mantığınızı saygılı bir şekilde açıklayın
Değişiklikleri hızla yapın
Bu yanıtlar iş birliğini kapatır ve güveni aşındırır
Bu yanıtlar güven oluşturur ve ekibi güçlendirir
Haklı olduğunuzu kanıtlamak
Egonuzu savunmak
Hızlı onay almak
Daha iyi kod göndermek
Birbirinden öğrenmek
Güvenilir bir ürün oluşturmak
Mühendis olarak büyümek
AI mühendisleri daha hızlı yapabilir, ama aynı zamanda daha özensiz de yapabilir.
Zaman kazanmak için şablon kod üretin
Mevcut fonksiyonlar için test senaryoları taslağı oluşturun
Tanıdık olmayan kodu veya kütüphaneleri açıklayın
Alternatif yaklaşımlar önerin
Dokümantasyon yazmaya yardımcı olun
Hata mesajlarını analiz ederek hata ayıklamaya yardımcı olun
PR göndermeden önce kodu ön incelemeye tabi tutun
AI'yı anlamadan kullanmak:
Kodu açıklayamıyorsanız, ona sahip değilsiniz
Anlamadığınız hatalara sahip AI üretimi kod, hiç kod olmamasından daha kötüdür
Daha iyi istemler, daha iyi sonuçlar:
Belirli, odaklanmış istemler faydalı, incelenebilir çıktı üretir
# Prompt to AI: "Write pytest tests for this function. Include: happy path, negative input, zero, empty list, and type error. Use descriptive test names." # Function: def calculate_average(numbers): if not numbers: raise ValueError("Cannot average empty list") return sum(numbers) / len(numbers)
İstem neyin test edileceği ve testlerin nasıl adlandırılacağı konusunda spesifik
Üretilen testleri hala inceleyip ayarlarsınız
# Prompt to AI: "Review this function as a senior engineer. Check for: - edge cases - naming clarity - error handling - testability - any potential bugs Be specific and suggest improvements."
Ekip arkadaşınız incelemeden önce AI'yı “ilk inceleyici” olarak kullanın
Bu, bariz sorunları yakalar ve inceleyicinin zamanından tasarruf sağlar
Geliştirme için faydalı AI araçları:
GitHub Copilot - satır içi kod önerileri
Claude Code (CLI) - terminal tabanlı AI asistanı
Cursor - AI tabanlı kod editörü
Codeium - ücretsiz AI otomatik tamamlama
İyi kullanım alanları:
Tekrarlayan kalıpları otomatik tamamlama
Docstring'ler üretme
Karmaşık kod bölümlerini açıklama
Test yapısı önerme
AI üretimi herhangi bir kodu birleştirmeden önce şunları yapmalısınız:
Anlayın - her satırı okuyun
Açıklayın - ne yaptığını ve neden yaptığını tarif edebilmelisiniz
Test edin - kendi testlerinizle doğrulayın
Onaylayın - PR'ınızda siz yazmışsınız gibi davranın
Birlikte gerçek bir PR'ı inceleyeceğiz ve şunları belirleyeceğiz:
• Kötü isimlendirme
• Eksik hata yönetimi
• Tekrarlanan mantık
• Eksik testler
• Belirsiz commit mesajları
• Kapsam genişlemesi
Bu demodan sonra şunları yapabilmelisiniz:
PR diff'ini eleştirel okumak
En az 5 tür sorun belirlemek
Yapıcı inceleme yorumları yazmak
Belirli iyileştirmeler önermek
Engelleyici sorunlar ile öneriler arasında ayrım yapmak
Görev:
GitHub'da açık kaynaklı bir PR bulun (veya eğitmen tarafından sağlananı kullanın)
En az 5 öz içerikli yorumla bir inceleme yazın
Her yorum belirli bir sorunu tanımlamalı ve bir iyileştirme önermelidir
Teslim gereksinimleri:
İncelediğiniz PR'ın bağlantısı
İnceleme yorumlarınız (ekran görüntüsü veya markdown)
Kısa bir yansıtma: başka birinin kodunu incelemekten ne öğrendiniz?
“Harika mühendisler sadece kodun çalışmasını sağlamaz. Kodu anlaşılır, test edilebilir, incelenebilir ve güvenilir yaparlar.”
“Bugün temizlemek için çok meşgul olduğunuz kod, yarın ekibinize sahip olan sorun haline gelir.”
Emre Varol · A2SV · Ruanda Üniversitesi