- Şelale ve çevik modelleri karşılaştırmak, Scrum rollerini ve etkinliklerini açıklamak
- Git'te dallarla çalışmak ve bir değişikliği güvenle geri almak
- Test düzeylerini seçmek ve döngüsel karmaşıklığı hesaplamak
- Kodda SOLID ilkelerinin ihlallerini fark etmek
Tek başına 200 satırlık bir betik yazmak programlamadır; 50 kişilik bir ekibin yıllarca milyonlarca satır kod geliştirmesi, test etmesi ve hiçbir şeyi bozmadan her hafta güncelleme yayımlaması ise yazılım mühendisliğidir. Buradaki asıl zorluk algoritmalar değil; karmaşıklık, değişen gereksinimler ve insanlar arası iletişimdir. Bu derste profesyonel ekiplerin bu sorunları nasıl yönettiğini öğreneceğiz.
Yaşam döngüsü: şelaleden çevik yöntemlere
- 1Gereksinimler
Sistem ne yapmalı (işlevsel) ve ne kadar iyi yapmalı (hız, güvenlik — işlevsel olmayan)?
- 2Tasarım
Mimari, modüller, veri tabanı, arayüzler.
- 3Gerçekleştirme
Kod yazma, kod incelemesi, sürüm denetimi.
- 4Test
Birim, entegrasyon, sistem ve kabul testleri.
- 5Dağıtım ve bakım
Yayın, izleme, hata düzeltme ve yeni gereksinimler; bir sistemin ömrünün en uzun kısmı.
| Ölçüt | Şelale (waterfall) | Çevik (agile) |
|---|---|---|
| aşamalar | sıralı, bir kez | kısa yinelemelerde tekrarlanır |
| gereksinim değişikliği | pahalı | beklenir ve kabul edilir |
| müşteri çalışan yazılımı görür | sonunda | her 1–4 haftada bir |
| uygun olduğu yer | sabit gereksinimler, sıkı düzenleme (tıp, havacılık) | belirsiz pazar, web ve mobil ürünler |
2001 tarihli Çevik Manifesto, çalışan yazılımı, müşteriyle iş birliğini ve değişime hazır olmayı öne çıkarır. En popüler çevik çerçeve Scrum'dır. Üç rolü vardır: Product Owner (ürün sahibi; Product Backlog'u ve öncelikleri yönetir), Scrum Master (sürece yardım eder, engelleri kaldırır) ve Developers (işi yapan ekip). İş sprintlerle, yani en fazla bir ay süren sabit dönemlerle yürür: Sprint Planning, her gün 15 dakikalık Daily Scrum, sonunda Sprint Review (sonucun gösterilmesi) ve Retrospective (sürecin iyileştirilmesi).
Product Backlog'da 150 hikâye puanı kaldı. Son üç sprintte ekip 16, 20 ve 18 puan tamamladı. Bir sprint 2 hafta sürüyor. Sürüme yaklaşık ne kadar kaldı?
Çözümü gösterÇözümü gizle
Sprint sayısı: ⌈150 / 18⌉ = ⌈8,33⌉ = 9 sprint.
Süre: 9 · 2 = 18 hafta (yaklaşık 4 ay).
Bu bir söz değil, tahmindir: her sprintten sonra güncellenir. Hız ekipler arasında karşılaştırılmamalıdır; puanlar her ekibin kendi ölçeğidir.
- Cekipteki ikili iletişim kanalı sayısı
- nekip üyesi sayısı
5 kişi → 10 kanal, 10 kişi → 45 kanal. Brooks yasası bununla açıklanır: geciken bir projeye insan eklemek onu çoğu zaman daha da geciktirir. Bu yüzden Scrum ekipleri küçük tutulur (genellikle 10 kişiye kadar).
Git ile sürüm denetimi
Git dağıtık bir sürüm denetim sistemidir: her geliştiricinin bilgisayarında tüm geçmişi içeren eksiksiz bir depo bulunur. Commit, projenin bir “anlık görüntüsüdür” (değişiklikler, yazar, zaman, açıklama); commit'ler bir zincir oluşturur ve her biri bir hash ile adlandırılır. Dal (branch), bir commit'e işaret eden hareketli bir işaretçidir: yeni bir özelliği ayrı bir dalda yapar, hazır olunca pull request ile incelemeye gönderir ve ana dala birleştirirsin (merge).
git switch -c feature/login # create and switch to a new branch
git add login.py
git commit -m 'Validate the login form'
git push -u origin feature/login # then open a pull request
git switch main
git pull # get the reviewed and merged work
git revert 3f2a9c1 # safely undo a bad pushed commitgit revert, hatalı bir commit'i geri alan yeni bir commit oluşturur; geçmiş yeniden yazılmaz, bu yüzden ortak dallarda güvenlidir.Test
| Düzey | Neyi denetler | Hız ve sayı |
|---|---|---|
| Birim (unit) | tek bir fonksiyon ya da sınıf, yalıtılmış | milisaniyeler, binlerce |
| Entegrasyon | modüllerin birlikte çalışması (kod + veri tabanı, API) | saniyeler, yüzlerce |
| Sistem (uçtan uca) | tüm sistem, bir kullanıcı gibi | dakikalar, onlarca |
| Kabul | müşteri gereksinimlerine uygunluk | yayından önce |
def is_leap(year):
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
def buggy_is_leap(year):
return year % 4 == 0
cases = {2024: True, 2023: False, 1900: False, 2000: True}
def run_tests(func):
failed = [y for y, expected in cases.items() if func(y) != expected]
return 'all passed' if not failed else 'FAILED for ' + str(failed)
print('is_leap:', run_tests(is_leap))
print('buggy_is_leap:', run_tests(buggy_is_leap))▸ Beklenen çıktı
is_leap: all passed buggy_is_leap: FAILED for [1900]
pytest ya da unittest ile otomatikleştirilir ve her commit'te bir CI sunucusunda çalışır.- MMcCabe'in döngüsel karmaşıklığı: bağımsız yol sayısı
- E, Ndenetim akışı grafının kenarları ve düğümleri
- Pbağlı bileşen sayısı (tek fonksiyon için 1)
M, tüm dalları kapsamak için gereken en az test sayısının iyi bir tahminidir. M > 10 olan fonksiyonlar genellikle daha küçük parçalara bölünür.
is_leap, A and (B or C) döndürür; burada A = “4'e bölünür”, B = “100'e bölünmez”, C = “400'e bölünür”. Döngüsel karmaşıklığı nedir ve testlerimizde neden 4 durum var?
Çözümü gösterÇözümü gizle
and, or) bir karar noktasıdır: 2 karar → M = 2 + 1 = 3.3 temel yol: A yanlış → 2023; A doğru, B doğru → 2024; A doğru, B yanlış → sonuç C'nin kendisidir.
Üçüncü yol C'yi döndürdüğü için iki değerini de sınarız: 1900 (C yanlış) ve 2000 (C doğru).
Yani M = 3 yol kapsamı için en azdır; 4 test ise tam koşul kapsamı sağlar; bu tam olarak
cases sözlüğümüzdür.SOLID ve kod incelemesi
| İlke | Anlamı |
|---|---|
| S — tek sorumluluk | bir sınıfın değişmek için tek bir nedeni olmalı |
| O — açık/kapalı | genişlemeye açık, değişikliğe kapalı: yeni davranışı yeni kodla ekle |
| L — Liskov yerine geçme | alt sınıf nesnesi, taban sınıfın beklendiği her yerde çalışmalı |
| I — arayüz ayrımı | tek büyük arayüz yerine birkaç küçük, özel arayüz |
| D — bağımlılığın tersine çevrilmesi | somut sınıflara değil, soyutlamalara bağlı ol |
from abc import ABC, abstractmethod
class Discount(ABC):
@abstractmethod
def apply(self, price): ...
class NoDiscount(Discount):
def apply(self, price):
return price
class Percent(Discount):
def __init__(self, p):
self.p = p
def apply(self, price):
return round(price * (1 - self.p / 100), 2)
class StudentCard(Discount):
def apply(self, price):
return max(price - 5, 0)
def checkout(price, discount: Discount):
return discount.apply(price)
for d in [NoDiscount(), Percent(20), StudentCard()]:
print(type(d).__name__, checkout(50, d))▸ Beklenen çıktı
NoDiscount 50 Percent 40.0 StudentCard 45
checkout somut indirimlere değil, Discount soyutlamasına bağlıdır (D). Yeni bir indirim türü eklemek için checkout'u değiştirmek gerekmez; yeni bir sınıf yeter (O). Her alt sınıf, Discount beklenen her yerde çalışır (L).Kod incelemesi (code review), bir değişikliğin ana dala girmeden önce başka bir geliştirici tarafından denetlenmesidir. İnceleyen kişi doğruluğa, testlere, okunabilirliğe, güvenliğe ve tasarıma bakar; biçim ise otomatik araçlarla (linter, biçimlendirici) denetlenmelidir. İnceleme hataları yakalamanın yanı sıra bilgiyi ekibe yayar: sistemin her parçasını en az iki kişi bilir. Küçük pull request'ler (birkaç yüz satırdan az) daha dikkatli okunur.
Önemli noktalar
- Yaşam döngüsü: gereksinimler → tasarım → gerçekleştirme → test → dağıtım ve bakım; çevik yöntemler onu kısa yinelemelerle tekrarlar.
- Scrum: Product Owner, Scrum Master, Developers; bir aya kadar sprintler ve dört etkinlik.
- Git: commit bir anlık görüntü, dal bir işaretçidir; ortak dalda geçmişi yeniden yazma,
revertkullan. - Test piramidi: çok sayıda birim, az sayıda sistem testi; döngüsel karmaşıklık M = kararlar + 1.
- SOLID değişime dayanıklı tasarım sağlar; kod incelemesi hataları yakalar ve bilgiyi yayar; ekip kanalları n(n − 1)/2 büyür.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.