---
title: 'Uygunluk İşlevleri'
source: 'https://academia.sh/tr/kurslar/mimari-yonetisim/uygunluk-islevleri'
course: 'Kalite Nitelikleri ve Yönetişim'
language: tr
updated: '2026-08-17T18:10:33+00:00'
license: 'CC BY-SA 4.0'
---

# Uygunluk İşlevleri

Bir mimari kuralın koşturulabilir bir sayıya çevrilmesi: iki uygunluk işlevinin gerçek modül dosyaları üzerinde yazılması, bilinen sızıntı kümesinde yakalanan ve kaçan ihlal ile yanlış alarmın sayılması, eşiğin baştan sona taranması, kuralın satır ve dosya okuma maliyeti ve yanlış alarmın kuralı devre dışı bıraktırması.

Önceki ders dokuz yönetişim kuralının beşinin bir denetime çevrilebileceğini yazdı ama çevirinin
bedelini saymadı. Çeviri şu demektir: kural bir **sayıya** ve bir **eşiğe** indirilir. Sayıyı üreten
işleve **uygunluk işlevi** denir; kaynağı, yapılandırmayı ya da bir grafiği okur ve tek bir nicelik
döndürür. Eşik ise kararın kendisidir — bu sayının üstü ihlal, altı uygundur.

Eşik seçildiği anda iki hata birden doğar. Eşik gevşekse gerçek ihlaller altından geçer; sıkıysa
kurallara uygun modüller ihlal diye işaretlenir. Bu ikili, kalite göstergelerindeki **kaçan hata**
ile **yanlış geçme** olgusunun aynısıdır; buradaki fark, hatayı yapanın bir sınama değil bir mimari
kural olmasıdır. Bu ders bir uygunluk işlevini gerçekten yazar, bilinen bir sızıntı kümesinde
koşturur ve üç sayıyı birden sayar: yakalanan, kaçan, yanlış alarm. Through-line **bölgesel
kütüphane ağıdır** ve kurgudur.

## Ölçülecek Modül Kümesi

Denetlenecek kural şudur: **bir modül, dışarıya işini yapmak için gerekenden fazlasını açmaz.**
Cümle hâliyle denetlenemez; denetlenebilmesi için "fazlasının" bir sayıya bağlanması gerekir.
Aşağıdaki blok ölçülecek kod tabanını diske yazar: on iki modül, her biri kendi dışa açık
sembollerini dışa aktarır ve başkalarının sembollerini ithal eder.

**YN4 — bölgesel kütüphane ağının kod tabanı on iki modülden oluşur ve her modülün dışa açtığı
sembol sayısı ile bu sembolleri kimin ithal ettiği aşağıdaki haritadan üretilir.** Gerekçe: uygunluk
işlevi gerçek dosyaları okumalıdır; harita, okunacak dosyaları üreten girdidir. Sayılar değişirse
aşağıdaki eşik taraması da değişir, işlevin kendisi değişmez.

