Müşteri soruyor: "Bu özelliği yeni bir uygulamada eklemek bir hafta sürecekse, burada neden üç hafta gerekiyor?"
Bu çok iyi bir soru.
Ve çoğu zaman cevap şu olmaz: "çünkü programcılar daha yavaş çalışıyor".
Sorun çok daha derinde olabilir - sistemin mimarisinde, bağımlılıklarında, veri saklama biçiminde, test eksikliğinde, geçmiş kararlarında ve yıllar içinde eklenen yeni değişikliklerde.
İşte bu yüzden yazılım geliştirme maliyeti sabit değildir.
Aynı özellik, iki farklı sistemde tamamen farklı bir tutara mal olabilir.
Kod yalnızca özellik sayısına göre fiyatlandırılmaz
İlk bakışta görev oldukça basit görünebilir.
"Verileri Excel'e aktarma özelliği ekleyelim."
Ya da: "Yeni bir kullanıcı rolü ekleyelim."
Ya da: "Sistemi CRM'imizle bağlayalım."
Sorun şu ki, bir özellik hiçbir zaman sistemin geri kalanından tamamen bağımsız olarak var olmaz.
Yeni işlevsellik şu alanlarda değişiklik gerektirebilir:
- veritabanında,
- API'de,
- backend'de,
- frontend'de,
- yetkilendirme sisteminde,
- giriş yapmada,
- raporlamada,
- entegrasyonlarda,
- testlerde,
- önbellekleme mekanizmalarında,
- dokümantasyonda,
- yayınlama sürecinde.
Sistem ne kadar birbirine bağlıysa, değişiklikten önce o kadar çok öğeyi analiz etmek gerekir.
En büyük maliyet, ilk kod satırı yazılmadan önce ortaya çıkabilir
Olgun bir sistemde geliştiricinin doğrudan kod yazmaya başlamaması gerekir.
Önce şu sorulara yanıt verilmelidir:
- Bu özellik nereye eklenmeli?
- Hangi modüllerle iletişim kuracak?
- Hangi verileri kullanıyor?
- Mevcut yetkilendirme mekanizmaları bunu kapsıyor mu?
- Değişiklik diğer süreçleri etkileyecek mi?
- Hangi testlerin güncellenmesi gerekiyor?
- Mevcut mimari bunu düzgün şekilde yapmaya gerçekten izin veriyor mu?
Bunların hepsi, özelliği üretme maliyetinin bir parçasıdır.
Bu nedenle eski bir sistemde işin önemli bir kısmı, kodlamanın kendisinden çok mevcut çözümün bağımlılıklarını ve kısıtlarını anlamak olabilir.
Teknik borç faiz gibi işler
Teknik borcu düşünmenin iyi bir yolu, sonraki değişikliklerin maliyetidir.
Eğer bir çözüm bir zamanlar hızlıca yapıldıysa, bu tamamen makul olabilir.
Sorun, geçici çözüm sistemin kalıcı bir parçası haline geldiğinde ortaya çıkar.
Yeni bir özellik gelir.
Sonra bir tane daha.
Bir istisna oluşur.
Sonra bir istisna daha.
Buna entegrasyon, geçici çözüm, manuel süreç ve ek bir kural eklenir.
Birkaç yıl sonra sistemin neden tam olarak böyle çalıştığını kimse hatırlamaz.
Ama sonraki her değişiklik tüm bu tarihsel kararları hesaba katmak zorundadır.
Martin Fowler, teknik borcu sistemin iç kalitesindeki sorunlar nedeniyle yapılan değişikliklerde ortaya çıkan ek çaba olarak tanımlar.
Şöyle denebilir: teknik borç gelişimi hemen durdurmak zorunda değildir. Önce, sonraki her değişikliğin daha pahalı hale gelmesine neden olur.
Birinci işaret: "Bu arada beş şey daha düzeltmek gerekiyor"
Bu, en karakteristik belirtilerden biridir.
Müşteri tek bir özellik sipariş eder.
Analiz sırasında, bunu uygulamak için şunların gerekli olduğu ortaya çıkar:
- tablo yapısını düzeltmek,
- yetkilendirme yöntemini değiştirmek,
- bir kütüphaneyi güncellemek,
- eski bir API'yi onarmak,
- frontend'in bir bölümünü yeniden yazmak.
Bir anda küçük özellik artık küçük bir özellik olmaktan çıkar. Bunun nedeni gereksinimin karmaşık olması değildir. Bunun nedeni sistemin artık uygun mimari sınırlarının olmamasıdır.
İkinci işaret: tek bir değişiklik tüm sistemin test edilmesini gerektirir
Küçük bir değişiklik tam bir manuel regresyon testi gerektiriyorsa, organizasyon otomasyon eksikliğinin bedelini ödüyordur.
Sistem büyüdükçe olası kombinasyonların sayısı artar.
Yeterli test seti olmadan yeni özelliğin eskiyi bozmadığından emin olmak giderek zorlaşır.
Bu da ihtiyatlılığa yol açar.
Yayınlar daha seyrek olur.
Değişiklikler büyür.
Risk artar.
Ve daha büyük yayınların, sorun çıktığında teşhis edilmesi daha zordur.
Kısır döngü oluşur.
Üçüncü işaret: "bu modüle dokunmamak daha iyi"
Bu cümle bir uyarı lambasını yakmalıdır.
Belirli bir modül, davranışı öngörülemez olduğu için ekip tarafından kaçınılan bir alan haline geldiyse, sistemde ciddi bir sürdürülebilirlik sorunu vardır.
Daha da kötüsü, eğer nasıl çalıştığını sadece bir kişi biliyorsa. O zaman şirket yalnızca teknik borca sahip değildir. Ayrıca bilgi riski de taşır.
Bir çalışanın ayrılması, sistemi güvenli şekilde geliştirmek için gereken bilginin kaybı anlamına gelebilir.
Dördüncü işaret: her özellik istisna gerektirir
İyi tasarlanmış bir sistemin öngörülebilir kuralları olmalıdır.
Eğer her yeni özellik özel bir istisna, ek bir koşul ya da bireysel bir yol gerektiriyorsa, mimari büyük olasılıkla gelişimi sınırlamaya başlıyordur.
Bu da çoğu zaman artık kolayca öngörülemeyen bir koda yol açar.
Ve öngörülebilirliğin olmaması, analiz, test ve bakım maliyetinin artması demektir.
Her şeyi yeniden mi yazmak gerekir?
Hayır.
Ve burada çok önemli bir ayrımı yapıyoruz.
Teknik borç otomatik olarak rewrite gerektirmez.
Olası çözümler şunları içerir:
Refaktörizasyon
Yani iş davranışını değiştirmeden mevcut kodun yapısını iyileştirmek.
Sistem hâlâ anlamlı bir mimariye sahipse ama belirli parçalar bakımı zorlaştırıyorsa bu iyi bir yaklaşımdır.
Seçilmiş bileşenleri modernleştirmek
Tüm uygulamayı değiştirmek gerekmez.
En sorunlu modülden, entegrasyondan ya da katmandan başlanabilir.
Kademeli göç
Yeni öğeler eski sistemin yanında çalışabilir ve sonraki alanlar aşamalı olarak taşınır.
Bu yaklaşım, tek seferlik bir migrasyonun riskini azaltmayı sağlar. Legacy modernizasyonu literatüründe genellikle işlevselliğin kademeli olarak ayrıştırılması ve sistemin sonraki parçalarının değiştirilmesi kullanılır.
Rewrite
Yeni bir sistemin inşası, mevcut mimari o kadar kısıtlayıcı hale geldiğinde anlamlıdır ki, daha fazla modernleştirme artık makul bir getiri sağlamaz.
Ama rewrite, ekibin frustrasyonuna bir tepki olarak değil, analiz sonucunda alınan bir karar olmalıdır.
Modernizasyona henüz yatırım yapmamak ne zaman daha iyidir?
Technical debt başlı başına geliştirmeyi durdurmak için bir neden değildir. Her sistemin belli bir teknik borç seviyesi vardır. Bazen bunu kapatmanın ekonomik bir anlamı olmaz.
Eğer uygulama:
- istikrarlı çalışıyorsa,
- güvenliyse,
- az sayıda değişiklik alıyorsa,
- önemli ölçüde gelişmeyecek bir süreci destekliyorsa,
- operasyonel sorunlar yaratmıyorsa,
onu mevcut halinde bırakmak mantıklı olabilir.
Mesele, her sistemin teknolojik olarak kusursuz olması değildir.
Mesele, borç seviyesinin bilinçli bir karar olmasıdır.
Borç maliyeti ne zaman bir iş problemine dönüşür?
Firmanın sonuçlarını etkilemeye başladığında.
Örneğin:
Yeni bir özelliğin bir ayda pazara çıkması planlanmıştı, ama üç ay gerekiyor.
Yeni bir ortakla entegrasyon uzuyor, çünkü eski sistemin API'si yeni verileri kolayca işlemeye izin vermiyor.
Ekipteki kilit kişi her seferinde çalışmalara katılmak zorunda kalıyor, çünkü yalnızca o eski modülü biliyor.
Her büyük dağıtım saatler süren regresyon testi gerektiriyor.
Rakip daha hızlı yeni özellikler sunuyor, çünkü onun platformu daha hızlı deney yapmaya izin veriyor.
Bu noktada technical debt artık IT departmanının bir sorunu olmaktan çıkar.
Bir iş problemi haline gelir.
Durumun kötüleştiği nasıl ölçülür?
Karmaşık bir KPI sistemi kurmaya gerek yok.
Birkaç basit göstergeyi izlemek faydalıdır:
Lead time - bir değişiklik üzerinde çalışmaya başlanmasından canlıya alınmasına kadar geçen süre.
Yayın sıklığı - ekibin değişiklikleri ne sıklıkla güvenli bir şekilde teslim edebildiği.
Change failure rate - yayınların ne sıklıkla sorunlara yol açtığı.
Hizmete dönüş süresi - bir arızadan sonra stabil çalışmaya ne kadar hızlı dönülebileceği.
Özellik teslim süresi - benzer işlerin giderek daha fazla çaba gerektirip gerektirmediği.
Buna ek olarak manuel işlem sayısını, test kapsamını, bağımlılıkların güncelliğini ve yeni bir geliştiricinin projeye adapte olması için gereken süreyi analiz etmek faydalıdır.
Bu tür veriler, sorunun gerçekten teknik olup olmadığını ya da süreçten, gereksinimlerden veya çalışma biçiminden mi kaynaklandığını görmeyi sağlar.
En kötü çözüm "bir hızlı düzeltme daha"dır
Ekip mimarinin değişmesi gerektiğini biliyor ama konuyu her seferinde erteliyorsa, sistem bir sarmala girebilir.
"Şimdilik bir workaround yapalım."
"Refaktörizasyonu sonra yaparız."
"Şimdilik yeter."
"Bir sonraki release'de."
Sorun şu ki bir sonraki release yeni gereksinimler getirir.
Ve yapılan her sonraki workaround, bir sonraki değişikliğin maliyetini artırır.
Bu yüzden technical debt'i kapatma kararı, krize verilen rastgele bir tepki değil, ürün geliştirme stratejisinin bir parçası olmalıdır.
İyi uygulama, asla eskimeyen uygulama değildir
Her sistem değişecektir.
Teknolojiler değişecektir.
Müşterilerin yeni ihtiyaçları olacaktır.
Yeni entegrasyonlar ortaya çıkacaktır.
Şirketin çalışma biçimi değişecektir.
Bu yüzden amaç, asla modernize edilmesi gerekmeyecek bir uygulama oluşturmak olmamalıdır. Amaç, modernizasyonun işi durdurmadan mümkün olduğu bir mimari oluşturmaktır. Bu çok büyük bir farktır.
Çünkü en iyi sistem, ilk açıldığı gün en modern görünen sistem değildir. Aynı zamanda birkaç yıl sonra da şirketin değişikliklere hızlı tepki vermesini sağlayan sistemdir.
Ve eğer her yeni özellik giderek daha pahalıya mal oluyorsa, bu her zaman özelliğin zor olduğu anlamına gelmez.
Belki de zorlaşan artık sistemin kendisidir.
