Konu Başlıkları Gizle
- 1 Giriş: Kod Yazmak Aslında Kitap Yazmaktır
- 2 Neden Temiz Kod Yazmak Bu Kadar Zor? (Becerebilen Var mı?)
- 3 Temiz Kodun Temel Taşları: KISS ve DRY
- 4 İsimlendirme: Kodun Sözlüğü
- 5 Fonksiyonlar: Tek İş, İyi İş
- 6 Yorum Satırları: Başarısızlığın İtirafı
- 7 SOLID Prensipleri
- 8 Süreç ve Kültür: Temiz Kod Bir Varış Noktası Değil
- 9 Sonuç
Selamlar arkadaşlar,
Biliyorsunuz daha önce yazılımla alakalı rehberler açmıştım. Şimdi işin özüne giriyoruz yani temiz kod nasıl yazılır, nasıl okunabilir bunları öğreneceğiz. Yazı uzun olacak okurken sıkılmazsınız umarım. Çayınızı kahvenizi alın başlıyoruz. Konuyu okumadan önce şu rehberlerime göz atmanızı öneririm;
Selamlar dostlar
Bugün sizlerle son birkaç aydır hayatımın resmen merkezine oturan, elimden düşüremediğim bir programlama dilinden bahsedeceğim. Go piyasada Golang diye de geçer. Bu rehberde size neden Go öğrenmeniz lazım, hangi alanlarda işinize yarar ve bu dil neden durup dururken bu kadar patladı, hepsini tek tek anlatacağım. Çayınızı kahvenizi alın, başlıyoruz.
Go Nedir ve Nerden Çıktı Bu?
Şimdi bu Go olayı 2009 yılında Google tarafından geliştirildi. Arkasındaki ekip şampiyonlar ligi gibi. Robert Griesemer, Rob Pike ve Ken Thompson. Bu adamlar C...Selam arkadaşlar,
Önce ki konuda bahsetmiştim, ilgi alanım Web. Kendi öğrenme sürecimi ve tecrübeleri sizinle paylaşayım dedim. Biliyorum, aranızda web işine girmek isteyip de "bra nereden başlayacağım ben?" diye kafası allak bullak olan bir sürü arkadaş var. Vallahi zamanında ben de o dönemlerden geçtim, o yüzden halinizden çok iyi anlıyorum. Bu konuyu bir seri haline getirmek istiyorum ilgi gelirse, gene rehber olarak veya blog üzerinden yayınlarım müsait oldukça. Foruma sadece makale paylaşmak için giriyorum zayen. Her neyse hazırsanız başlayalım demeden önce şu konumu...
Giriş: Kod Yazmak Aslında Kitap Yazmaktır
Evet. Robert C. Martin (nam-ı diğer Uncle Bob) abimizin şahane bir sözü var: "Kodun okunma sayısı, yazılma sayısından katbekat fazladır."
Bu basit gerçek aslında yazılım mesleğinin en büyük paradoksunu yüzümüze vuruyor. Biz kodu yazarken genelde "abi çalışsın yeter ya, proda atalım gitsin" kafasında oluyoruz. Ama o kod aylar sonra, belki bir hata çıktığında, belki yeni bir özellik ekleneceğinde karşımıza dikiliyor. İşte o zaman, vaktiyle yaptığımız her kestirme yol bize harcanan saatler olarak geri dönüyor.
Temiz kod dediğimiz şey sadece derleyicinin veya tarayıcının değil, insanların da yani benim, senin, rahatça anlayabileceği, geliştirebileceği koddur. Tıpkı iyi yazılmış bir roman gibi yukarıdan aşağıya su gibi akmalı. Okuyanı "burada ne demek istemiş şair?" diye düşündürmemeli, şaşırtmamalı.
Neden Temiz Kod Yazmak Bu Kadar Zor? (Becerebilen Var mı?)
Leonardo da Vinci reyiz ne demiş: "Basitlik, en üst düzey sofistikeliktir." Yazılım dünyasında bu lafın doğruluğunu her gün test ediyoruz.Kodu karmaşıklaştırmak dünyanın en kolay işi arkadaşlar. Her yeni istek geldiğinde basarsın
if-else, acil durumlarda yapıştırırsın patch, olur biter. Ama o karmaşıklığı alıp sadeleştirmek? İşte o uzmanlık ister.Temiz kodun önündeki en büyük engel ne biliyor musunuz? Zaman baskısı. Müşteri ensede... Kimse kodun kalitesine bakmaz, yetişti mi? diye sorar. Bu baskı altında yazılımcılar ne yapar? "Teknik Borç" biriktiririz. Aynen banka borcu gibi bu borç faiz işletir. Günün sonunda ya planlı bir şekilde refactoring yaparak ödersin, ya da sistem elinde patlar, iflas edersin.
Bir de şu "Çalışıyorsa dokunma" mantığı var. En tehlikeli kafa budur bana kalırsa. Bu kafa yapısı kodu zamanla çürütür. Kırık Camlar Teorisi'ni duydunuz mu? Bir mahallede kırık bir cam varsa ve tamir edilmezse, kısa sürede millet "burası zaten batık" der, diğer camları da indirmeye başlar. Kod tabanı da aynen böyledir. Bir yer kötü yazılmışsa, gelen diğer geliştiriciler de "aman zaten çöp burası" der, aynı kalitesizlikle devam eder.
Temiz Kodun Temel Taşları: KISS ve DRY
İki prensibimiz var:- KISS (Keep It Simple Stupid): Yani "Basit tut şapşal". Gereksiz atraksiyonlara girme. Bir problemi çözmenin en basit yolu, genelde en doğru yoldur.
- DRY (Don't Repeat Yourself): Yani "Kendini tekrar etme". Aynı kod bloğunu kopyala-yapıştır yapıyorsan dur. Onu bir fonksiyon haline getirmenin vakti gelmiştir.
İsimlendirme: Kodun Sözlüğü
Bana sorarsanız isimlendirme temiz kodun yarısıdır. Bir değişkenin veya fonksiyonun adı, onun ne halt yediğini anlatmalı.Bakın şu örneğe:
Kod:
# Kötü İsimlendirme (Bunu yapanı döverler)
d = 5
x = get_data()
temp = calculate()
# Temiz İsimlendirme (Oh be dünya var mış)
gun_sayisi = 5
musteri_listesi = get_active_customers()
toplam_fiyat = calculate_order_total()
Kod:
# Kötü Kullanım
if len(users) > 5:
raise Exception("Limit aşıldı")
# Temiz Kullanım
MAX_USER_LIMIT = 5
if len(users) > MAX_USER_LIMIT:
raise Exception("Kullanıcı limiti aşıldı")
get dediysen, başka yerde fetch deme. Okuyanın kafası yanar. Tüm projede aynı terimleri kullanın ki millet kodu daha hızlı öğrensin.Fonksiyonlar: Tek İş, İyi İş
Bir fonksiyon sadece tek bir işi yapmalı babacım onu da iyi yapmalı. Söylemesi kolay ama en çok ihlal ettiğimiz kural bu.Alın size çorba gibi bir fonksiyon:
Kod:
# Kötü Fonksiyon: Her işe burnunu sokuyor
def process_order(order):
# Validasyon (Doğrulama)
if not order.items:
raise ValueError("Sepet boş")
if not order.customer.address:
raise ValueError("Adres yok")
# Fiyat hesaplama
total = 0
for item in order.items:
total += item.price * item.quantity
if item.discount:
total -= item.discount
# Veritabanına kaydet
db = MySQLConnection()
db.connect()
db.execute("INSERT INTO orders...")
# Email gönder
smtp = SMTPClient()
smtp.send(order.customer.email, "Siparişiniz alındı")
# Fatura yazdır
printer = Printer()
printer.print(order.invoice)
return total
Kod:
# Temiz Fonksiyonlar: Herkes kendi işine bakıyor
def validate_order(order):
if not order.items:
raise ValueError("Sepet boş")
if not order.customer.address:
raise ValueError("Adres yok")
def calculate_order_total(order):
total = 0
for item in order.items:
total += item.price * item.quantity
total -= item.discount or 0
return total
def save_order(order, repository):
repository.save(order)
def notify_customer(order, notification_service):
notification_service.send_order_confirmation(order)
def process_order(order, repository, notification_service):
# Ana fonksiyon sadece orkestra şefi gibi diğerlerini yönetiyor
validate_order(order)
order.total = calculate_order_total(order)
save_order(order, repository)
notify_customer(order, notification_service)
return order
Kod:
# Kötü: Destan gibi parametre
def create_user(name, surname, email, phone, address, city, country, postal_code):
pass
# Temiz: Parametreler nesne oldu
class UserInfo:
def __init__(self, name, surname, email, phone, address):
self.name = name
self.surname = surname
self.email = email
self.phone = phone
self.address = address
def create_user(user_info):
pass
Yorum Satırları: Başarısızlığın İtirafı
Başlık sert geldi dimi? Ama gerçek bu. Eğer yazdığın kodu açıklamak için yorum yazmaya ihtiyaç duyuyorsan, kodun yeterince açık değildir hacı. Yorum yazacağına git kodu düzelt.
Kod:
# Kötü: Yorum ile durumu kurtarmaya çalışmak
# Kullanıcının 18 yaşından büyük olup olmadığını kontrol et
if u.a >= 18:
pass
# Temiz: Kod kendini anlatıyor
def is_adult(user):
LEGAL_AGE = 18
return user.age >= LEGAL_AGE
if is_adult(user):
pass
Kod:
# İyi Yorum: Sebebini açıklıyor
# KVKK gereği kullanıcı verileri 6 ay sonra anonimleştirilmeli
ANONYMIZATION_PERIOD_DAYS = 180
SOLID Prensipleri
SOLID, Nesne Yönelimli Programlama'nın 5 şartıdır. Yazılımın bakımını kolaylaştırır, genişletilebilir yapar.S - Single Responsibility Principle (Tek Sorumluluk Prensibi)
Bir sınıfın değişmesi için SADECE tek bir sebebi olmalı. Sınıfınız İsviçre Çakısı gibi her işi yapıyorsa orada durun.
Kod:
# HATALI KOD: Order sınıfı her şeyi yapıyor
class Order:
def __init__(self, items):
self.items = items
self.total = 0
def calculate_total(self):
for item in self.items:
self.total += item.price * item.quantity
return self.total
def save_to_database(self): # Veritabanı işi?
connection = MySQLConnection()
connection.connect()
connection.execute(f"INSERT INTO orders VALUES ({self.total})")
connection.close()
def print_invoice(self): # Yazıcı işi?
printer = Printer()
printer.initialize()
printer.print(f"Toplam: {self.total}")
def send_confirmation_email(self): # Mail işi?
smtp = SMTPClient("smtp.mail.com")
smtp.send("Siparişiniz alındı")
Kod:
# TEMİZ KOD: Her sınıfın tek sorumluluğu var
class Order:
def __init__(self, items):
self.items = items
self.total = 0
class OrderCalculator: # Sadece hesap yapar
def calculate_total(self, order):
total = 0
for item in order.items:
total += item.price * item.quantity
order.total = total
return total
class OrderRepository: # Sadece veritabanı işleri
def __init__(self, database_connection):
self.connection = database_connection
def save(self, order):
self.connection.execute(
"INSERT INTO orders (total) VALUES (?)", [order.total]
)
class OrderPrinter: # Sadece yazdırma
def __init__(self, printer):
self.printer = printer
def print_invoice(self, order):
self.printer.print(f"Toplam: {order.total}")
class OrderNotifier: # Sadece bildirim
def __init__(self, email_service):
self.email_service = email_service
def send_confirmation(self, order, customer_email):
self.email_service.send(
to=customer_email,
subject="Sipariş Onayı",
body=f"Siparişiniz alındı. Toplam: {order.total}"
)
O - Open/Closed Principle (Açık/Kapalı Prensibi)
Yazılım gelişime açık, değişime kapalı olmalı. Yani yeni özellik eklerken çalışan kodu bozmamalısın.
En yaygın hata indirim sistemlerinde yapılıyor. Yeni indirim gelince
if-else eklemek bu prensibi deler.
Kod:
# HATALI KOD: Her yeni indirimde burayı mıncıklamak lazım
class DiscountCalculator:
def calculate(self, customer_type, amount):
if customer_type == "ogrenci":
return amount * 0.20
elif customer_type == "emekli":
return amount * 0.15
elif customer_type == "ogretmen":
return amount * 0.10
elif customer_type == "doktor": # Yeni eklendi!
return amount * 0.12
elif customer_type == "asker": # Bu da yeni!
return amount * 0.18
else:
return 0
Kod:
# TEMİZ KOD: Yeni indirim için sadece yeni sınıf ekle
from abc import ABC, abstractmethod
class DiscountStrategy(ABC):
@abstractmethod
def calculate(self, amount):
pass
class StudentDiscount(DiscountStrategy):
def calculate(self, amount):
return amount * 0.20
class RetiredDiscount(DiscountStrategy):
def calculate(self, amount):
return amount * 0.15
# ... Diğer sınıflar ...
class DiscountCalculator:
def __init__(self, strategy: DiscountStrategy):
self.strategy = strategy
def calculate(self, amount):
return self.strategy.calculate(amount)
# Kullanım
student_calculator = DiscountCalculator(StudentDiscount())
discount = student_calculator.calculate(100) # 20 döner
# Yeni indirim türü eklemek için ana koda dokunmadan:
class MilitaryDiscount(DiscountStrategy):
def calculate(self, amount):
return amount * 0.18
L - Liskov Substitution Principle (Liskov'un Yerine Geçme Prensibi)
Alt sınıflar, üst sınıfların yerine geçebilmeli. Program sapıtmamalı. Barbara Liskov ablamızın prensibi.
Kuş örneği klasiktir.
Kod:
# HATALI KOD: Tavuk uçamaz ama Bird sınıfından türüyor
class Bird:
def __init__(self, name):
self.name = name
def fly(self):
return f"{self.name} uçuyor!"
def eat(self):
return f"{self.name} yemek yiyor."
class Eagle(Bird):
def fly(self):
return f"{self.name} yükseklerde süzülüyor!"
class Chicken(Bird):
def fly(self):
# Tavuk uçamaz! Ne yapacağız?
raise Exception("Tavuklar uçamaz!")
# Kullanım
def make_bird_fly(bird: Bird):
print(bird.fly())
eagle = Eagle("Kartal")
chicken = Chicken("Tavuk")
make_bird_fly(eagle) # Çalışır
make_bird_fly(chicken) # PATLADI! Liskov ihlali!
Kod:
# TEMİZ KOD: Yetenekler arayüzlere bölündü
from abc import ABC, abstractmethod
class Bird(ABC):
# Temel özellikler
def __init__(self, name):
self.name = name
@abstractmethod
def eat(self): pass
class Flyable(ABC): # Uçabilme yeteneği
@abstractmethod
def fly(self): pass
class Swimmable(ABC): # Yüzebilme yeteneği
@abstractmethod
def swim(self): pass
class Eagle(Bird, Flyable):
def eat(self): return "Et yiyor"
def fly(self): return "Uçuyor"
class Chicken(Bird):
def eat(self): return "Yem yiyor"
# Tavuk uçamaz, Flyable değil!
class Duck(Bird, Flyable, Swimmable):
def eat(self): return "Ekmek yiyor"
def fly(self): return "Uçuyor"
def swim(self): return "Yüzüyor"
# Kullanım
def make_it_fly(flyable: Flyable):
print(flyable.fly())
eagle = Eagle("Kartal")
# make_it_fly(chicken) # Derleme hatası verir, kod patlamaz.
Müşterileri kullanmadıkları metotlara zorlamayın. Şişman interfaceler yerine küçük, odaklı interfaceler yapın.
Kod:
# HATALI KOD: Tek ve şişman bir arayüz
from abc import ABC, abstractmethod
class IOrderService(ABC):
@abstractmethod
def calculate_total(self): pass
@abstractmethod
def process_online_payment(self): pass
@abstractmethod
def process_in_store_payment(self): pass # Mağaza ödemesi
class OnlineOrder(IOrderService):
def calculate_total(self): return 100
def process_online_payment(self): pass
def process_in_store_payment(self):
# Online siparişte mağaza ödemesi olmaz!
raise NotImplementedError("Gereksiz metod!")
process_in_store_payment metodunu implemente etmek zorunda kalıyor. Çözüm parçalamak:
Kod:
# TEMİZ KOD: Odaklı arayüzler
class ICalculatable(ABC):
@abstractmethod
def calculate_total(self): pass
class IOnlinePayable(ABC):
@abstractmethod
def process_online_payment(self): pass
class IInStorePayable(ABC):
@abstractmethod
def process_in_store_payment(self): pass
class OnlineOrder(ICalculatable, IOnlinePayable):
# Sadece ihtiyacı olanları alır
def calculate_total(self): return 100
def process_online_payment(self): print("Online ödeme")
class InStoreOrder(ICalculatable, IInStorePayable):
# Sadece ihtiyacı olanları alır
def calculate_total(self): return 100
def process_in_store_payment(self): print("Nakit ödeme")
Yüksek seviyeli modüller (iş mantığı), düşük seviyeli modüllere (veritabanı vs.) doğrudan bağımlı olmamalı. İkisi de soyutlamaya (interface) bağımlı olmalı.
Kod:
# HATALI KOD: Doğrudan bağımlılık
class MySQLDatabase:
def connect(self): print("MySQL bağlandı")
def execute(self, query): print(f"Sorgu: {query}")
class OrderManager:
def __init__(self):
# Doğrudan MySQL'e bağımlı!
self.database = MySQLDatabase()
def save_order(self, order):
self.database.connect()
self.database.execute(...)
OrderManager sınıfını değiştirmek zorundasın.
Kod:
# TEMİZ KOD: Soyutlamaya bağımlılık
from abc import ABC, abstractmethod
class IDatabase(ABC): # Soyutlama
@abstractmethod
def connect(self): pass
@abstractmethod
def execute(self, query): pass
class MySQLDatabase(IDatabase): # Somut sınıf
def connect(self): print("MySQL")
def execute(self, query): print("Sorgu")
class PostgreSQLDatabase(IDatabase): # Somut sınıf
def connect(self): print("Postgre")
def execute(self, query): print("Sorgu")
class OrderManager:
def __init__(self, database: IDatabase):
# Hangi DB geldiği umurumda değil, IDatabase olsun yeter
self.database = database
def save_order(self, order):
self.database.connect()
self.database.execute(...)
# Kullanım - Dependency Injection
mysql_db = MySQLDatabase()
manager = OrderManager(mysql_db) # MySQL ile çalışır
postgres_db = PostgreSQLDatabase()
manager = OrderManager(postgres_db) # Kod değişmeden Postgre ile çalışır
Süreç ve Kültür: Temiz Kod Bir Varış Noktası Değil
Arkadaşlar temiz kod bir kere yazıp bitirilen bir iş değil, sürekli bir süreçtir. Bu süreci ayakta tutan 3 şey var:- Refactoring (Yeniden Düzenleme): Kodu yazdın bitti mi? Hayır. Davranışını değiştirmeden yapısını iyileştireceksin. "Çalışan koda dokunma" dersen kaybedersin. Geri dönüp "Bunu nasıl sadeleştiririm?" diye kafa yorman lazım.
- Code Review (Kod İnceleme): Başkasının anlamadığı kod kirli koddur, net. Kod sadece yazan değil, ekip arkadaşları tarafından da okunmalı. Hem bilgi paylaşılır hem standartlar korunur.
- Pair Programming (Eşli Programlama): İki geliştirici tek bilgisayar. Biri yazar, diğeri anlık denetler. Bilgi paylaşımını zirveye çıkarır.
Sonuç
Temiz kod yazmak yetenek değil, disiplindir. SOLID prensipleri bu işin kurallarıdır.Single Responsibility ile odaklan, Open/Closed ile koru, Liskov ile mirası düzgün kullan, Interface Segregation ile böl, Dependency Inversion ile esnek ol.
Bunları bilmek yetmez, uygulamak lazım. Sürekli pratik, code review ve refactoring şart. Temiz kod lüks değil, yazılımın sağlığı için zorunluluktur.
Umarım faydalı olmuştur dostlar. Elimde birçok kaynak kitap var telif hakkı sorun olur diye paylaşamıyorum. Yöneticilerden dönüş alırsak konuya eklerim. İyi sosyaller herkese!