```js
// kur.mjs — bolgesel kutuphane agi modullerini disa acilan sembolleriyle diske yazar
import { mkdirSync, writeFileSync } from "node:fs";

// acik: disa acilan sembol sayisi | tuketici: hangi modul hangi sembol sirasini ithal ediyor
const MODUL = {
  ortakBicim:      { acik: 9, tuketici: { raporlama: [0, 1, 2], oduncAkisi: [0, 3, 4],
                                          subeUcu: [1, 5], ayarlar: [2] } },
  subeUcu:         { acik: 11, tuketici: { raporlama: [0, 1, 2] } },
  katalogKoprusu:  { acik: 7, tuketici: { oduncAkisi: [0, 1, 2, 3, 4, 5, 6] } },
  oduncAkisi:      { acik: 8, tuketici: { subeUcu: [0, 1, 2, 3], raporlama: [4, 5],
                                          ucretHesabi: [6] } },
  ucretHesabi:     { acik: 4, tuketici: { subeUcu: [0] } },
  uyelikKaydi:     { acik: 3, tuketici: { subeUcu: [0, 1], oduncAkisi: [2] } },
  kimlikKoprusu:   { acik: 2, tuketici: { subeUcu: [0], uyelikKaydi: [1] } },
  bildirimKuyrugu: { acik: 5, tuketici: { oduncAkisi: [0] } },
  raporlama:       { acik: 6, tuketici: { subeUcu: [0, 1, 2, 3, 4, 5] } },
  depoErisimi:     { acik: 3, tuketici: { oduncAkisi: [0] } },
  olayGunlugu:     { acik: 4, tuketici: { oduncAkisi: [0] } },
  ayarlar:         { acik: 4, tuketici: { subeUcu: [0], oduncAkisi: [1], raporlama: [2],
                                          ucretHesabi: [3] } },
};

mkdirSync("modul", { recursive: true });
for (const ad of Object.keys(MODUL)) {
  const ithal = Object.entries(MODUL)
    .filter(([, m]) => m.tuketici[ad])
    .map(([hedef, m]) => `import { ${m.tuketici[ad].map((i) => `${hedef}${i + 1}`).join(", ")} }` +
      ` from "./${hedef}.mjs";`);
  const disa = Array.from({ length: MODUL[ad].acik },
    (_, i) => `export const ${ad}${i + 1} = (x) => x;`);
  writeFileSync(`modul/${ad}.mjs`, `${ithal.join("\n")}\n${disa.join("\n")}\n`);
}

const kenar = Object.values(MODUL).reduce((s, m) => s + Object.keys(m.tuketici).length, 0);
const sembol = Object.values(MODUL).reduce((s, m) => s + m.acik, 0);
console.log(`${Object.keys(MODUL).length} modul dosyasi yazildi; ` +
  `${sembol} disa acilan sembol, ${kenar} ithal kenari`);
console.log(`\n${"modul".padEnd(17)}${"disa acilan".padStart(12)}   ithal eden modul`);
for (const [ad, m] of Object.entries(MODUL)) {
  console.log(`${ad.padEnd(17)}${String(m.acik).padStart(12)}   ` +
    `${Object.keys(m.tuketici).join(", ") || "-"}`);
}
```

```
12 modul dosyasi yazildi; 66 disa acilan sembol, 22 ithal kenari

modul             disa acilan   ithal eden modul
ortakBicim                  9   raporlama, oduncAkisi, subeUcu, ayarlar
subeUcu                    11   raporlama
katalogKoprusu              7   oduncAkisi
oduncAkisi                  8   subeUcu, raporlama, ucretHesabi
ucretHesabi                 4   subeUcu
uyelikKaydi                 3   subeUcu, oduncAkisi
kimlikKoprusu               2   subeUcu, uyelikKaydi
bildirimKuyrugu             5   oduncAkisi
raporlama                   6   subeUcu
depoErisimi                 3   oduncAkisi
olayGunlugu                 4   oduncAkisi
ayarlar                     4   subeUcu, oduncAkisi, raporlama, ucretHesabi
```

## Kuralı Sayıya Çeviren İşlev

Aynı kural iki ayrı sayıya çevrilebilir. **F1 — yüzey büyüklüğü** en ucuz çeviridir: modülün dışa
açtığı sembolleri sayar ve tek dosya okur. **F2 — ölü yüzey oranı** daha pahalıdır: dışa açılan
sembollerin kaç tanesinin hiçbir modül tarafından ithal edilmediğini bulmak için bütün kod tabanını
okur. İkisi de aynı kuralın adayıdır; hangisinin daha iyi olduğu ancak bilinen bir ihlal kümesinde
sınanarak söylenir.

