التقييم
معادلة Clean Code Score، والتقليص البايزي، ومستويات الثقة، والترتيب، ومثال محسوب
هذا الملف يشرح بالضبط كيف تُحسب Clean Code Score. الكود موجود في src/scoring/؛ وكل ثابت موجود في scoringConfig.ts.
المدخلات لكل مطوّر
من محرّك النسب (07):
analyzedLines: الأسطر التي أضافها أو غيّرها في الملفات القابلة للتحليل، ضمن الـ commits المحللة؛weighted[category]: مجموع أوزان خطورة المخالفات التي أدخلها بثقةhigh/medium، للفئاتduplicationوstructureوhygiene.
فقط المخالفات التي أُدخلت تُحسب. المخالفات الموجودة سابقاً والمُصلحة والمستثناة وغير المنسوبة لا تخفض الدرجة أبداً.
المعادلة
لكل فئة:
kloc = analyzedLines / 1000
adjusted(cat) = ( weighted(cat) + projectDensity(cat) × priorKLOC )
/ ( kloc + priorKLOC )
subScore(cat) = 100 × e^( −adjusted(cat) / scale(cat) )ثم:
score = subScore(duplication) × 0.30
+ subScore(structure) × 0.45
+ subScore(hygiene) × 0.25النتيجة تُحصر بين 0 و100 وتُقرَّب. والقيمة غير المقرّبة تُحفظ في scoreExact.
لماذا الكثافة؟
الأعداد الخام للمخالفات تعاقب المطورين المنتجين: من يكتب 20,000 سطر ستكون لديه مخالفات أكثر ممن يكتب 500. القسمة على KLOC تقيس مدى نظافة الكود الذي يكتبه، وليس كميته.
لماذا الدالة الأسية؟
e^(−density / scale) تحوّل أي كثافة ≥ 0 إلى قيمة بين 100 و0:
- الكثافة 0 ← 100؛
- الكثافة =
scale← حوالي 37 (مضاعف "e" واحد)؛ - تقترب من 0 بسلاسة ولا تصبح سالبة أبداً.
كل وحدة إضافية من الكثافة تكلّف نقاطاً أقل من التي قبلها، فلا يستطيع ملف واحد سيئ جداً أن يُنزل الدرجة إلى الصفر.
لماذا التقليص البايزي؟
بدونه، المطوّر الذي كتب 50 سطراً نظيفاً سيحصل على 100 كاملة، ومن كتب 50 سطراً فيها سرّ واحد سيحصل على قريب من 0. ولا واحدة من النتيجتين تعني الكثير.
التقليص يضيف لكل مطوّر priorKLOC (= 1 KLOC) من "الكود الافتراضي" بكثافة متوسط المشروع:
- المطوّر الذي كتب كوداً قليلاً يُسحب بقوة نحو متوسط المشروع؛
- المطوّر الذي كتب كوداً كثيراً بالكاد يتأثر؛ بياناته هو هي الغالبة.
كثافة المشروع لكل فئة:
projectDensity(cat) = Σ over all developers of weighted(cat)
/ (total analyzed lines of all commits / 1000)إذا كان المشروع كله نظيفاً، تكون القيمة المسبقة 0 ولا توجد قسمة على صفر.
الثوابت
| الثابت | الافتراضي | المعنى | قابل للضبط من ملف الإعدادات؟ |
|---|---|---|---|
SEVERITY_WEIGHT | low 1، medium 3، high 5، critical 8 | وزن كل مخالفة | انظر الملاحظة أدناه |
CATEGORY_SCALE | duplication 15، structure 45، hygiene 38 | الكثافة التي تنخفض عندها الدرجة الفرعية إلى حوالي 37 | ✅ scoring.categoryScales |
CATEGORY_WEIGHT | duplication 0.30، structure 0.45، hygiene 0.25 | حصة كل درجة فرعية في الدرجة النهائية | ✅ scoring.weights |
PRIOR_KLOC | 1 | قوة التقليص | ❌ من الكود فقط |
DEFAULT_MIN_LINES_FOR_RANKING | 1000 | الأسطر المطلوبة لدخول الترتيب | ✅ scoring.minLinesForRanking |
ENGINE_VERSION | 2.0.0 | مفتاح إلغاء الذاكرة المؤقتة | ❌ من الكود فقط |
المقاييس مُعايَرة على مستودعات حقيقية (CleanLens نفسها، ومستودع Django + React موحّد، وأداتا سطر أوامر) بحيث يحصل الكود المُعتنى به، مع القواعد الافتراضية، على حوالي 70–85، والكود الذي عليه دَين تقني حقيقي على حوالي 40–55.
أوزان الخطورة
ماذا يعني المقياس عملياً
الكثافة (نقاط موزونة لكل KLOC) التي تصل عندها الدرجة الفرعية إلى قيمة معينة:
| الدرجة الفرعية | duplication (مقياس 15) | structure (مقياس 45) | hygiene (مقياس 38) |
|---|---|---|---|
| 90 | 1.6 | 4.7 | 4.0 |
| 80 | 3.3 | 10.0 | 8.5 |
| 70 | 5.4 | 16.1 | 13.6 |
| 50 | 10.4 | 31.2 | 26.3 |
| 37 | 15 | 45 | 38 |
المعادلة: density = −scale × ln(subScore / 100).
مثلاً، درجة structure 80 تسمح بحوالي 10 نقاط موزونة لكل 1,000 سطر، أي تقريباً ثلاث نتائج بنية بخطورة medium (3 × 3 = 9) لكل KLOC.
مثال محسوب
متوسطات المشروع: duplication 4، وstructure 20، وhygiene 8 نقاط موزونة لكل KLOC.
المطوّرة Alice كتبت 2,000 سطر (2 KLOC) وأدخلت:
- duplication: نسختان مكررتان بخطورة medium ← 6 نقاط
- structure: عشر نتائج medium ← 30 نقطة
- hygiene: نتيجة low واحدة + ثلاث medium ← 10 نقاط
duplication: adjusted = (6 + 4×1) / (2 + 1) = 3.33 → 100·e^(−3.33/15) = 80.1
structure: adjusted = (30 + 20×1)/ (2 + 1) = 16.67 → 100·e^(−16.67/45) = 69.1
hygiene: adjusted = (10 + 8×1) / (2 + 1) = 6.00 → 100·e^(−6/38) = 85.4
score = 80.1×0.30 + 69.1×0.45 + 85.4×0.25 = 24.0 + 31.1 + 21.3 = 76.4 → 76يعرض التقرير 76/100 (duplication 80 · structure 69 · hygiene 85). الدرجات الفرعية تخبر Alice أن تركز على البنية.
مستويات ثقة الدرجة
مبنية على analyzedLines فقط:
| المستوى | الأسطر المحللة | كيف تقرؤه |
|---|---|---|
insufficient | أقل من 300 | غالباً هي متوسط المشروع؛ لا تقارن |
provisional | 300 – 999 | استرشادية فقط |
reliable | 1,000 – 4,999 | مقبولة للمقارنة |
highly_reliable | 5,000 أو أكثر | مؤشر قوي |
هذه الحدود موجودة في confidenceLevel() ولا يمكن تغييرها إلا من الكود.
الترتيب
rankable= المطوّر لديه درجة وanalyzedLines ≥ minLinesForRanking(افتراضياً 1,000).- المطورون غير القابلين للترتيب يحصلون على درجة، لكنهم يُعرضون منفصلين ("Not ranked — insufficient analysed code").
- المطوّر الذي لديه 0 أسطر محللة يحصل على
score = null("no analysed code")، وهذا يختلف عن الدرجة الكاملة. - الترتيب (في سطر الأوامر وMarkdown ولوحة المعلومات): المطورون القابلون للترتيب حسب الدرجة، الأدنى أولاً، ثم غير القابلين للترتيب، ثم من ليست لهم درجة.
أرقام أخرى لكل مطوّر (للعرض فقط)
| الحقل | المعادلة | في الدرجة؟ |
|---|---|---|
violationDensity | مجموع النقاط الموزونة ÷ KLOC، قبل التقليص | لا |
contributionPercent | أسطر المطوّر المحللة ÷ كل الأسطر المحللة | لا — تُعرض منفصلة عن قصد |
netQualityImpact | الموزون المُدخل − الموزون المُصلح | لا (الموجب = أضاف دَيناً أكثر مما أزال) |
activeDays، وأول/آخر مساهمة | من git log | لا |
الاختبارات
test/scoring.test.ts يتحقق من: null عند صفر أسطر؛ والعينة الصغيرة النظيفة تأخذ درجة أقل من العينة الكبيرة النظيفة؛ والحجم لا يُعاقَب؛ وفئة واحدة تنخفض باستقلال؛ والدرجات تبقى بين 0 و100؛ ولا قسمة على صفر؛ والأوزان 0.30/0.45/0.25؛ وحدود الثقة؛ وحد ترتيب قابل للضبط.