Quality Attribute'lar: ISO/IEC 25010:2023 ile Architecture Characteristic'leri Tanımlamak
Architecture characteristic'ler sistemin ne yaptığını değil, bunu ne kadar iyi yaptığını anlatır ve ancak bir domain hedefine ve sayıya bağlandığında karar üretir. ISO/IEC 25010:2023'ün dokuz characteristic'i ve 2011'den farkları, FoSA'nın sınıflaması, .NET 10'daki karşılıkları ve Bookstore için seçilen ilk üç characteristic.
Sürüm notu: Bu yazıdaki kodlar C# 14 ve .NET 10 ile derlenip denenmiştir.
Önce Tahmin Et
Geçen ay bir bot, Bookstore'un katalog aramasını bir gece boyunca saniyede yüzlerce istekle taradı ve veritabanını yordu. Ertesi sabah ekipten biri ASP.NET Core'un yerleşik rate limiter'ını ekledi; değerleri de "güvenli olsun" diye seçti. Kodu küçültülmüş haliyle, kendi kendine istek atan bir console uygulaması olarak çalıştırıyoruz:
using Microsoft.AspNetCore.RateLimiting;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.WebHost.UseUrls("http://127.0.0.1:5199");
builder.Services.AddRateLimiter(options =>
{
options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
// Bot taramasından sonra "güvenli olsun" diye seçildi
options.AddFixedWindowLimiter("search", limiter =>
{
limiter.PermitLimit = 3;
limiter.Window = TimeSpan.FromSeconds(10);
limiter.QueueLimit = 0;
});
});
builder.Services.AddHealthChecks();
WebApplication app = builder.Build();
app.UseRateLimiter();
app.MapGet("/books/search", (string q) => Results.Ok(new[] { "Dune", "Emma" }))
.RequireRateLimiting("search");
app.MapHealthChecks("/health");
await app.StartAsync();
using HttpClient http = new() { BaseAddress = new Uri("http://127.0.0.1:5199") };
for (int i = 1; i <= 5; i++)
{
HttpResponseMessage response = await http.GetAsync("/books/search?q=dune");
Console.WriteLine($"search #{i}: {(int)response.StatusCode} {response.StatusCode}");
}
HttpResponseMessage health = await http.GetAsync("/health");
Console.WriteLine($"health: {(int)health.StatusCode} {await health.Content.ReadAsStringAsync()}");
await app.StopAsync();
Limiter global değil, endpoint'e bağlı ve tek bir fixed window paylaşıyor. Beş arama isteğinden kaçı geçer? Daha önemlisi: okul açılışı kampanyasında Bookstore'un katalog araması saniyede yaklaşık 200 istek alıyor. O gün bu ayar ne yapar ve 3 sayısı nereden geldi?
Sayı Nereden Geldi?
search #1: 200 OK
search #2: 200 OK
search #3: 200 OK
search #4: 429 TooManyRequests
search #5: 429 TooManyRequests
health: 200 Healthy
Dördüncü istekten itibaren herkes 429 alıyor. Policy bir partition key kullanmadığı için limit kişi başına değil, endpoint'in tamamı için geçerli: on saniyede üç arama, bütün Bookstore için. Kampanya günü bu ayar botu durdurur, ama aynı anda bütün müşterileri de durdurur. Health check ise yeşil yanıyor; sistem sağlıklı, sadece kimseye hizmet vermiyor.
Kodda bir hata yok. Eksik olan şey koddan önce gelmesi gereken bir cümle: "Katalog araması kampanya döneminde saniyede 200 meşru isteği karşılamalı; tek bir istemci saniyede 20 aramayı aşarsa yavaşlatılmalı." Bu cümle olmadan PermitLimit bir tahmindir. Bu cümle bir quality attribute'u tarif ediyor: sistemin ne yaptığını değil, bunu ne kadar iyi, hangi koşullarda yaptığını. Bu yazı bu tür cümleleri yazmak için ortak bir dil kuruyor.
Functional ve Non-Functional Requirement
Functional requirement sistemin ne yaptığını söyler: "müşteri kitabı başlığa göre arayabilir", "sipariş verildiğinde stok ayrılır". Non-functional requirement ise bunun nasıl yapılacağına dair koşulları: ne kadar hızlı, ne kadar süre kesintisiz, hangi saldırılara karşı dayanıklı, değiştirmek ne kadar kolay. İngilizcede çoğu "-ility" ile bittiği için bunlara "ilities" de denir: availability, scalability, maintainability, testability.
"Non-functional" adı yanıltıcı, çünkü bu requirement'lar işlevsiz değil; yerine getirilmezlerse sistem işlevini kaybeder. Kampanya günü kimseye arama yaptırmayan bir katalog, fonksiyonel olarak "arama yapabiliyor" olsa da işe yaramaz. Bu yüzden literatürde iki terim öne çıktı. SEI (Software Engineering Institute) çevresi ve ISO (International Organization for Standardization) standartları quality attribute ya da quality characteristic der. Richards ve Ford'un Fundamentals of Software Architecture kitabı (2. baskı, 2025; bundan sonra FoSA) ise architecture characteristic terimini tercih eder ve bir şeyin architecture characteristic sayılması için üç koşul koyar: domain'e ait olmayan bir design kaygısı olmalı (arama yapmak domain'e ait, aramanın 300 milisaniyede dönmesi değil), yapının bir yönünü etkilemeli (çözmek için mimaride bir şey değişmeli) ve uygulamanın başarısı için kritik ya da önemli olmalı.
İkinci koşul pratik bir filtre. "Sipariş toplamı doğru hesaplanmalı" çok önemli, ama mimariyi değiştirmez; doğru yazılmış bir method ve iyi testler yeter. "Kampanya günü saniyede 200 aramayı karşılamalı" ise cache, okuma replikası ya da arama motoru gibi yapısal kararları tetikleyebilir. Mimari konuşmayı ikinci türden requirement'lar yönetir.
ISO/IEC 25010:2023'ün Dokuz Characteristic'i
Characteristic'lerin isimlerini her ekip kendisi uydurursa "performans" bir kişi için latency, bir başkası için CPU (Central Processing Unit) kullanımı olur. ISO ve IEC (International Electrotechnical Commission) ortak standardı ISO/IEC 25010 bu yüzden faydalı: ortak bir isim listesi veriyor. Kasım 2023'te yayımlanan ikinci baskı, product quality model'i dokuz characteristic ve onların altındaki subcharacteristic'lerle tanımlıyor. Standart ücretli; aşağıdaki liste ISO'nun resmî önizleme sayfalarına ve arc42'nin quality model özetine dayanıyor, isimler standarttaki İngilizce halleriyle.