```js
// uygunluk.mjs — bir mimari kurali sayiya ceviren iki uygunluk islevi ve esik taramasi
import { readFileSync, readdirSync } from "node:fs";

const DIZIN = "modul";
const dosya = readdirSync(DIZIN);

// >>> F1  yuzey buyuklugu: modulun kendi dosyasi yeter
function F1(ad) {
  const t = readFileSync(`${DIZIN}/${ad}.mjs`, "utf8");
  return [...t.matchAll(/^export const (\w+)/gm)].length;
}
// <<< F1

// >>> F2  olu yuzey orani: disa acilan sembollerin kac tanesi hic ithal edilmiyor
function F2(ad) {
  const acik = new Set([...readFileSync(`${DIZIN}/${ad}.mjs`, "utf8")
    .matchAll(/^export const (\w+)/gm)].map((m) => m[1]));
  const kullanilan = new Set();
  for (const d of dosya) {
    for (const m of readFileSync(`${DIZIN}/${d}`, "utf8")
      .matchAll(/^import \{ ([^}]+) \} from "\.\/(\w+)\.mjs"/gm)) {
      if (m[2] === ad) for (const s of m[1].split(", ")) kullanilan.add(s);
    }
  }
  return (acik.size - kullanilan.size) / acik.size;
}
// <<< F2

// YN5: mimar on iki modulu elle inceledi ve besini gercek yuzey sizintisi olarak isaretledi.
// subeUcu ve ucretHesabi olu sembol tasiyor; bildirimKuyrugu ve depoErisimi ic ayrintiyi aciyor;
// katalogKoprusu yedi sembolunu tek tuketiciye acarak ic temsilini disari tasiriyor.
const SIZINTI = new Set(["subeUcu", "katalogKoprusu", "ucretHesabi", "bildirimKuyrugu", "depoErisimi"]);
const ad = dosya.map((d) => d.replace(".mjs", ""));

console.log(`${"modul".padEnd(17)}${"F1 yuzey".padStart(10)}${"F2 olu oran".padStart(13)}${"mimar etiketi".padStart(15)}`);
for (const a of ad) {
  console.log(`${a.padEnd(17)}${String(F1(a)).padStart(10)}${F2(a).toFixed(2).padStart(13)}` +
    `${(SIZINTI.has(a) ? "sizinti" : "uygun").padStart(15)}`);
}

function tara(islev, esikler, etiket) {
  console.log(`\n${etiket.padEnd(12)}${"isaretlenen".padStart(13)}${"yakalanan".padStart(11)}` +
    `${"kacan".padStart(7)}${"yanlis alarm".padStart(14)}`);
  for (const e of esikler) {
    const isaret = ad.filter((a) => islev(a) > e);
    const y = isaret.filter((a) => SIZINTI.has(a)).length;
    console.log(`${`esik > ${e}`.padEnd(12)}${String(isaret.length).padStart(13)}` +
      `${`${y}/${SIZINTI.size}`.padStart(11)}${String(SIZINTI.size - y).padStart(7)}` +
      `${String(isaret.length - y).padStart(14)}`);
  }
}
tara(F1, [2, 3, 4, 5, 6, 7, 8, 10], "F1 yuzey");
tara(F2, [0.2, 0.4, 0.5, 0.6, 0.7], "F2 olu oran");

// kuralin kendi maliyeti: satir, dosya okuma, kosum
const kaynak = readFileSync("uygunluk.mjs", "utf8").split("\n");
const satir = (m) => {
  const b = kaynak.findIndex((l) => l.startsWith(`// >>> ${m}`));
  return kaynak.findIndex((l) => l.startsWith(`// <<< ${m}`)) - b - 1;
};
const toplamSatir = ad.reduce((s, a) => s + readFileSync(`${DIZIN}/${a}.mjs`, "utf8").split("\n").length, 0);
console.log(`\n${"islev".padEnd(12)}${"kural satiri".padStart(14)}${"dosya okuma".padStart(13)}` +
  `${"taranan satir".padStart(15)}`);
console.log(`${"F1".padEnd(12)}${String(satir("F1")).padStart(14)}${String(ad.length).padStart(13)}` +
  `${String(toplamSatir).padStart(15)}`);
console.log(`${"F2".padEnd(12)}${String(satir("F2")).padStart(14)}` +
  `${String(ad.length * (1 + dosya.length)).padStart(13)}${String((1 + ad.length) * toplamSatir).padStart(15)}`);
console.log("kosum suresi ortama baglidir; burada ortamdan bagimsiz nicelikler basilir " +
  `(F2 / F1 okuma orani ${(ad.length * (1 + dosya.length)) / ad.length}x)`);
```

```
modul              F1 yuzey  F2 olu oran  mimar etiketi
ayarlar                   4         0.00          uygun
bildirimKuyrugu           5         0.80        sizinti
depoErisimi               3         0.67        sizinti
katalogKoprusu            7         0.00        sizinti
kimlikKoprusu             2         0.00          uygun
oduncAkisi                8         0.13          uygun
olayGunlugu               4         0.75          uygun
ortakBicim                9         0.33          uygun
raporlama                 6         0.00          uygun
subeUcu                  11         0.73        sizinti
ucretHesabi               4         0.75        sizinti
uyelikKaydi               3         0.00          uygun

