"Kod yalnızca makine için değil, onu okuyacak diğer insanlar için de yazılır."
Bu rehber, Python dilinde temiz, okunabilir ve sürdürülebilir kod yazma alışkanlıklarını geliştirmek amacıyla hazırlanmıştır. Amaç; kodu sadece çalışan bir yapı değil, aynı zamanda ekip içinde anlaşılır, bakımı kolay ve hataya daha az açık hale getirmektir.
Python geliştiricileri için geçerli olan temel prensipleri, örneklerle birlikte sunar. Her bölüm, bir konuyu ele alır ve iyi/kötü uygulama örnekleriyle desteklenir. Geliştiricilerin hem bireysel projelerde hem de ekip çalışmalarında daha temiz kod yazmaları hedeflenmiştir.
📌 Versiyon: v1.1.0 – Son güncelleme: 11 Mayıs 2025
🔗 CHANGELOG.md dosyasından tüm sürüm geçmişine ulaşabilirsiniz.
- 1. Giriş ve Temel Felsefe
- 2. Anlamli Degisken Isimleri
- 3. Fonksiyonlar ve Tek Sorumluluk
- 4. Kod Tekrarından Kaçınmak (DRY)
- 5. Yorumlar ve Kodun Anlatımı
- 6. Exception Handling (Hata Yönetimi)
- 7. Kod Biçimlendirme ve Linter Kullanımı
- 8. Kötü Pratikler (Anti-Patterns)
- 9. OOP ve Kompozisyon Prensipleri
- 10. Pythonic Kod Yazımı
- 11. Sihirli Sayılardan (Magic Numbers) Kaçının
- 12. Modülerlik ve Sınıf Yapıları (Modularity and Classes)
Python dili, belirli bir yazım felsefesine dayanır. Bu felsefe, yalnızca çalışır kod değil, okunabilir, anlaşılabilir ve sürdürülebilir kod üretmeyi öncelikli hale getirir. Bu yaklaşıma "The Zen of Python" adı verilir ve Python içinde doğrudan erişilebilir durumdadır:
>>> import this
Zen of Python'dan bazı temel ilkeler:
- Güzel olan çirkinden iyidir.
- Açık olan örtük olandan iyidir.
- Basit olan karmaşıktan iyidir.
- Karmaşık olan anlaşılmazdan iyidir.
- Okunabilirlik önemlidir.
Bu ilkeler, Python ile geliştirme yaparken sadece dilin sözdizimine değil, aynı zamanda yazım tarzına da dikkat etmeniz gerektiğini ifade eder.
Clean Code yaklaşımı yalnızca Python'a özel değildir; evrensel bir yazılım mühendisliği disiplinidir. Ancak Python’un sadeliğe ve açıklığa verdiği önem nedeniyle bu yaklaşım dil ile doğrudan örtüşür.
Bu bölümün amacı, "neden temiz kod yazmalıyız?" sorusuna net bir cevap vermektir:
- Takım çalışmasında başkalarının sizin kodunuzu anlayabilmesi için,
- Gelecekte kendi kodunuzu tekrar okuduğunuzda zorluk yaşamamak için,
- Hataları azaltmak ve test edilebilirliği artırmak için,
- Yeni özellikleri kolayca ekleyebilmek için.
Bu rehberdeki her başlık, Python kodunu daha temiz, anlaşılır ve bakımı kolay hale getirmek için temel yapı taşlarını sunacaktır.
Kodun en temel yapı taşlarından biri olan değişkenler, doğru isimlendirilmediği sürece kodun anlaşılabilirliğini doğrudan olumsuz etkiler. Değişken isimleri, değişkenin neyi temsil ettiğini açıkça göstermelidir. Kısa ama anlamsız, ya da çok uzun ama belirsiz isimlerden kaçınılmalıdır.
- Anlamlı, açıklayıcı ve bağlamı ifade eden isimler kullanın.
- Tek harfli değişken isimlerini yalnızca döngü sayaçlarında kullanın (
i
,j
gibi). - Boole değişkenlerini
is_
,has_
,can_
gibi öneklerle tanımlayın. - Kısaltmalardan kaçının, gerekirse açık yazın (
temp_user_id
>tmp_id
). - Negatif anlamlı isimlerden kaçının (
is_not_empty
yerineis_empty == False
daha açık olabilir).
d = 100
t = 20
tp = d + t
product_price = 100
tax_amount = 20
total_price = product_price + tax_amount
x
,y
,z
,data1
,temp2
gibi bağlamsız isimler yalnızca kısa süreli değişkenler için kullanılmalıdır.data
,value
,info
,result
gibi aşırı genel isimler yerine daha belirleyici terimler tercih edilmelidir.
Geliştiricinin kodu yorum satırı olmadan bile anlayabilmesi gerekir. Anlamlı değişken ismi, çoğu zaman yoruma gerek bırakmaz.
Fonksiyonlar, kodun temel yapı taşlarındandır. Temiz kod yazımında bir fonksiyonun yalnızca tek bir sorumluluğu olması gerekir. Bu prensibe Single Responsibility Principle (SRP) denir.
Fonksiyonlar kısa, anlaşılır ve test edilebilir olmalıdır. Çok fazla iş yapan, uzun ve iç içe geçmiş fonksiyonlar hem okunabilirliği düşürür hem de bakım maliyetini artırır.
- Her fonksiyon yalnızca bir iş yapmalıdır.
- Fonksiyon isimleri açık, fiil temelli ve yaptığı işle uyumlu olmalıdır.
- Girdi sayısı mümkün olduğunca azaltılmalıdır (tercihen 3 veya daha az).
- Fonksiyon içindeki kod blokları, başka bir fonksiyonla kolayca ayrıştırılabilecek yapıdaysa bölünmelidir.
def process_user(user):
# validate user
if not user.get("email"):
raise ValueError("No email")
# save to db
db.save(user)
# send email
send_welcome_email(user["email"])
Bu fonksiyon birden fazla işi aynı anda yapıyor: doğrulama, veri kaydı ve bildirim. Bu, test yazmayı zorlaştırır ve hata ayıklamayı güçleştirir.
def validate_user(user):
if not user.get("email"):
raise ValueError("No email")
def save_user(user):
db.save(user)
def notify_user(email):
send_welcome_email(email)
def process_user(user):
validate_user(user)
save_user(user)
notify_user(user["email"])
Her bir işlem ayrı fonksiyon haline getirilerek kod hem okunur hem de yeniden kullanılabilir hale gelir. Bu yaklaşım hata ayıklamayı da kolaylaştırır.
- Test yazımı açısından, tek sorumluluk prensibine uygun fonksiyonlar daha kolay test edilir.
- Büyük fonksiyonlar zamanla daha da büyür; küçük fonksiyonlar ise bölünebilir, sadeleştirilebilir.
- Fonksiyon içinde
ve
,veya
,ama
,hem...hem de
gibi ifadeler zihinsel karmaşıklığa yol açıyorsa, fonksiyon bölünmelidir.
Kod tekrarından kaçınmak yazılım geliştirmede temel bir ilkedir. Bu ilke genellikle "Don't Repeat Yourself (DRY)" şeklinde ifade edilir. Aynı kod veya mantık birden fazla yerde tekrarlandığında, bakım maliyeti ve hata riski artar.
- Aynı kod blokları birden fazla yerde kullanılıyorsa fonksiyon haline getirin.
- Tekrar eden verileri sabit (constant) olarak tanımlayın.
- Ortak işlem akışları için yardımcı modüller oluşturun.
- Magic number (sihirli sayı) kullanımından kaçının.
print("User created successfully.")
# ...
print("User created successfully.")
Aynı metin iki kez tekrar ediyor. Bu durumda bir sabit ya da fonksiyon kullanmak daha doğrudur.
SUCCESS_MESSAGE = "User created successfully."
print(SUCCESS_MESSAGE)
# ...
print(SUCCESS_MESSAGE)
Ya da:
def notify_user_created():
print("User created successfully.")
notify_user_created()
# ...
notify_user_created()
- Bir değişiklik gerektiğinde tek bir noktadan güncelleme yapılır.
- Hatalı kopyalama, unutulan güncellemeler gibi riskler azalır.
- Kod daha sade ve anlaşılır hale gelir.
DRY prensibini uygularken aşırı soyutlamadan kaçının. Her benzerlik soyutlama gerektirmez. Anlamlı tekrarlar bazen okunabilirliği artırabilir. DRY ile birlikte KISS (Keep It Simple, Stupid) prensibi de göz önünde bulundurulmalıdır.
Yorumlar (comments), kodun anlaşılmasını kolaylaştırabilir; ancak kötü yazılmış yorumlar kafa karıştırabilir veya gereksiz yere kodu şişirebilir. Temiz kodda amaç, mümkün olan her durumda yorum ihtiyacını ortadan kaldıracak kadar açık ve okunabilir kod yazmaktır.
- Kod kendi kendini açıklamalıdır, yorum ihtiyacı en aza indirgenmelidir.
- Ne yapıldığını değil, neden yapıldığını açıklayan yorumlar yazılmalıdır.
- Güncel olmayan veya yanıltıcı yorumlardan kaçınılmalıdır.
- Açıklayıcı değişken ve fonksiyon isimleri tercih edilmelidir.
# kullanıcıyı siler
def delete(u):
db.delete(u)
Buradaki yorum, fonksiyonun ne yaptığını tekrar etmektedir. Bu hem gereksiz hem de düşük kaliteli bir yorumdur.
def delete_user(user):
db.delete(user)
Yoruma gerek kalmadan fonksiyonun amacı açıktır. Fonksiyon adı yeterince açıklayıcıdır.
- Karmaşık bir algoritmanın neden bu şekilde yazıldığını açıklamak.
- Geçici veya bilinçli bir teknik borç (technical debt) alanını belirtmek.
- Belirli sistemsel sınırlamalar veya dış API kısıtlamalarını belgelemek.
- TODO, FIXME, HACK gibi işaretlerle gelecekte yapılması gerekenleri belirtmek.
- Yorumlar kodla birlikte güncellenmelidir.
- Kodun ne yaptığını anlatmak yerine, geliştiriciye ek bağlam sağlamalıdır.
- Yorumlar profesyonel ve tarafsız bir dil ile yazılmalıdır.
Yorumlara bağımlı olmak yerine, okunabilirliği yüksek kod yazmak daha sürdürülebilir bir yaklaşımdır. Yorum, kodun yetersizliğini telafi eden bir araç değil, destekleyici bir açıklamadır.
Hata yönetimi, uygulamanın kararlı ve güvenli çalışması açısından kritik öneme sahiptir. Python'da try/except
bloklarıyla istisnalar (exceptions) kontrol altına alınabilir. Temiz kod yaklaşımında, sadece beklenen ve yönetilebilir hatalar yakalanmalı, hatalar sessizce yutulmamalıdır.
- Sadece beklenen ve anlamlı hataları yakalayın.
- Hata mesajları açık ve anlaşılır olmalı.
except Exception:
gibi geniş kapsamlı yakalamalardan kaçının.- Her
try
bloğu yalnızca gerekli kodu kapsamalı. - Gerekirse özel exception sınıfları oluşturun.
try:
user = db.get_user(id)
user.do_something()
except:
pass
Bu örnekte tüm hatalar yakalanıp hiçbir işlem yapılmıyor. Bu, hata ayıklamayı imkânsız hale getirir ve sistemin sessizce bozulmasına neden olur.
try:
user = db.get_user(id)
user.do_something()
except DatabaseError as e:
logger.error(f"Database error: {e}")
raise
Hata yalnızca belirli bir durumda yakalanır, loglanır ve tekrar fırlatılır. Böylece hem takip edilebilir olur hem de sistem davranışı kontrol altında kalır.
class InvalidUserInput(Exception):
pass
def process_input(data):
if not isinstance(data, dict):
raise InvalidUserInput("Input must be a dictionary.")
Bu yaklaşım, uygulamanın belirli hatalarını ayırmak ve anlamlı hale getirmek için idealdir.
- Hataları gizlemek, hata yönetimi değildir.
finally:
bloğu her durumda çalışır, kaynak temizliği için kullanılabilir.- Sık kullanılan hatalar için
ValueError
,TypeError
gibi standart sınıflar tercih edilmelidir.
Kodun biçimi, okunabilirliği ve sürdürülebilirliği doğrudan etkiler. Biçimsel tutarsızlıklar zamanla kodun anlaşılmasını zorlaştırır ve ekip içi iş birliğini engeller. Python için PEP8 standardı, kod stilinin nasıl olması gerektiğini tanımlar.
- Kodunuzu otomatik olarak biçimlendirmek için araçlar kullanın (örneğin
black
). - Gereksiz boşluklardan ve hizalama hatalarından kaçının.
- Satır uzunluğu genellikle 79–88 karakteri geçmemelidir.
- Değişken, sınıf ve fonksiyon adlarında tutarlı isimlendirme kuralları izlenmelidir.
- Black → Otomatik kod biçimlendirici.
- isort →
import
sıralamalarını düzenler. - Flake8 → PEP8 hatalarını kontrol eder.
- Pylint → Kod kalitesi, hata analizi ve öneriler sağlar.
- mypy → Tip kontrolü yapar (optional typing desteği).
def getuser( id ):
return db.get( id )
def get_user(id: int) -> User:
return db.get(id)
# Flake8 ile kodu kontrol et
flake8 your_file.py
# Black ile otomatik düzeltme yap
black your_file.py
# isort ile import sıralarını düzelt
isort your_file.py
Statik tip kullanımı, büyük projelerde hataları erken yakalamaya yardımcı olur.
def add(a: int, b: int) -> int:
return a + b
mypy ile kontrol etmek:
mypy your_file.py
Biçimsel kurallar zamanla değişebilir, ancak proje genelinde tutarlılık en önemli kriterdir. Linter kurallarının CI/CD sürecine dahil edilmesi önerilir.
Anti-pattern'ler, kısa vadede işe yarıyor gibi görünen ancak uzun vadede bakımı zorlaştıran, hataya açık ve yanlış yaklaşımlardır. Kod kalitesini düşüren bu alışkanlıklar fark edilip düzeltilmediği sürece projelerde teknik borç birikir.
def create_user(user):
db.insert(user)
logger.info("User created")
def create_admin(admin):
db.insert(admin)
logger.info("User created")
Yerine:
def create_entity(entity):
db.insert(entity)
logger.info("Entity created")
counter = 0
def increment():
global counter
counter += 1
Yerine sınıf bazlı yaklaşım tercih edilmelidir.
i = 0 # sayaç
Yorum yerine daha iyi değişken adı seçilmeli:
counter = 0
def do():
x = 1
return x
Bu tür yapılar gereksizdir ve kod karmaşıklığını artırır.
try:
risky_operation()
except:
pass
Bu yaklaşım hatayı saklar ve ciddi sorunlara neden olabilir.
- Kodda "iş görsün yeter" yaklaşımından kaçının.
- Kod okunabilirliği, kısa yazmaktan daha önemlidir.
- Anlamlı yapılar oluşturmak, koda değer katar.
Anti-pattern’ler zamanla alışkanlığa dönüşebilir. Bu nedenle düzenli kod gözden geçirmeleri (code review) yapılmalı, ekip içinde bu pratikler belirlenip belgelenmelidir.
Nesne yönelimli programlama (OOP), Python projelerinde sık kullanılan bir yaklaşımdır. Ancak OOP kullanımı doğru uygulanmadığında kodu karmaşıklaştırabilir. Bu nedenle sınıf tasarımı, kalıtım (inheritance) ve kompozisyon (composition) gibi kavramların bilinçli şekilde kullanılması önemlidir.
- Her sınıf tek bir sorumluluğa sahip olmalıdır (SRP).
- Kalıtım yerine kompozisyon tercih edilmelidir.
- Gereksiz soyutlamalardan kaçınılmalıdır.
- Sınıflar birbiriyle gevşek bağlı (loosely coupled) olmalıdır.
Kalıtım, bir üst sınıfın işlevselliğini alt sınıflara aktarmak için kullanılır. Ancak her durumda tercih edilmemelidir.
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Bark"
Bu yapılar mantıklı soyutlamalar varsa kullanılabilir. Ancak sık güncellenen yapılar için risklidir.
Kompozisyon, bir sınıfın başka sınıfları özellik olarak barındırmasıdır. Daha esnek ve test edilebilir yapı sağlar.
class Engine:
def start(self):
print("Engine started")
class Car:
def __init__(self):
self.engine = Engine()
def drive(self):
self.engine.start()
print("Driving")
class Database:
pass
class MyDatabase(Database):
pass
Burada alt sınıfın özel bir katkısı yoksa, gereksiz kalıtım yapılmış olur.
class Logger:
def log(self, message):
print(message)
class Service:
def __init__(self, logger: Logger):
self.logger = logger
def run(self):
self.logger.log("Service running")
Python’da @dataclass
, abc
(abstract base class) gibi yapılar da OOP projelerinde kodu sadeleştirmek için kullanılabilir. Ancak her yapı, amacına uygun ve gerekliyse tercih edilmelidir.
Pythonic kod, dilin sunduğu özellikleri ve felsefesini en verimli, okunabilir ve sade biçimde kullanan koddur. Bu tarz kod yazımı, hem Python’a özgü söz dizimini hem de topluluk tarafından benimsenmiş en iyi uygulamaları içerir.
- Sadelik, açık ifade ve okunabilirlik ön plandadır.
- Yerleşik fonksiyonlar ve dil yapıları tercih edilir.
- Gereksiz tekrar, karmaşık yapılar ve "Java tarzı" sınıf kullanımlarından kaçınılır.
- List comprehension, unpacking, context manager gibi yapılar aktif olarak kullanılır.
numbers = [1, 2, 3, 4, 5]
squares = []
for n in numbers:
squares.append(n * n)
squares = [n * n for n in [1, 2, 3, 4, 5]]
- List Comprehension
- Enumerate & Zip
- Unpacking (tuple/list)
- Context Manager (
with
kullanımı) - Ternary Operator
- Walrus Operator (
:=
) - F-string ile formatlama
- Any / All kullanımı
for index, item in enumerate(items):
print(index, item)
for name, score in zip(names, scores):
print(f"{name}: {score}")
with open("file.txt") as f:
data = f.read()
name = "John"
print(f"Hello, {name}!")
Pythonic düşünce tarzı sadece sözdizimi ile ilgili değildir. Aynı zamanda “işi en anlaşılır ve sade şekilde yapma” zihniyetini de içerir. Bu bakış açısı, temiz kodun doğal bir parçasıdır.
Sihirli sayılar (magic numbers), kod içerisinde doğrudan kullanılan, anlamı belirsiz sabit sayılardır. Bu tür değerler, kodun okunabilirliğini ve sürdürülebilirliğini zorlaştırır.
Kodda geçen bir sayının neyi temsil ettiğini anlamak için ek bağlam gerekmesi, kodu okuyan geliştiriciler için zaman kaybına ve hata riskine yol açar.
def calculate_discount(price):
return price * 0.9 # Bu oran neye göre belirlendi?
DISCOUNT_RATE = 0.9
def calculate_discount(price):
return price * DISCOUNT_RATE
0.9 değeri burada %10'luk bir indirim oranını temsil eder; ancak bu doğrudan yazıldığında anlamı belirsiz kalır — sabit bir değişkene atanarak oran hem açıklanmış hem de yönetilebilir hale getirilmiş olur.
🔖 İpucu: Sabitleri büyük harflerle ve snake_case
formatında adlandırmak, Python topluluk standartlarıyla uyumludur.
Kodun yalnızca çalışması yeterli değildir; aynı zamanda sürdürülebilir, anlaşılabilir ve genişletilebilir olması gerekir. Bu hedeflere ulaşmanın yolu, projeyi anlamlı parçalara bölmekten geçer. Modülerlik ve sınıf temelli tasarım, bu bağlamda temiz kodun temel yapı taşlarındandır.
- Her modül, belirli bir sorumluluğa sahip olmalıdır.
- Kod bölümleri birbirinden mümkün olduğunca bağımsız olmalıdır.
- Modüller, yeniden kullanılabilir olacak şekilde tasarlanmalıdır.
Python'da modülerliği sağlamak için:
- Fonksiyonlar belirli görevlerle sınırlandırılmalı,
- İlgili fonksiyonlar sınıflar altında gruplanmalı,
- Bu sınıflar, işlevsel gruplara göre farklı dosyalara dağıtılmalıdır.
Gerçek dünyada modüler mimariye örnek olarak Django’nun varsayılan proje yapısı verilebilir:
myproject/
├── auth/
│ ├── views.py
│ ├── models.py
│ └── urls.py
├── shop/
│ ├── views.py
│ ├── models.py
│ └── urls.py
└── config/
├── settings.py
├── urls.py
└── wsgi.py
Bu yapıda her "app" kendi iş alanını temsil eder. Bu sayede birimlerin bağımsız olarak gelişmesi ve test edilmesi kolaylaşır.
Sınıflar, kodu hem mantıksal hem yapısal olarak gruplamanın etkili bir yoludur. Ancak yalnızca "class" yazmak yeterli değildir. Temiz kod için sınıflar şu ilkeleri gözetmelidir:
- Single Responsibility (Tek Sorumluluk): Her sınıf yalnızca tek bir iş yapmalı.
- Açık Arayüz: Sınıf dışına açık olan metotlar net ve gerekli olanla sınırlı olmalı.
- Uygulanabilirlik: Gereksiz miras veya soyutlamalardan kaçınılmalı.
class Invoice:
def __init__(self, amount: float):
self.amount = amount
def calculate_tax(self):
return self.amount * 0.18
class InvoicePrinter:
def print_invoice(self, invoice: Invoice):
print(f"Total with tax: {invoice.amount + invoice.calculate_tax()}")
Burada Invoice
sınıfı sadece veriyi ve işlemi taşır, çıktı ise ayrı bir sınıfa aittir. Bu, sorumlulukların ayrılması anlamında temiz kodun idealidir.
🔎 Not: Kod modülerleştikçe test edilebilirlik artar. Her sınıf ve modül, tek başına çalışabilir olmalıdır.