الفصل الثالث عشر: الصيانة والتطوير المستمر Chapter 13: Maintenance & Continuous Improvement
كيف نحافظ على استقرار التطبيق ونضيف مميزات جديدة دون إزعاج المستخدم الحالي.
برمجة التطبيق هي مجرد البداية. الحياة الحقيقية للتطبيق بتبدأ لما ينزل للمستخدمين. في الفصل ده هنتعلم إزاي ندير "حياة التطبيق" (App Lifecycle) باحترافية.
استلام تقارير الأخطاء من المستخدمين ودمجها في التحديث القادم.
تحديث المكتبات (Packages) لمواجهة التغييرات في أنظمة أندرويد و iOS.
إضافة مميزات جديدة بناءً على طلبات المستخدمين الحقيقية.
إحنا بنتبع نظام MAJOR.MINOR.PATCH في MRE CashBook عشان الفريق والمستخدمين يعرفوا حجم التغيير:
| Part | Change Type | Example |
|---|---|---|
| MAJOR | تغيير جذري أو إعادة تصميم كاملة (Breaking Changes). | 2.0.0 |
| MINOR | إضافة مميزات جديدة بدون كسر المميزات القديمة. | 1.1.0 |
| PATCH | تصليح أخطاء (Bugs) أو تحسينات بسيطة. | 1.0.5 |
version: 1.2.3+14 // 1.2.3 (Version) + 14 (Build Number)
تنبيه: الـ Build Number لازم يزيد مع كل رفعة على المتجر، حتى لو الـ Version ثابت.
مش كل التحديثات زي بعضها. إحنا بنقسمها لنوعين:
لو في مشكلة أمنية أو تغيير في الداتابيز، بنمنع المستخدم يكمل غير لما يحدث.
لو مميزات جديدة، بنظهر له Banner لطيف "توجد نسخة جديدة متاحة".
واحدة من أقوى الأدوات في مشروعنا هي Shorebird. بتسمح لنا نصلح الـ Bugs لحظياً بدون ما المستخدم يروح المتجر أو يحمل تحديث كبير.
shorebird patch android --release-version 1.2.3
ليش بنستخدم Shorebird؟ لأن مراجعة أبل وجوجل للتطبيقات ممكن تاخد أيام. مع Shorebird، بنصلح الكراش في 5 دقايق بس.
إحنا مش بنستخدم print العادية. إحنا عندنا AppLogger خاص بينا بيسهل علينا تتبع المشاكل:
AppLogger.info("User opened Settings screen");
AppLogger.warning("Low storage detected");
AppLogger.error("Failed to save transaction", error: e, stack: stack);
الميزة: الـ Logger بيقدر يفرق بين وضع الـ Debug (بيظهر في الـ Console) ووضع الـ Release (بيبعت لـ Crashlytics).
عشان نتأكد إن التطبيق سريع وسلس، بنستخدم الـ Flutter DevTools لمراقبة الـ CPU والـ Memory:
التأكد إن الـ Frames بتترسم في أقل من 16ms لضمان تجربة 60 FPS.
مراقبة الـ Heap لضمان إن التطبيق مبيستهلكش رام بمرور الوقت بسبب الـ Controllers اللي متقفلتش.
PRO TIP: استخدم debugPrintStack() لو عاوز تعرف مين اللي بينادي على Function معينة وبياخد وقت طويل.
لما بنحتاج نضيف Column جديد في الداتابيز، لازم نعمل Migration عشان بيانات المستخدمين القديمة ما تتمسحش:
// AppDatabase migration logic
MigrationStrategy get migration => MigrationStrategy(
onUpgrade: (m, from, to) async {
if (from < 2) {
await m.addColumn(transactions, transactions.categoryEmoji);
}
},
);
التطوير مش بيوقف. دي خطتنا للمستقبل:
مزامنة البيانات بين الموبايل والتابلت والويب بشكل لحظي.
مساعد ذكي يحلل مصاريفك ويديك نصائح لتقليل الإنفاق.
| Task | Frequency | Importance |
|---|---|---|
| تحديث الـ SDK والـ Packages | شهرياً | High |
| مراجعة الكراشات في Crashlytics | أسبوعياً | Critical |
| تنظيف الكود (Refactoring) | كل 3 شهور | Medium |
| نسخ احتياطي للداتابيز التجريبية | يومياً (تلقائي) | High |
| Term | Meaning (AR) |
|---|---|
| Cold Boot | تشغيل التطبيق من الصفر (أول مرة بعد ما اتقفل). |
| UI Jitter | تقطيع في الـ UI بسبب سقوط الـ Frames. |
| Regression | خلل ظهر في ميزة قديمة بسبب تعديل جديد. |
المكتبات (Packages) بتتحدث بسرعة. عشان نعرف إيه اللي محتاج تحديث، بنستخدم الكوماند ده:
flutter pub outdated
| Status | Meaning | Action |
|---|---|---|
| Current | النسخة اللي إنت شغال بيها دلوقتي. | - |
| Upgradable | أحدث نسخة متوافقة مع الـ pubspec بتاعك. | flutter pub upgrade |
| Resolvable | أحدث نسخة موجودة (ممكن تحتاج تعديل كود يدوي). | Update manually |
تنبيه: بلاش تستخدم any في إصدارات المكتبات. دايماً ثبت النسخة بـ ^ عشان تتجنب الـ Breaking Changes المفاجئة.
في MRE CashBook، إحنا بنهتم إن المستخدم يعرف إيه الجديد. بدل ما نكتب يدوي، بنستخدم الـ Git Commits:
feat: add backup to Google Drive
fix: resolve crash in reports screen
perf: optimize database queries
باستخدام Conventional Commits، بنقدر نولد ملف CHANGELOG.md تلقائياً بضغطة زرار.
إزاي نعرف إن مجهود الصيانة بتاعنا ناجح؟ بنبص على الـ KPIs دي:
PRO TIP: لو الـ Crash-Free Users نزل عن 98%، دي حالة طوارئ (Severity 1) ولازم نوقف التطوير ونركز في التصليح.
إزاي بنتعامل مع Bug طارئ في الـ Prod؟
اكتشاف الـ Bug عن طريق Crashlytics Alert.
كتابة Test Case بتفشل (Red) عشان نتأكد إننا فهمنا المشكلة.
تصليح الكود وعمل shorebird patch للتحديث اللحظي.
قبل ما نرفع النسخة لكل الناس على المتجر، لازم نجربها داخلياً. بنستخدم Firebase App Distribution:
بنعمل نسخة Release من التطبيق.
بنرفع الملف وبنضيف إيميلات المختارين (Testers).
المختبرين بيقدروا يبعتوا سكرين شوت ووصف للمشكلة لو لقوا Bug.
الصيانة غالباً بتضمن تعديل في الـ Cubit أو الـ Bloc. لازم نضمن إن الـ UI بيفضل مستقر:
// AppSettingsCubit example
void toggleDarkMode() {
final newState = state.copyWith(isDarkMode: !state.isDarkMode);
emit(newState);
_saveToPrefs(newState.isDarkMode);
}
PRO TIP: دايماً استخدم copyWith عشان تحافظ على باقي الـ Variables في الـ State وما تضيعش معلومات تانية بالصدفة.
سنقوم ببناء هذا الفصل لنغطي كل جوانب استدامة المشروع.