F1 yuzey      isaretlenen  yakalanan  kacan  yanlis alarm
esik > 2               11        5/5      0             6
esik > 3                9        4/5      1             5
esik > 4                6        3/5      2             3
esik > 5                5        2/5      3             3
esik > 6                4        2/5      3             2
esik > 7                3        1/5      4             2
esik > 8                2        1/5      4             1
esik > 10               1        1/5      4             0

F2 olu oran   isaretlenen  yakalanan  kacan  yanlis alarm
esik > 0.2              6        4/5      1             2
esik > 0.4              5        4/5      1             1
esik > 0.5              5        4/5      1             1
esik > 0.6              5        4/5      1             1
esik > 0.7              4        3/5      2             1

islev         kural satiri  dosya okuma  taranan satir
F1                       4           12            106
F2                      12          156           1378
kosum suresi ortama baglidir; burada ortamdan bagimsiz nicelikler basilir (F2 / F1 okuma orani 13x)
```

## Eşiğin Okunması

F1 taraması eşiğin ne olduğunu çıplak gösterir. En gevşek uçta (`> 2`) beş sızıntının beşi de
yakalanır ve hiçbiri kaçmaz — ama on iki modülün on biri işaretlenir ve bunların altısı yanlış
alarmdır. En sıkı uçta (`> 10`) yanlış alarm sıfırlanır ve dört sızıntı kaçar. Arada hiçbir eşik
yoktur ki hem kaçırma hem yanlış alarm birlikte düşük olsun: `> 4` eşiği üç yakalar, iki kaçırır ve
üç yanlış alarm verir. **F1 kaçırma ile yanlış alarmı birbirinden ayıramaz, çünkü ölçtüğü sayı
kuralın sorduğu şeye yaklaşık bile değildir** — dokuz sembolünün altısı kullanılan `ortakBicim`
ile üç sembolünün ikisi ölü olan `depoErisimi` aynı ekseni paylaşır.

F2 taraması aynı ihlal kümesinde farklı bir yerde durur. `> 0.4` ile `> 0.6` arasında sonuç
değişmez: dört yakalanan, bir kaçan, bir yanlış alarm. Bu düzlük iyi bir işaret sayılır — eşiğin
tam değeri sonucu belirlemiyorsa kural eşiğe aşırı duyarlı değildir. F2'nin tek kaçırdığı
`katalogKoprusu`dur ve nedeni tanımın kendisindedir: yedi sembolünün yedisi de ithal edilir, yani
ölü yüzey sıfırdır; mimarın sızıntı demesinin nedeni sembollerin kullanılmaması değil, hepsinin tek
tüketiciye açılmış olmasıdır. F2'nin tek yanlış alarmı `olayGunlugu`dur: dört sembolünün üçü
grafiğin dışındaki işletim araçları için açıktır ve denetleyici o araçları görmez.

**Kural kodun içinde tanımlanır, kuralın kendisi ise koddan çıkarılamaz.** F2, mimarın etiketine
F1'den çok daha yakındır ama ikisi de yaklaşıktır; hiçbir eşik `katalogKoprusu` ile `olayGunlugu`
çiftini birden doğru sınıflandırmaz.

## Kuralın Kendi Maliyeti

Son tablo kuralın bedelidir. F1 dört satırdır ve tam koşumda on iki dosya, 106 satır okur. F2 on
iki satırdır ve tam koşumda 156 dosya, 1.378 satır okur — her modül için bütün dizini taradığından
okuma **on üç katına** çıkar. Koşum süresi ortama bağlı olduğu için basılan nicelikler ortamdan
bağımsız olanlardır: okunan dosya ve taranan satır. Modül sayısı arttığında F2'nin okuması karesel
büyür, F1'inki doğrusal.

Buradan çıkan sonuç, kuralı ucuzlatmanın yolu ölçüyü bozmadan okuma sayısını düşürmektir: F2 bütün
dosyaları modül başına değil bir kez okuyup ithal haritasını bellekte kurabilir. Ama bu ders için
sayılması gereken asıl bedel koşum değil, **bakımdır**: F2 ithal biçimini ayrıştırdığı için ithal
söz diziminin her yeni biçimi kuralı bozar; F1'in ayrıştırdığı tek şey `export` satırıdır.

## Yanlış Alarmın Ürettiği Davranış

Yanlış alarm bir sayı değil, bir davranıştır. Bir kural yanlış alarm verdiğinde ekip önce inceler,
sonra istisna ekler, sonunda kuralı kapatır. Aşağıdaki blok bunu modeller.

```js
// devre.mjs — yanlis alarmin ekipte urettigi davranis: kural ucuncu alarmda devre disi kalir
import { readFileSync, readdirSync } from "node:fs";

