Yazılımda Ownership: Elindeki Bombayı Güvenli Limana Taşımak
Yazılım projelerinde “kucağımızda bir bomba bulduk” hissinden, “artık bu sistemi düşünmemize gerek yok” rahatlığına giden yolu ve sahiplenme (ownership) kültürünü keşfedin.
Yazılım geliştirme dünyasında hepimizin en az bir kez karşılaştığı, adeta kabus gibi çöken o anı hatırlayalım. Şirkete yeni katıldınız veya mevcut ekibinizde yeni bir projeye dahil oldunuz. Önünüze konulan kod tabanı (codebase) tamamen belirsizliklerle dolu, dokümantasyonu olmayan, her an çökmeye hazır ve en ufak bir değişiklikte zincirleme hatalar zinciri (regression) başlatan bir yapıya sahip. Portekizcede bu duruma tam olarak “temos um rojão na mão” yani “elimizde patlamak üzere olan bir havai fişek/bomba var” denir. Türkçede ise biz buna genellikle “kucağımızda saatli bir bomba bulduk” veya “ateşten gömlek giymek” deriz.
Peki, bu tehlikeli ve stresli durumdan, sistemin tıkır tıkır çalıştığı ve geliştiricilerin arkasına yaslanıp “não precisa mais pensar nisso” (artık bunu düşünmemize gerek yok) dediği o huzurlu aşamaya nasıl geçebiliriz? Bu dönüşümün anahtarı, teknik araçlardan ziyade bir zihniyet değişimi olan “Ownership” yani sahiplenme kavramında gizlidir. Bu makalede, bir yazılım sisteminin sorumluluğunu sıfırdan alıp onu tamamen otonom, güvenilir ve zihinsel yük oluşturmayan bir yapıya nasıl dönüştüreceğimizi adım adım inceleyeceğiz.
Yazılımda Ownership (Sahiplenme) Nedir ve Neden Hayatidir?
Yazılım projelerinde sahiplenme (ownership), sadece kod yazıp görevi tamamlamakla ilgili bir durum değildir. Gerçek sahiplenme; bir sistemin tasarımı, kalitesi, hataları, performansı, bakımı ve en önemlisi de işe kattığı değer üzerinde uçtan uca sorumluluk hissetmektir. Birçok organizasyonda geliştiriciler kendilerini sadece “bilet kapatan” (ticket closer) makineler olarak görürler. Bu durum, sistemlerin zamanla sahipsiz kalmasına ve teknik borç (technical debt) batağına saplanmasına yol açar.
Bununla birlikte, sahiplenme bilinci yüksek olan ekiplerde süreçler çok daha farklı işler. Bir hata oluştuğunda “bu benim kodum değil” demek yerine, “bu sorunu sistem genelinde nasıl kalıcı olarak çözeriz?” sorusu sorulur. Sahiplenme kültürü, mikro yönetim (micromanagement) ihtiyacını ortadan kaldırır. Çünkü ekip üyeleri, sistemin sağlığını kendi kişisel prestijleri ve profesyonel standartları olarak görürler. Sonuç olarak, yüksek sahiplenme duygusu doğrudan daha kaliteli yazılımlar, daha az kesinti süresi (downtime) ve daha mutlu bir mühendislik ekibi anlamına gelir.
Sahiplenmeyi üç temel seviyede inceleyebiliriz:
- Bireysel Sahiplenme: Geliştiricinin yazdığı kodun kalitesinden, testlerinden ve canlı ortamdaki (production) davranışından sorumlu olmasıdır.
- Ekip Sahiplenme: Ekibin, üzerinde çalıştığı mikro servislerin veya ürün özelliklerinin tüm yaşam döngüsünü (lifecycle) üstlenmesidir.
- Organizasyonel Sahiplenme: Farklı ekiplerin ortak hedefler doğrultusunda birbirlerinin sistemlerini desteklemesi ve şirket vizyonunu paylaşmasıdır.
“Kucağımızda Bir Saatli Bomba Var!” Aşaması (Temos um rojão na mão)
Bir sistemin “bomba” olarak nitelendirilmesinin arkasında genellikle yılların birikimi olan ihmaller yatar. Bu aşamada, sistemin nasıl çalıştığını kimse tam olarak bilmez. Kod tabanında gezinmek, mayın tarlasında yürümek gibidir. En küçük bir bağımlılık (dependency) güncellemesi bile tüm uygulamayı çökertebilir. Bu durum, ekipler üzerinde inanılmaz bir bilişsel yük (cognitive load) yaratır.
Örneğin, eski bir ödeme entegrasyonu düşünün. Ne zaman bir kampanya dönemi olsa bu sistem kilitleniyor, veri tabanında kilitlenmelere (deadlock) yol açıyor ve müşterilerden şikayet yağmasına neden oluyor. Ekipteki herkes bu servise dokunmaktan korkuyor çünkü kodun içinde yüzlerce satırlık “spagetti” fonksiyonlar bulunuyor. İşte bu, tam anlamıyla elimizde patlamak üzere olan bir bombadır. Bu aşamada geliştiricilerin hissettiği başlıca duygular çaresizlik, stres ve sürekli tetikte olma (on-call nöbeti korkusu) halidir.
Bu aşamadan kurtulmanın ilk adımı, panik yapmayı bırakıp durum tespiti yapmaktır. Bombayı etkisiz hale getirmek için önce onun nasıl çalıştığını, kablolarının nereye gittiğini anlamamız gerekir. Bu da bizi bir sonraki aşamaya, yani kaosun anatomisini çıkarmaya götürür.
Kaosun Anatomisi: Bir Kod Neden “Bomba” Haline Gelir?
Bir sistemin zamanla kontrol edilemez bir canavara dönüşmesinin belirli ve tekrarlayan sebepleri vardır. Bu sebepleri bilmek, teşhis koymamızı kolaylaştırır. Aşağıdaki tabloda, sahipsiz bırakılmış bir sistemin özellikleri ile sahiplenilmiş, sağlıklı bir sistemin özelliklerini karşılaştırmalı olarak görebilirsiniz.
| Özellik | Bomba Sistem (Sahipsiz) | Güvenli Liman (Sahiplenilmiş) |
|---|---|---|
| Test Kapsamı | Yok denecek kadar az veya sadece “yeşil görünsün” diye yazılmış işlevsiz testler. | Yüksek birim (unit) ve entegrasyon (integration) test kapsamı, kritik yolların test edilmesi. |
| Gözlemlenebilirlik | Hatalar sadece kullanıcılar şikayet ettiğinde veya sunucu çöktüğünde fark edilir. | Gelişmiş loglama, metrik takibi ve anlık alarm (alerting) mekanizmaları. |
| Dokümantasyon | Ya hiç yok ya da 3 yıl öncesinden kalma, güncelliğini yitirmiş belgeler. | Güncel README dosyaları, mimari karar kayıtları (ADR) ve API dokümanları. |
| Bağımlılıklar | Yıllardır güncellenmemiş, güvenlik açıklarıyla dolu kütüphaneler. | Düzenli olarak güncellenen ve otomatik araçlarla taranan bağımlılıklar. |
Görüldüğü üzere, aradaki fark sadece teknik beceri değil, aynı zamanda sisteme gösterilen özen ve disiplindir. Bombayı etkisiz hale getirmek için bu tablodaki sol sütundan sağ sütuna doğru planlı bir geçiş yapmamız şarttır.
Adım Adım Sahiplenme: Yangını Söndürme Planı Nasıl Yapılır?
Kucağımızdaki bombayı etkisiz hale getirmek ve sistemi kontrol altına almak için sistematik bir yaklaşım uygulamalıyız. Bu süreci üç ana adıma ayırabiliriz: Gözlemle, Koruma Altına Al ve Yeniden Yapılandır (Refactor).
1. Gözlemlenebilirlik (Observability) Kurulumu
Karanlıkta neyle savaştığınızı göremezsiniz. Bu nedenle, ilk işimiz sisteme ışık tutmaktır. Uygulamanın nerede hata verdiğini, hangi isteklerin yavaş çalıştığını anlamak için loglama (logging) ve metrik izleme araçlarını entegre etmeliyiz. APM (Application Performance Monitoring) araçları kullanarak sistemin röntgenini çekmeliyiz. Böylece, kullanıcılar hatayı fark etmeden önce biz nerede sorun olduğunu görebiliriz.
2. Test Ağını Örmek
Koda dokunmaya korkmamızın sebebi, yapacağımız değişikliğin neleri bozacağını bilmememizdir. Bu korkuyu yenmenin tek yolu test yazmaktır. Ancak, tüm sistemi baştan sona test etmek gerçekçi değildir. Bunun yerine, en kritik iş akışlarını (happy path) belirleyip buralara entegrasyon testleri yazarak başlamalıyız. Bu testler bizim emniyet kemerimiz olacaktır.
3. Küçük Adımlarla Yeniden Yapılandırma (Refactoring)
Büyük ve radikal değişiklikler (big bang rewrite) genellikle başarısızlıkla sonuçlanır. Sistemi tamamen çöpe atıp baştan yazmak yerine, küçük parçalar halinde iyileştirmeler yapmalıyız. Her dokunduğumuz fonksiyonu, bulduğumuzdan biraz daha temiz bırakmalıyız (İzci Kuralı – Boy Scout Rule).
Şimdi, güvensiz ve her an hata vermeye açık bir kod bloğunun, sahiplenme süreciyle nasıl daha güvenli ve yönetilebilir hale getirildiğini bir kod örneği üzerinden inceleyelim.
// KÖTÜ ÖRNEK: Hata yönetimi olmayan, gözlemlenemeyen ve kırılgan kod
async function processPayment(orderId, amount) {
const user = await db.getUserByOrderId(orderId);
// Hata kontrolü yok, user null gelirse sistem çöker
const paymentResult = await paymentGateway.charge(user.email, amount);
if (paymentResult.success) {
await db.updateOrderStatus(orderId, 'PAID');
return true;
}
// Başarısızlık durumu sessizce geçiştiriliyor, loglama yok
return false;
}
Yukarıdaki kod tam bir saatli bombadır. Veri tabanı bağlantısı koptuğunda veya kullanıcı bulunamadığında tüm uygulama çökebilir. Şimdi bu kodu, sahiplenilmiş ve güvenli bir yapıya dönüştürelim:
// İYİ ÖRNEK: Hata yönetimi gelişmiş, loglama barındıran ve test edilebilir kod
async function processPayment(orderId, amount) {
logger.info(Ödeme işlemi başlatıldı. Sipariş ID: ${orderId}, Tutar: ${amount});
try {
const user = await db.getUserByOrderId(orderId);
if (!user) {
logger.error(Ödeme başarısız: Sipariş ID ${orderId} için kullanıcı bulunamadı.);
throw new UserNotFoundError(orderId);
}
const paymentResult = await paymentGateway.charge(user.email, amount);
if (paymentResult.success) {
await db.updateOrderStatus(orderId, 'PAID');
logger.info(Ödeme başarıyla tamamlandı. Sipariş ID: ${orderId});
return { success: true };
}
logger.warn(Ödeme reddedildi. Sipariş ID: ${orderId}, Sebep: ${paymentResult.errorMessage});
return { success: false, reason: paymentResult.errorMessage };
} catch (error) {
logger.error(Ödeme işlemi sırasında kritik hata oluştu. Sipariş ID: ${orderId}, { error: error.message });
// Hata izleme sistemine (örneğin Sentry) bildiriliyor
errorTracker.captureException(error);
throw error; // Hatanın yukarı fırlatılması sağlanıyor
}
}
Gördüğünüz gibi, ikinci örnekte neyin nerede yanlış gittiğini anında görebiliriz. Hatalar yakalanıyor, loglanıyor ve sistemin çökmesi engelleniyor. Bu basit dönüşüm bile ekibin üzerindeki stres yükünü inanılmaz derecede azaltacaktır.
Teknik Borç (Technical Debt) Nasıl Yönetilir?
Yangını söndürdükten sonra, gelecekte yeni yangınların çıkmasını önlemek için teknik borçları yönetilebilir bir seviyede tutmalıyız. Teknik borç, hızlı teslimat yapmak adına bilinçli veya bilinçsiz olarak yapılan kötü tasarım tercihleridir. Tıpkı finansal borçlar gibi, teknik borçların da faizi vardır ve bu faiz zamanla geliştirme hızımızı yavaşlatır.
Teknik borçla mücadele ederken ürün yöneticileri (product managers) ile ortak bir dil konuşmak çok önemlidir. Onlara “bu kod çok kötü, yeniden yazmalıyız” demek yerine, “bu teknik borç yüzünden yeni özellikleri eklememiz normalden iki kat daha uzun sürüyor” şeklinde iş çıktısı odaklı açıklamalar yapmalıyız. Her sprint (geliştirme dönemi) içinde teknik borçları temizlemek için %15-20 oranında bir bütçe ayırmak, sistemin sağlığını korumak için en sürdürülebilir yöntemdir.
“Artık Bunu Düşünmemize Gerek Yok” Aşamasına Geçiş (Não precisa mais pensar nisso)
Sahiplenme sürecinin nihai hedefi, bir sistemi tamamen görünmez kılmaktır. İyi tasarlanmış, testleri tam, gözlemlenebilirliği yüksek ve kendi kendini iyileştirebilen (self-healing) bir sistem, geliştiricilerin zihninde yer kaplamaz. İşte bu aşama, Portekizce ifadesiyle “não precisa mais pensar nisso” aşamasıdır.
Bu aşamaya ulaştığımızda neler değişir?
- Otomatik Dağıtım (CI/CD): Kod her commit edildiğinde otomatik testlerden geçer ve güvenle canlı ortama aktarılır. Manuel müdahaleye gerek kalmaz.
- Kendi Kendini İyileştirme: Sunucu kaynakları tükendiğinde sistem otomatik olarak yeni sunucular ayağa kaldırır (auto-scaling).
- Akıllı Alarmlar: Sadece gerçekten insan müdahalesi gerektiren durumlarda (örneğin ödeme geçidi tamamen çöktüğünde) alarm çalar. Önemsiz hatalar arka planda sessizce çözülür veya loglanır.
- Bilişsel Rahatlık: Ekip, o sistemin varlığını unutur. Sistem arka planda sessizce şirkete para kazandırmaya devam ederken, mühendisler yeni ve heyecan verici projelere odaklanabilirler.
Bu seviyeye ulaşmak zaman ve sabır ister ancak ulaşıldığında sağladığı iş gücü ve motivasyon kazancı paha biçilemezdir.
Gerçek Dünya Senaryosu: Bir E-Ticaret Ödeme Sisteminin Dönüşümü
Konuyu daha iyi somutlaştırmak için gerçek bir vaka analizine (case study) göz atalım. Türkiye’nin önde gelen e-ticaret platformlarından birinde yaşanan gerçek bir dönüşüm hikayesini inceleyelim.
Sorun: Şirketin eski ödeme mikro servisi, her cuma akşamı yapılan indirim kampanyalarında aşırı yük altında eziliyor ve çöküyordu. Sistem çöktüğünde siparişler yarıda kalıyor, veri tabanında tutarsızlıklar oluşuyordu. Yazılım ekibi her cuma gecesini ofiste sabahlayarak, manuel olarak veri tabanını düzeltmekle geçiriyordu. Ekip tam anlamıyla “kucağında bir bomba” ile yaşıyordu.
Çözüm Süreci: Yeni atanan bir teknik lider, “ownership” prensiplerini uygulamaya karar verdi. İlk olarak, sisteme detaylı metrikler eklendi ve darboğazın (bottleneck) veri tabanı bağlantı havuzunun (connection pool) tükenmesi olduğu tespit edildi. Ardından, ödeme istekleri bir kuyruk yapısına (message queue – RabbitMQ) yönlendirilerek asenkron hale getirildi. Kritik ödeme adımları için entegrasyon testleri yazıldı.
Sonuç: Altı aylık planlı bir çalışma sonucunda, ödeme sistemi saniyede 10.000 isteği sorunsuz karşılayabilir hale geldi. Bir sonraki büyük kampanya döneminde sistem tek bir hata bile vermedi. Geliştirici ekibi cuma akşamı ofisten saat 18:00’de ayrıldı ve hafta sonunu aileleriyle geçirdi. Sistem artık “düşünülmesine gerek olmayan” güvenli bir limana dönüşmüştü.
Sıkça Sorulan Sorular (FAQ)
1. Sahiplenme (Ownership) kültürü bir ekipte nasıl başlatılır?
Sahiplenme kültürü yukarıdan aşağıya dikte edilemez; aşağıdan yukarıya doğru teşvik edilmelidir. İlk adım olarak, ekiplere kendi sistemleri üzerinde karar alma özgürlüğü (autonomy) verilmelidir. Hata yapıldığında suçluyu aramak yerine (blameless post-mortem), sistemdeki açıkları bulmaya odaklanılmalıdır.
2. Legacy (eski) bir sistemi baştan yazmak mı yoksa refaktör etmek mi daha mantıklıdır?
Neredeyse her zaman refaktör etmek daha mantıklıdır. Sistemi baştan yazmak (rewrite) çok büyük bir risk barındırır ve genellikle eski sistemin tüm gizli bug’larını yeni sisteme de taşımakla sonuçlanır. Küçük, güvenli adımlarla mevcut sistemi iyileştirmek daha az riskli ve daha öğreticidir.
3. Teknik borçları temizlemek için ürün yöneticisini (Product Owner) nasıl ikna edebiliriz?
Ürün yöneticisine teknik terimlerle değil, iş çıktılarıyla yaklaşmalısınız. Teknik borçların temizlenmesinin yeni özellikleri ne kadar hızlandıracağını, hata oranlarını nasıl düşüreceğini ve sunucu maliyetlerini nasıl azaltacağını somut verilerle sunmalısınız.
#YazılımGeliştirme #Ownership #YazılımMimarisi #CleanCode #Teknoloji