Functional suitability, ürünün kullanıcıların açık ve örtük ihtiyaçlarını karşılayan fonksiyonları sağlaması; doğru sonuç vermesi (functional correctness) de bunun parçası. Performance efficiency, belirtilen süre ve throughput sınırları içinde çalışmak ve kaynakları verimli kullanmak; capacity subcharacteristic'i eşzamanlı kullanıcı sayısı ya da veritabanı boyutu gibi üst sınırları kapsıyor. Compatibility, aynı ortamı başka ürünlerle paylaşabilmek ve onlarla bilgi alışverişi yapabilmek. Interaction capability, kullanıcının arayüz üzerinden görevini tamamlayabilmesi. Reliability, belirtilen koşullarda belirtilen süre boyunca doğru çalışmak; availability ve fault tolerance burada. Security, bilgiyi yetkisiz erişime karşı korumak; 2023'te eklenen resistance, saldırı altındayken çalışmaya devam edebilmeyi anlatıyor. Maintainability, ürünü değiştirmenin etkinliği ve verimliliği; modifiability ve testability burada. Flexibility, ürünün değişen requirement'lara, ortamlara ve kullanım bağlamlarına uyarlanabilmesi; scalability artık burada. Safety ise ürünün insan hayatını, sağlığını, malı ya da çevreyi tehlikeye atmaması.
Açılıştaki rate limiter bu ağaçta üç yere dokunuyor: capacity (kampanya yükünü karşılamak), resistance (bot saldırısı altında çalışmaya devam etmek) ve availability (meşru kullanıcıya hizmet vermek). Ayar tek başına capacity'yi öldürdü, resistance'ı ise kazandı. Bu, bir sonraki bölümlerin ana fikrinin küçük bir örneği: characteristic'ler birbirinden bağımsız değil.
2011'den 2023'e Ne Değişti?
İnternette bulacağın çoğu tablo hâlâ 2011 sürümünü gösteriyor. Standardın kendi önsözüne göre ana farklar şunlar:
- Safety yeni bir characteristic olarak eklendi; altında operational constraint, risk identification, fail safe, hazard warning ve safe integration var.
- Usability'nin yerini interaction capability, portability'nin yerini flexibility aldı.
- Interaction capability'ye inclusivity ve self-descriptiveness, security'ye resistance, flexibility'ye scalability eklendi.
- User interface aesthetics yerini user engagement'a, maturity yerini faultlessness'a bıraktı; accessibility, inclusivity ve user assistance olarak ikiye bölündü.
- Eskiden aynı dokümanda olan quality in use modeli ISO/IEC 25019'a, modellerin genel kullanımı ISO/IEC 25002'ye taşındı.
Bir mimar için en önemli iki değişiklik scalability'nin artık resmî bir subcharacteristic olması ve security'nin resistance ile "saldırı altında hizmet vermeye devam etmek" boyutunu kazanması. İkisi de Bookstore'un kampanya günü sorununu doğrudan ilgilendiriyor. Usability'nin interaction capability'ye dönüşmesinin nedeni de ilginç: standart artık ürünün kendisine ait özelliği (arayüzün etkileşime uygunluğu) ile kullanım sonucunu (kullanıcının gerçekten verimli olup olmadığı) ayırıyor; ikincisi quality in use modelinde.
FoSA'nın Sınıflaması: Operational, Structural, Cloud, Cross-Cutting
ISO listesi kapsamlı, ama bir architect'in günlük konuşmasında FoSA'nın daha kaba sınıflaması daha kullanışlı. Kitabın ikinci baskısı characteristic'leri dört gruba ayırıyor. Operational characteristic'ler sistemin çalışırken nasıl davrandığıyla ilgili: availability, continuity, performance, recoverability, reliability, robustness, scalability. Structural characteristic'ler kodun yapısıyla: configurability, extensibility, installability, reuse, localization, maintainability, portability, upgradeability. İkinci baskıda ayrı bir grup olan cloud characteristic'leri: on-demand scalability ve elasticity, zone bazlı availability, region bazlı privacy ve security. Cross-cutting olanlar ise her yere dokunanlar: accessibility, archivability, authentication, authorization, legal, privacy, security, supportability, usability.
FoSA'nın daha da önemli bir ayrımı explicit ve implicit characteristic'ler. Explicit olanlar requirement dokümanında yazar: "kampanyada saniyede 200 arama". Implicit olanlar hiçbir yerde yazmaz ama herkes varsayar: sistemin güvenli olması, verinin kaybolmaması, makul bir sürede cevap vermesi. Architect'in işinin bir kısmı implicit olanları görünür kılmak. Bookstore'da kimse "ödeme bilgileri sızdırılmamalı" diye bir requirement yazmadı, ama bunu sağlamayan bir mimari kabul edilemez.
Characteristic'ler Koda Nasıl Yansır?
Characteristic'ler soyut görünür, ama .NET 10'da çoğunun doğrudan bir karşılığı var. Bu karşılıklar bir characteristic'in nerede mimari karara dönüştüğünü görmek için iyi bir harita:
| ISO/IEC 25010:2023 | .NET 10 / ASP.NET Core karşılığı | Hangi kararı gerektirir |
|---|---|---|
| capacity, resistance | AddRateLimiter, PartitionedRateLimiter | Kim için, saniyede kaç istek, limit aşılınca ne olacak |
| availability | AddHealthChecks, MapHealthChecks | "Sağlıklı" ne demek, hangi bağımlılık kontrol edilecek |
| fault tolerance | IHttpClientFactory, DelegatingHandler, timeout'lar | Ödeme sağlayıcısı yanıt vermezse ne olacak |
| time behaviour | HybridCache, OutputCache | Hangi veri ne kadar bayat olabilir |
| analysability | System.Diagnostics.Metrics, ActivitySource, ILogger | Neyi ölçeceğiz, kim bakacak |
| confidentiality | ASP.NET Core Data Protection, authorization policy'leri | Hangi veri kime görünür |
| resource utilization | Native AOT (Ahead-of-Time compilation), trimming | Bellek ve başlama süresi için reflection'dan vazgeçmeye değer mi |
Tablonun son kolonu asıl mesaj. Framework sana mekanizmayı veriyor, sayıyı ve politikayı vermiyor. AddHealthChecks tek satır, ama "veritabanı yavaşsa sağlıklı mıyız?" sorusunun cevabı bir availability kararıdır; ASP.NET Core Web API (Application Programming Interface) serisinin health check yazısında liveness ve readiness ayrımı tam bu soruyu cevaplıyordu. Açılıştaki rate limiter da aynı: mekanizma doğru, sayı uydurma.
Bookstore İçin Hangileri Gerçekten Kritik?
Bir requirement toplantısında "sistem scalable, secure, highly available, maintainable ve performant olmalı" cümlesini duymuşsundur. Bu cümle hiçbir şey söylemez, çünkü characteristic'ler birbiriyle çatışır: her güvenlik kontrolü latency ekler, her esneklik noktası karmaşıklık ekler, her availability önlemi maliyet ekler. Hepsini istemek, hiçbirini önceliklendirmemek demek. FoSA'nın tavsiyesi net: en iyi mimariyi değil, en az kötü mimariyi hedefle ve olabildiğince az characteristic'i destekle. Kitaptaki çalışma kağıdında characteristic'ler için yalnız yedi satır var ve bunlardan üçünün "en önemli" diye işaretlenmesi isteniyor; amaç zorla önceliklendirmek.
Bookstore için ilk yazıdaki sayılara bakarak bu seçimi yapalım. Availability: checkout durursa para kaybediliyor ve kampanya günleri yılın cirosunun büyük kısmını getiriyor; ilk üçte. Performance efficiency, özellikle time behaviour: arama ve checkout yavaşlarsa müşteri sepeti bırakıyor; ilk üçte. Modifiability: üç kişilik ekip haftada bir deploy ediyor ve on sekiz ay içinde ikinci ülke ve mobil uygulama gelecek; değişim hızını korumak ilk üçte.
Implicit ama vazgeçilmez olanlar: security (ödeme ve kişisel veri var; OWASP (Open Worldwide Application Security Project) Top 10 serisinin konusu) ve functional correctness (para ve stok yanlış hesaplanamaz). Bunlar "ilk üç" listesinde değil, çünkü tartışmaya açık değiller; her mimari seçenek bunları sağlamak zorunda.
Bilinçli olarak "şimdilik değil" denenler de en az bu kadar önemli: elasticity (trafik zirveleri önceden biliniyor, yük anlık patlamıyor; kampanyadan önce kapasite artırmak yeter), multi-region çalışma (tek ülkede satış var) ve parçaların bağımsız deploy edilebilmesi (tek ekip, tek deployment yeterli). Bu "hayır"ları yazmak, ileride biri microservices önerdiğinde konuşmayı gerekçeye bağlar: hangi characteristic değişti de bu karar yeniden açılıyor?
Sık Düşülen Tuzaklar
Characteristic'i domain hedefinden koparmak. "Sistem scalable olmalı" ama kaç kullanıcı, hangi zirve, ne kadar sürede? Sayı yoksa characteristic de yoktur, sadece bir dilek vardır. Açılıştaki PermitLimit = 3 tam olarak bir dileğin koda dönüşmüş haliydi.
Hepsini istemek. Dokuz characteristic'in hepsini "yüksek" işaretleyen bir liste, ekibe hiçbir şey söylemez. Çatışma anında hangisinin kazanacağı belli değilse, karar o anki en yüksek sesli kişiye kalır.
Implicit olanları unutmak. Kimsenin yazmadığı characteristic'ler en geç fark edilenlerdir; genellikle bir production incident'ından sonra. İlk mimari konuşmada security, recoverability ve analysability'yi bilinçli olarak masaya koy.
2011 tablosuyla çalışmak. Eski bir template'ten kopyalanan "usability" ve "portability" başlıkları bugün yanlış değil ama eksik: resistance, scalability ve safety yok. Yeni bir requirement dokümanında 2023 isimlerini kullan.
Characteristic'i mekanizmayla karıştırmak. "Redis kullanacağız" bir characteristic değil, bir çözüm. Characteristic "katalog araması kampanyada p95 300 milisaniyenin altında" gibi bir hedeftir; Redis ona ulaşmanın yollarından biri.
Küçük Bir Deneme
★☆☆ Açılıştaki rate limiter'ı AddPolicy ve RateLimitPartition.GetFixedWindowLimiter ile istemci IP'sine (Internet Protocol) göre partition'lanacak şekilde değiştir. Aynı beş isteği iki farklı partition key'le gönderip çıktının nasıl değiştiğine bak.
★★☆ Üzerinde çalıştığın sistem için FoSA'nın çalışma kağıdını doldur: en fazla yedi characteristic, bunlardan üçü "en önemli", ayrıca implicit olanlar ve bilinçli olarak "şimdilik değil" dediklerin. Her biri için ISO/IEC 25010:2023'teki karşılığını yaz.
★★★ Ekibindeki bir requirement dokümanından üç non-functional cümle seç ve FoSA'nın üç koşuluna göre değerlendir: domain dışı mı, yapıyı etkiliyor mu, başarı için kritik mi? Yapıyı etkilemeyenleri listeden çıkarmayı öner.
Bu yazıda characteristic'lere isim verdik ve Bookstore için ilk üçü seçtik. Ama "availability" ve "performance" hâlâ birer kelime. Bir sonraki yazıda onları SEI'nin quality attribute senaryolarıyla ölçülebilir requirement'lara çevireceğiz ve "hızlı olsun"un neden ortalama ile ölçülemeyeceğini gerçek bir ölçümle göreceğiz.