const DIZIN = "modul";
const dosya = readdirSync(DIZIN);
const acikSembol = (a) => [...readFileSync(`${DIZIN}/${a}.mjs`, "utf8")
  .matchAll(/^export const (\w+)/gm)].map((m) => m[1]);
const F1 = (a) => acikSembol(a).length;
const F2 = (a) => {
  const acik = new Set(acikSembol(a));
  const kullanilan = new Set();
  for (const d of dosya) {
    for (const m of readFileSync(`${DIZIN}/${d}`, "utf8")
      .matchAll(/^import \{ ([^}]+) \} from "\.\/(\w+)\.mjs"/gm)) {
      if (m[2] === a) for (const s of m[1].split(", ")) kullanilan.add(s);
    }
  }
  return (acik.size - kullanilan.size) / acik.size;
};
const SIZINTI = new Set(["subeUcu", "katalogKoprusu", "ucretHesabi", "bildirimKuyrugu", "depoErisimi"]);

// YN6: yirmi yayimin her biri tek bir module dokunur ve kural yalniz dokunulan modulde kosar.
// YN7: ekip ucuncu yanlis alarmdan sonra kurali devre disi birakir (esik gevsetme yerine kapatma).
const YAYIM = ["ayarlar", "subeUcu", "oduncAkisi", "raporlama", "ucretHesabi", "ortakBicim",
  "katalogKoprusu", "olayGunlugu", "depoErisimi", "oduncAkisi", "bildirimKuyrugu", "uyelikKaydi",
  "raporlama", "subeUcu", "ayarlar", "kimlikKoprusu", "ucretHesabi", "ortakBicim",
  "depoErisimi", "katalogKoprusu"];

function kosu(islev, esik) {
  let alarm = 0, acik = true, yakalanan = 0, kacan = 0, kapandi = 0, kapandiktanSonra = 0;
  YAYIM.forEach((m, i) => {
    if (!acik) { if (SIZINTI.has(m)) { kacan += 1; kapandiktanSonra += 1; } return; }
    const isaret = islev(m) > esik;
    if (isaret && SIZINTI.has(m)) yakalanan += 1;
    else if (!isaret && SIZINTI.has(m)) kacan += 1;
    else if (isaret) {
      alarm += 1;
      if (alarm === 3) { acik = false; kapandi = i + 1; }
    }
  });
  return { alarm, yakalanan, kacan, kapandi, kapandiktanSonra };
}

const ihlalliYayim = YAYIM.filter((m) => SIZINTI.has(m)).length;
console.log(`${YAYIM.length} yayim, ${ihlalliYayim} tanesi sizintili bir module dokunuyor`);
console.log(`\n${"kural".padEnd(16)}${"yanlis alarm".padStart(14)}${"kapandigi yayim".padStart(17)}` +
  `${"yakalanan".padStart(11)}${"kacan".padStart(7)}${"kapandiktan sonra kacan".padStart(25)}`);
for (const [etiket, islev, esik] of [["F1 esik > 3", F1, 3], ["F2 esik > 0.5", F2, 0.5]]) {
  const r = kosu(islev, esik);
  console.log(`${etiket.padEnd(16)}${String(r.alarm).padStart(14)}` +
    `${(r.kapandi ? String(r.kapandi) : "kapanmadi").padStart(17)}` +
    `${`${r.yakalanan}/${ihlalliYayim}`.padStart(11)}${String(r.kacan).padStart(7)}` +
    `${String(r.kapandiktanSonra).padStart(25)}`);
}

// YN8: cevrilemeyen kural icin yerine konan sey her ucuncu sembolun elle okunmasidir
const sembol = dosya.flatMap((d) => acikSembol(d.replace(".mjs", "")));
const ornek = sembol.filter((_, i) => i % 3 === 2);
console.log(`\ncevrilemeyen kural ("disa acilan ad modulun isini anlatir"): ` +
  `${sembol.length} sembolun ${ornek.length} tanesi elle okunur, ${sembol.length - ornek.length} tanesi okunmaz`);
```

```
20 yayim, 9 tanesi sizintili bir module dokunuyor

