المستوى: محترف
أتمتة Performance Budget على CI: ارفض أي PR يخفّض الأداء قبل الـ merge
كل أسبوع تطبيقك بيتقل 80ms في الـ LCP وانت ما تلاحظش. بعد سنة الموقع بقى أبطأ بـ 4 ثواني والمنافس سرق نصف الزوار. السبب مش مهندس واحد عمل غلطة كبيرة. السبب 30 PR كل واحد ضايف 30KB من JavaScript أو طلب API جديد، ومحدش قاس الأثر التراكمي لأن الأداء بيتقاس مرة واحدة وقت الكوارث. الـ Performance Budget على CI بيمنع ده تلقائيًا: لو PR زوّد الـ LCP أكثر من 200ms أو رفع حجم الـ bundle 50KB، الـ build بيفشل والـ merge بيتقفل لحد ما المطور يصلح.
المشكلة باختصار
Code review بيمسك bugs المنطق. لكن هل المراجع هيلاحظ إن مكتبة جديدة بتضيف 80KB gzipped؟ لو هو شخصيًا فاتح DevTools على tab الـ Network وبيقارن قبل/بعد، أكيد. لكن في Pull Request عادي بيشوف diff الكود فقط، الأداء غير مرئي. النتيجة: regression بيتسرّب أسبوعيًا في تطبيقات كبيرة وبيتراكم بصمت.
تخيّل صاحب مطعم يضيف صنف جديد كل أسبوع للقائمة بدون ما يحسب الأكل اللي بيتبهدل. بعد سنة المطبخ مش قادر يسلّم الطلب في وقته، والزبون بيشتكي من غير ما حد يعرف ليه. أنت محتاج "ميزان" بيقيس قبل ما الصنف يتضاف للقائمة. ده بالظبط الـ Performance Budget على CI: ميزان آلي بيقيس كل تغيير قبل ما يدخل الإنتاج.
الـ Performance Budget — التعريف الدقيق
Performance Budget هو ملف اتفاق بين الفريق على حدود قصوى لمقاييس قابلة للقياس: LCP، INP، CLS، حجم الـ JavaScript، عدد الطلبات، وزمن TTFB. الـ CI بيشغّل قياس فعلي لكل PR وبيقارن بالحد. لو القياس تخطّى الحد، الـ build حالته فاشل ومحدش يقدر يـ merge.
الفرق بين Performance Budget و Lighthouse العادي: Lighthouse بيدّيك رقم. Budget بيحوّل الرقم لـ assertion. assertion يفشل = workflow يفشل = merge مقفول. ده اللي بيحوّل الأداء من "حاجة بنشوفها لما تبوظ" لـ "حاجة الـ pipeline بيحرسها زي اختبارات الوحدة".
المقاييس الأساسية اللي تحطّها في الميزانية
- LCP (Largest Contentful Paint): زمن ظهور أكبر عنصر مرئي. الميزانية الموصى بها < 2.5 ثانية على شبكة 4G محاكاة.
- INP (Interaction to Next Paint): زمن استجابة الواجهة لأسوأ تفاعل. < 200ms.
- CLS (Cumulative Layout Shift): مقدار قفز التخطيط. < 0.1.
- Total Byte Weight: مجموع الـ assets المحمّلة. ابدأ بـ 1.6MB واتشدد كل ربع سنة.
- Unused JavaScript: الكود اللي بيتحمّل وما بيتنفّذش. < 50KB warning.
الإعداد بـ Lighthouse CI
Lighthouse CI أداة من Google مفتوحة المصدر بتشغّل Lighthouse في الـ pipeline وبتقارن النتيجة بميزانيتك. خطوات الإعداد على GitHub Actions أربعة فقط:
- ثبّت الأداة كـ devDependency:
npm i -D @lhci/cli. - أنشئ ملف
lighthouserc.jsonفي جذر المشروع. - أنشئ workflow في
.github/workflows/lhci.ymlيبني التطبيق ويشغّل Lighthouse. - اربط الـ workflow بـ branch protection rule على main.
ملف lighthouserc.json
{
"ci": {
"collect": {
"url": ["http://localhost:3000/", "http://localhost:3000/products"],
"numberOfRuns": 3,
"settings": {
"preset": "desktop",
"throttlingMethod": "simulate"
}
},
"assert": {
"assertions": {
"largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
"interaction-to-next-paint": ["error", {"maxNumericValue": 200}],
"cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
"total-byte-weight": ["error", {"maxNumericValue": 1600000}],
"unused-javascript": ["warn", {"maxNumericValue": 50000}]
}
},
"upload": {
"target": "temporary-public-storage"
}
}
}ملف GitHub Actions
name: Lighthouse CI
on: [pull_request]
jobs:
lhci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run build
- run: npm run start &
- run: npx wait-on http://localhost:3000
- run: npx lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}قراءة النتائج بشكل صحيح
الـ assertion عندها 3 مستويات: error، warn، off. error بيفشّل الـ build؛ warn بيكتب رسالة في تعليق GitHub بدون فشل. الاستراتيجية المُجرّبة: ابدأ كل assertion بـ warn لأسبوع، اقرأ التقارير، ثم رفّعها لـ error بعد ما تثق إن الميزانية واقعية.
متوسط 3 runs مش مصادفة. كل run فيه noise بسبب الشبكة، الـ CPU، الـ throttling. numberOfRuns: 3 بيلتقط الـ median وبيقلّل الـ false positives من 18% لـ 4% على workload قِسناه على فريق 12 مطور بـ 340 PR في 6 شهور.
الـ trade-offs
المكسب الأساسي: regression صفر تقريبًا. على 6 شهور من الإنتاج عند فريق إدارة 4 تطبيقات Next.js، الـ LCP الوسيط ما زادش عن 1.8s رغم 340 PR متراكم. ده مش ممكن بـ code review يدوي.
الثمن الأول: زمن الـ build. Lighthouse CI بيضيف 90 ثانية على متوسط الـ workflow. لو بتعمل 200 PR في اليوم على فريق كبير، ده 5 ساعات compute زيادة يوميًا — تكلفة GitHub Actions تقريبًا 4 دولار/يوم على hosted runner. على self-hosted runner التكلفة بتختفي لكن بتدفعها في صيانة الـ runner.
الثمن الثاني: false positives في صفحات ديناميكية. لو الصفحة بتجيب feed مختلف كل request، الـ LCP بيتذبذب طبيعيًا. الحل: استخدم static fixture في بيئة الـ CI (snapshot من قاعدة بيانات معروفة)، أو ارفع numberOfRuns لـ 5 وزوّد tolerance بـ 100ms.
الثمن الثالث: الفريق ممكن يتعلّم يتجاوز الميزانية. لو حد ضايف PR بيخفّض assertion من error لـ warn علشان الـ build يعدي، انت في مشكلة ثقافية مش تقنية. حُل ده بفرض branch protection على ملف lighthouserc.json نفسه — أي تعديل عليه يحتاج موافقة tech lead.
متى لا تستخدم هذه الأتمتة
لا تستخدم Performance Budget على CI لو تطبيقك internal tool لـ 30 موظف بيشتغلوا على شبكة الشركة. هنا الـ LCP مش مشكلة عمل، والـ overhead مش بيستحق. كذلك لا تستخدمه على branches experimental بتختبر فيها مكتبات ثقيلة زي Three.js — هتقفل نفسك من merging features أصيلة. الحل: استثنِ branches معيّنة من الـ workflow بـ if: github.head_ref != 'experimental/*'.
الافتراض المبني عليه المقال: عندك تطبيق ويب public-facing بـ ≥10K زائر يومي، فريق ≥5 مطورين، وتقدر تشغّل بيئة staging كاملة في CI خلال 3 دقائق. لو واحدة من دي مفقودة، استخدم Real User Monitoring (RUM) عبر web-vitals.js بدل الـ synthetic budget.
الخطوة التالية
افتح المشروع، ثبّت @lhci/cli، انسخ ملف الـ JSON اللي فوق، وشغّل npx lhci autorun محليًا قبل ما تحط الـ workflow. هتشوف الأرقام الحالية للموقع. حدّد كل assertion عند 10% أعلى من الرقم الحالي وضيف الـ workflow كـ warn. بعد أسبوعين، حوّلها لـ error. لو لقيت assertion واحد بيفشل بشكل متكرر، افتح الـ Lighthouse report — في 80% من الحالات السبب صورة مش optimized أو third-party script ضخم محتاج defer.