kural             yanlis alarm  kapandigi yayim  yakalanan  kacan  kapandiktan sonra kacan
F1 esik > 3                  3                4        1/9      8                        8
F2 esik > 0.5                1        kapanmadi        7/9      2                        0

cevrilemeyen kural ("disa acilan ad modulun isini anlatir"): 66 sembolun 22 tanesi elle okunur, 44 tanesi okunmaz
```

Sonuç, eşik taramasının tek başına anlattığından farklıdır. F1 eşiği `> 3` seçilerek kurulduğunda
tarama tablosuna göre dört sızıntıyı yakalıyordu; yirmi yayımlık koşumda ise **dördüncü yayımda
kapanır** ve kalan on altı yayım boyunca hiçbir şeye bakmaz. Dokuz ihlalli yayımın yalnız biri
yakalanır, sekizi kaçar ve sekizinin sekizi de kural kapandıktan sonradır. F2 aynı koşumda tek
yanlış alarm verir, hiç kapanmaz ve dokuz ihlalli yayımın yedisini yakalar.

Aradaki fark eşik seçiminden değil, **yanlış alarm oranından** doğar. Bir kural kapatıldığı andan
sonra ölçtüğü değer sıfırdır; bu yüzden kuralın kalitesi tek koşumdaki yakalama sayısıyla değil,
ekipte kaç yayım boyunca açık kaldığıyla ölçülür. Kuralın çevrilemeyen kısmı ise adıyla durur: dışa
açılan adın modülün işini anlatıp anlatmadığını hiçbir işlev söylemez ve yerine konan örneklem, 66
sembolün 22'sini okur; 44'ü hiç okunmaz.

## Özet

- Aynı mimari kural iki uygunluk işlevine çevrildi: F1 yüzey büyüklüğü (dışa açılan sembol sayısı),
  F2 ölü yüzey oranı (hiç ithal edilmeyen sembollerin payı); ikisi de gerçek modül dosyalarını okur.
- F1'in eşik taramasında hiçbir eşik iyi değildir: `> 2` beş sızıntının beşini yakalar ve 6 yanlış
  alarm verir, `> 10` yanlış alarmı sıfırlar ve 4 sızıntıyı kaçırır.
- F2 aynı ihlal kümesinde `> 0.4` ile `> 0.6` arasında değişmez (4 yakalanan, 1 kaçan, 1 yanlış
  alarm) ve eşiğe duyarsızlık kuralın sağlamlık işaretidir; iki hatası da tanımdan doğar —
  `katalogKoprusu`nu kaçırır (yedi sembolü de kullanılıyor), `olayGunlugu`na yanlış alarm verir
  (üç sembolü grafiğin dışındaki araçlar için açık).
- Kuralın bedeli F1 için 4 satır, 12 dosya ve 106 satır okuma; F2 için 12 satır, 156 dosya ve
  1.378 satır okumadır; fark 13 kattır ve modül sayısıyla karesel büyür.
- Yirmi yayımlık koşumda F1 üçüncü yanlış alarmında dördüncü yayımda kapanır ve 9 ihlalli yayımın
  8'ini kaçırır; F2 tek yanlış alarmla açık kalır ve 7'sini yakalar.
- Çevrilemeyen kural adıyla yazıldı ("dışa açılan ad modülün işini anlatır") ve yerine konan
  örneklem 66 sembolün 22'sini okur.

## Sonraki Adım

Bu dersin uygunluk işlevi tek bir modülün yüzeyine baktı ve komşularını yalnız sembol sayarken
kullandı. Mimari kuralların bir bölümü ise tek modülde hiç görünmez: hangi modülün hangi modülü
ithal edebileceği, kod tabanının **grafiğinde** durur ve ancak grafiğin tamamı okunarak
denetlenebilir. Sonraki ders bu kuralı yazar: gerçek bir ithal grafiği üzerinde koşan bir katman
denetleyicisi kurulur ve asıl soru denetleyicinin değil kuralın kendisidir — aynı katman kuralı
izin listesi biçiminde de yasak listesi biçiminde de yazılabilir. İki biçim aynı grafikte
koşturulur ve üç sayı karşılaştırılır: hangisi daha az yanlış alarm veriyor, hangisi kaç ihlali
kaçırıyor ve kod tabanına yeni bir modül eklendiğinde kural kaç yerde güncelleniyor.
