> ## Content Index
> Fetch the complete content index at: https://arabroot.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# أسئلة مقابلة مطور تطبيقات الجوال من المبتدئ إلى الخبير
- URL: https://arabroot.io/articles/اسئلة-مقابلة-مطور-تطبيقات-الجوال-من-المبتدئ-الى-الخبير/
- Published: 2026-10-13T05:30:00.000Z
- Updated: 2026-10-12T21:41:03.000Z
- Description: بنك أسئلة لمقابلة مطور تطبيقات الجوال على Android وiOS، لكل سؤال إجابة المبتدئ وإجابة الخبير، مع تمارين عملية شغلناها.
- Author: فريق عرب رووت
- Tags: الأدلة التقنية, الفرق والمنتج, استراتيجية تقنية, الاستضافة الذاتية

مطور الجوال يعمل في بيئة لا يتحكم فيها: النظام يقتل تطبيقه في الخلفية دون إذن، والشبكة تنقطع في المصعد، والمستخدم يدير الشاشة في منتصف الدفع، والإصدار القديم يبقى في أيدي الناس لأشهر، والمتجر يرفض الإصلاح العاجل لأن وصف الخصوصية ناقص. والمقابلة التي تسأل عن الفرق بين `val` و`var` أو عن آخر ميزة في SwiftUI لا تكشف من يعرف هذه البيئة ومن لم يعشها بعد.

وفي [دليل إدارة المقابلة التقنية](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%AF%D9%8A%D8%B1-%D9%85%D9%82%D8%A7%D8%A8%D9%84%D8%A9-%D8%AA%D9%82%D9%86%D9%8A%D8%A9-%D8%AA%D9%83%D8%B4%D9%81-%D9%85%D8%B3%D8%AA%D9%88%D9%89-%D8%A7%D9%84%D9%85%D8%B1%D8%B4%D8%AD/) شرحنا الطريقة: المستويات، وسلم أسئلة المتابعة، والتقييم بالدرجات، وهذا المقال هو بنك الأسئلة لمطور تطبيقات الجوال، وفيه 43 سؤالاً تغطي Android وiOS والتطبيقات متعددة المنصات، ولكل سؤال إجابة المبتدئ وإجابة الخبير وأسئلة متابعة وعلامات تحذير ورابط للمقال الذي يشرح الموضوع، ثم ثلاثة تمارين عملية شغلناها فعلاً.

وسوف نناقش في هذا المقال ما يلي:

- كيف تستخدم بنك الأسئلة في مقابلة واحدة.
- الأسئلة مرتبة حسب المجال، من دورة حياة التطبيق إلى النشر في المتاجر واختيار التقنية.
- ثلاثة تمارين عملية: تجمد الواجهة بعمل حسابي، وشاشة لا تغادر الذاكرة، وتعارض تعديلين دون اتصال.

## كيف تستخدم هذه الأسئلة؟

لا تسأل كل الأسئلة، فهذا بنك تختار منه، والقاعدة أن تأخذ لكل جلسة مدتها ساعة ثمانية أسئلة تقريباً من ثلاثة أو أربعة مجالات مع تمرين عملي واحد، وتترك الوقت لأسئلة المتابعة. ولكل سؤال مستوى (مبتدئ أو متوسط أو خبير) يبين أين يكون السؤال في محله، وليس المستوى الوحيد الذي تسأله له.

وأغلب الأسئلة مكتوبة بأمثلة من Android وiOS معاً، فإذا كان المرشح يعمل على منصة واحدة أو على Flutter أو React Native فاسأله عن نفس الفكرة في منصته، ولا تخصم منه لأنه لا يعرف اسم الواجهة البرمجية في المنصة الأخرى، فالمهم أن يعرف لماذا يقتل النظام التطبيق في الخلفية، وليس اسم الدالة التي تحفظ الحالة في كل منصة. وفي كل بطاقة: «ماذا يختبر»، و«إجابة المبتدئ» وهي ما نقبله من مبتدئ جيد، و«إجابة الخبير»، و«أسئلة المتابعة»، و«علامات التحذير»، ورابط المقال الذي يشرح الموضوع.

والمجالات في هذا المقال:

- [دورة حياة التطبيق في Android وiOS](#lifecycle) (4 أسئلة)
- [الخيط الرئيسي والتزامن](#threading) (4 أسئلة)
- [العمل دون اتصال والمزامنة](#offline) (3 أسئلة)
- [التخزين المحلي والتشفير](#storage) (3 أسئلة)
- [الشبكة والأمن في الاتصال](#network) (3 أسئلة)
- [الإشعارات Push Notifications](#push) (3 أسئلة)
- [الروابط العميقة Deep Links](#deep-links) (سؤالان)
- [الأداء والبطارية وحجم التطبيق](#performance) (4 أسئلة)
- [الأعطال وANR وتسرب الذاكرة](#crashes) (3 أسئلة)
- [الإصدارات والنشر في المتاجر](#release) (3 أسئلة)
- [اختيار التقنية بين الأصلي ومتعدد المنصات](#platforms) (3 أسئلة)
- [سهولة الوصول والعربية](#a11y-rtl) (3 أسئلة)
- [الاختبارات](#testing) (سؤالان)
- [الأمن في تطبيقات الجوال](#security) (سؤالان)
- [مراجعة كود الذكاء الاصطناعي](#ai-code) (سؤال واحد)
- [تمارين عملية للمقابلة](#exercises)
- [الخلاصة](#summary)

## دورة حياة التطبيق في Android وiOS

### 1\. المستخدم أدار الشاشة فاختفى ما كتبه في النموذج، لماذا؟

مبتدئ **ماذا يختبر:** دورة حياة ال Activity في Android وتغيير الإعدادات Configuration Change.

إجابة المبتدئ

لأن Android يعيد إنشاء الشاشة عند الدوران، فنحفظ البيانات في ViewModel.

إجابة الخبير

عند تغيير الإعدادات (الدوران، أو اللغة، أو الوضع الليلي، أو حجم النافذة في الأجهزة القابلة للطي) يهدم النظام ال Activity وينشئها من جديد، فكل ما في حقولها يضيع. وال ViewModel يبقى خلال تغيير الإعدادات، ولكنه لا يبقى إذا قتل النظام العملية Process Death وهي في الخلفية لتوفير الذاكرة، وهنا يحتاج ما كتبه المستخدم إلى `SavedStateHandle` أو `rememberSaveable` في Compose، وما يجب أن يبقى فعلاً (مسودة رسالة) إلى قاعدة البيانات المحلية. وأختبر ذلك بتفعيل «Don't keep activities» في خيارات المطور، أو بقتل العملية من `adb` والتطبيق في الخلفية.

أسئلة المتابعة
- ما الفرق بين ما يبقى في ViewModel وما يبقى في SavedState؟
- كيف تختبر موت العملية Process Death بنفسك؟
- لماذا تعطيل الدوران في ال Manifest ليس حلاً؟

**علامات تحذير:** يقفل الشاشة على الوضع العمودي ليحل المشكلة.

**المقال الذي يشرح الموضوع:** [اختبار تطبيقات الجوال مع Appium وMaestro](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AC%D9%88%D8%A7%D9%84-%D9%85%D8%B9-appium-%D9%88-maestro/).

### 2\. ماذا يحدث لتطبيق iOS عندما ينتقل إلى الخلفية؟

متوسط **ماذا يختبر:** حالات التطبيق في iOS وحدود العمل في الخلفية.

إجابة المبتدئ

يتوقف التطبيق مؤقتاً، وعندما يعود المستخدم يكمل من حيث توقف.

إجابة الخبير

التطبيق ينتقل من نشط Active إلى غير نشط ثم إلى الخلفية Background ثم يعلق Suspended بعد وقت قصير، وفي حالة التعليق لا ينفذ أي كود، وقد ينهيه النظام دون أي إشعار إذا احتاج الذاكرة. لذلك عند الانتقال إلى الخلفية أحفظ الحالة المهمة فوراً، وإذا كان هناك عمل يجب أن يكتمل (رفع صورة) أطلب وقتاً إضافياً محدوداً أو أستخدم جلسة URLSession في الخلفية التي يكمل النظام نقلها حتى لو علق التطبيق، وللعمل الدوري BGTaskScheduler الذي يقرر النظام متى يشغله بحسب البطارية والاستخدام. ولا أعد المستخدم بأن شيئاً سيحدث في الخلفية في وقت محدد.

أسئلة المتابعة
- كيف ترفع ملف فيديو كبيراً ويكتمل الرفع والتطبيق في الخلفية؟
- لماذا قد لا تعمل المهمة الدورية في الخلفية لأيام؟

**علامات تحذير:** يفترض أن التطبيق يستمر في العمل في الخلفية مثل خدمة على السيرفر.

**المقال الذي يشرح الموضوع:** [كيف تعمل الإشعارات الفورية Push Notifications؟](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%B9%D9%85%D9%84-%D8%A7%D9%84%D8%A7%D8%B4%D8%B9%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%81%D9%88%D8%B1%D9%8A%D8%A9-push-notifications/).

### 3\. كيف تزامن البيانات كل ساعة في Android دون أن تستنزف البطارية؟

متوسط **ماذا يختبر:** العمل في الخلفية وقيود النظام الحديثة.

إجابة المبتدئ

نستخدم Service يعمل في الخلفية ومؤقتاً كل ساعة.

إجابة الخبير

الخدمات الدائمة في الخلفية مقيدة بشدة في إصدارات Android الحديثة، والطريقة الموصى بها للعمل المؤجل الذي يجب أن يحدث هي WorkManager: أطلب عملاً دورياً مع قيود (شبكة متاحة، وشحن غير منخفض)، والنظام يجمع الأعمال من التطبيقات ويشغلها في نوافذ الصيانة Doze، فيضمن التنفيذ ولا يضمن الدقيقة. وإذا كان المستخدم يحتاج أن يعرف أن شيئاً يعمل الآن (تتبع مسار التوصيل، أو تشغيل صوت) فهذه خدمة أمامية Foreground Service بإشعار ظاهر ونوع مصرح به. والأفضل غالباً أن يبلغ السيرفر التطبيق بإشعار صامت عندما توجد بيانات جديدة بدلاً من السؤال كل ساعة.

أسئلة المتابعة
- ما الفرق بين WorkManager والخدمة الأمامية، ومتى تحتاج كلاً منهما؟
- ماذا يفعل وضع Doze بالمؤقتات والشبكة؟

**علامات تحذير:** يقترح خدمة دائمة أو AlarmManager دقيقاً لمزامنة عادية.

**مقالات تشرح الموضوع:** [كيف تبني نظام إشعارات يصل إلى ملايين المستخدمين](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D8%B4%D8%B9%D8%A7%D8%B1%D8%A7%D8%AA-%D9%8A%D8%B5%D9%84-%D8%A7%D9%84%D9%89-%D9%85%D9%84%D8%A7%D9%8A%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D9%8A%D9%86/)، و[تتبع مواقع المندوبين لحظياً: من الهاتف إلى الخريطة](https://arabroot.io/articles/%D8%AA%D8%AA%D8%A8%D8%B9-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%85%D9%86%D8%AF%D9%88%D8%A8%D9%8A%D9%86-%D9%84%D8%AD%D8%B8%D9%8A%D8%A7/).

### 4\. أين تحفظ حالة الشاشة: في الذاكرة أم SavedState أم قاعدة البيانات؟

خبير **ماذا يختبر:** تصنيف الحالة حسب عمرها، وهي مهارة المعماري في تطبيقات الجوال.

إجابة المبتدئ

البيانات المهمة في قاعدة البيانات، والباقي في ViewModel.

إجابة الخبير

أصنف الحالة حسب ما يجب أن تنجو منه: حالة مؤقتة للعرض (قائمة مفتوحة، أو موقع التمرير) في الواجهة نفسها أو SavedState، وحالة الشاشة المحملة من الشبكة في ViewModel مع إعادة جلبها إذا ضاعت، وما أدخله المستخدم ولا يجب أن يضيع (مسودة، أو سلة) في قاعدة البيانات المحلية فوراً، وما يتبع الحساب في السيرفر. والقاعدة في التطبيقات التي تعمل دون اتصال أن قاعدة البيانات المحلية هي مصدر الحقيقة للواجهة، والواجهة تراقبها، والشبكة تحدثها فقط. وأنتبه لحجم SavedState لأن له حداً، ولا أضع فيه قوائم كبيرة.

أسئلة المتابعة
- ماذا يحدث إذا وضعت قائمة من ألف عنصر في SavedState؟
- كيف تتعامل مع التنقل العميق إلى شاشة بعد موت العملية؟

**علامات تحذير:** يضع كل شيء في متغيرات عامة Singleton.

**المقال الذي يشرح الموضوع:** [التخزين المؤقت Caching بالتفصيل: الاستراتيجيات والإبطال](https://arabroot.io/articles/%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA-caching-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B1%D8%A7%D8%AA%D9%8A%D8%AC%D9%8A%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%A7%D8%A8%D8%B7%D8%A7%D9%84/).

## الخيط الرئيسي والتزامن

### 5\. لماذا لا تقرأ ملفاً أو تتصل بالشبكة على الخيط الرئيسي؟

مبتدئ **ماذا يختبر:** قاعدة الخيط الرئيسي Main Thread وما يحدث عند كسرها.

إجابة المبتدئ

لأن الواجهة تتجمد، وفي Android يظهر خطأ ANR.

إجابة الخبير

الخيط الرئيسي يرسم الإطارات ويعالج اللمس، والشاشة بمعدل 60 إطاراً في الثانية تعطيه 16 ملي ثانية تقريباً لكل إطار (وأقل في الشاشات الأسرع)، فأي عمل أطول يسقط إطارات ويظهر تقطعاً Jank. وإذا لم يستجب التطبيق للمس خلال 5 ثوان يظهر Android رسالة «التطبيق لا يستجيب» ANR، كما في [وثائق Android](https://developer.android.com/topic/performance/vitals/anr?ref=arabroot.io)، وAndroid يمنع الشبكة على الخيط الرئيسي باستثناء. وأخطر من الشبكة الأشياء التي لا تبدو ثقيلة: قراءة SharedPreferences أول مرة، وفك تشفير صورة كبيرة، وتحليل JSON كبير، واستعلام قاعدة بيانات على جهاز قديم.

أسئلة المتابعة
- كيف تكتشف العمل على الخيط الرئيسي أثناء التطوير؟ (StrictMode وMain Thread Checker)
- لماذا قد يكون التطبيق سريعاً على جهازك ويظهر ANR عند المستخدمين؟

**علامات تحذير:** يعتبر أن العمليات السريعة على جهازه آمنة على الخيط الرئيسي.

**المقال الذي يشرح الموضوع:** [Threads وasync/await: كود لا يتجمد ولا يضيع بياناته](https://arabroot.io/articles/%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%88%D8%A7%D9%84%D8%A3%D9%82%D9%81%D8%A7%D9%84-%D9%88-async-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/).

### 6\. خرج المستخدم من الشاشة والطلب ما زال يعمل، ماذا يحدث؟

متوسط **ماذا يختبر:** التزامن المنظم Structured Concurrency والإلغاء في Kotlin Coroutines وSwift Concurrency.

إجابة المبتدئ

الطلب يكمل في الخلفية، وعندما ينتهي قد يحاول تحديث شاشة غير موجودة، فنتحقق أن الشاشة ما زالت موجودة.

إجابة الخبير

في التزامن المنظم كل عمل له نطاق Scope مرتبط بعمر من بدأه: `viewModelScope` أو `lifecycleScope` في Kotlin، و`.task` في SwiftUI، فعندما ينتهي النطاق تلغى كل الأعمال التي بداخله تلقائياً، فلا تسرب ولا تحديث لشاشة ميتة. والإلغاء تعاوني: الكود يجب أن يصل إلى نقطة تعليق أو يتحقق من الإلغاء، وحلقة حسابية طويلة دون ذلك لا تلغى. وأنتبه لـ `GlobalScope` و`Task.detached` لأنهما يخرجان من هذا النظام، ولالتقاط الاستثناء الخاص بالإلغاء ثم ابتلاعه بـ `catch` عام فيتوقف الإلغاء.

أسئلة المتابعة
- لماذا يكسر `catch (e: Exception)` الإلغاء في Kotlin؟
- متى تريد عملاً يكمل حتى لو خرج المستخدم من الشاشة؟ وأين تضعه؟

**علامات تحذير:** يستخدم `GlobalScope` أو خيوطاً يدوية لكل طلب.

**مقالات تشرح الموضوع:** [Threads وasync/await: كود لا يتجمد ولا يضيع بياناته](https://arabroot.io/articles/%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%88%D8%A7%D9%84%D8%A3%D9%82%D9%81%D8%A7%D9%84-%D9%88-async-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/)، و[كيف تتعامل لغات البرمجة مع الأخطاء؟](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%85%D8%B9-%D8%A7%D9%84%D8%A7%D8%AE%D8%B7%D8%A7%D8%A1/).

### 7\. تحليل ملف JSON كبير يجمد تطبيق Flutter، مع أن الكود async، لماذا؟

متوسط **ماذا يختبر:** نموذج ال Isolates في Dart، وهو التمرين العملي الأول في هذا المقال.

إجابة المبتدئ

لأن التحليل ثقيل، فنستخدم `compute` لنقله إلى مكان آخر.

إجابة الخبير

كود Dart في التطبيق يعمل في Isolate رئيسي واحد له حلقة أحداث واحدة، و`async` يعني فقط أن الكود ينتظر دون أن يحجز الحلقة أثناء I/O، أما العمل الحسابي داخل دالة async فيحجز الحلقة كلها، فلا ترسم إطارات ولا يعالج لمس. والحل نقل العمل إلى Isolate آخر بـ `Isolate.run` (أو `compute` في Flutter)، وهو له ذاكرته الخاصة، فالبيانات تنسخ عند الإرسال والاستقبال، وهذا يكلف وقتاً مع البيانات الكبيرة جداً. ونفس الفكرة في React Native: كود JavaScript يعمل على خيط واحد، والعمل الثقيل يحتاج وحدة Native أو خيطاً منفصلاً.

أسئلة المتابعة
- ما الذي لا يمكن إرساله بين Isolates؟
- متى يكون إنشاء Isolate لكل مهمة أبطأ من تنفيذها مباشرة؟

**علامات تحذير:** يعتقد أن `async` و`Future` يعملان في خيط آخر.

**مقالات تشرح الموضوع:** [Threads وasync/await: كود لا يتجمد ولا يضيع بياناته](https://arabroot.io/articles/%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%88%D8%A7%D9%84%D8%A3%D9%82%D9%81%D8%A7%D9%84-%D9%88-async-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/)، و[التزامن Concurrency والخيوط: ماذا يعني لسيرفرك وتطبيقاتك](https://arabroot.io/articles/%D8%A7%D9%84%D9%85%D8%B9%D8%A7%D9%84%D8%AC-%D9%88%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-%D9%88%D8%A7%D9%84%D8%AA%D8%B2%D8%A7%D9%85%D9%86-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/).

### 8\. المستخدم ضغط على محادثتين بسرعة فظهرت رسائل الأولى في الثانية، كيف حدث ذلك؟

خبير **ماذا يختبر:** حالات السباق في الواجهة بين طلبات غير متزامنة.

إجابة المبتدئ

الطلب الأول تأخر ووصل بعد الثاني، فنلغي الطلب الأول عند فتح الثانية.

إجابة الخبير

الطلبات عادت بترتيب مختلف عن ترتيب إرسالها، والشاشة ترسم كل رد يصل، فرد المحادثة الأولى المتأخر كتب فوق الثانية. والحل أن يكون الرد مرتبطاً بالطلب الذي يخصه: إلغاء العمل القديم عند بدء الجديد (مثل `collectLatest` و`flatMapLatest` في Kotlin Flow، أو إلغاء ال Task في Swift)، وأن تراقب الشاشة مصدراً واحداً مفتاحه معرف المحادثة الحالية، بحيث لا يكتب رد محادثة أخرى في حالتها أصلاً. وهذا نفس التمرين الثالث في مقال أسئلة Frontend، والفكرة واحدة في كل المنصات.

أسئلة المتابعة
- كيف تكتب اختباراً يعيد إنتاج هذا السباق بشكل ثابت؟
- ما الفرق بين `flatMapLatest` و`flatMapMerge`؟

**علامات تحذير:** يقترح تعطيل الضغط حتى ينتهي الطلب كحل وحيد.

**مقالات تشرح الموضوع:** [كيف تبني تطبيق محادثة مثل WhatsApp لملايين المستخدمين](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82-%D9%85%D8%AD%D8%A7%D8%AF%D8%AB%D8%A9-%D9%85%D8%AB%D9%84-whatsapp/)، و[Threads وasync/await: كود لا يتجمد ولا يضيع بياناته](https://arabroot.io/articles/%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%88%D8%A7%D9%84%D8%A3%D9%82%D9%81%D8%A7%D9%84-%D9%88-async-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/).

## العمل دون اتصال والمزامنة

### 9\. كيف تبني تطبيقاً يعمل في المترو دون شبكة؟

متوسط **ماذا يختبر:** معمارية Offline-first.

إجابة المبتدئ

نحفظ آخر البيانات في الجهاز ونعرضها عند انقطاع الشبكة.

إجابة الخبير

قاعدة البيانات المحلية (Room أو SQLite أو Core Data أو SwiftData) هي مصدر الحقيقة للواجهة، والواجهة تراقبها فقط، والمزامنة طبقة منفصلة: تجلب التغييرات منذ آخر مزامنة (بمؤشر أو رقم إصدار من السيرفر وليس بساعة الجهاز)، وترسل التعديلات المحلية من طابور صادر Outbox كل عنصر فيه بمفتاح Idempotency. والواجهة تبين حالة كل عنصر (محفوظ محلياً، أو قيد الإرسال، أو فشل). والسؤال الصعب هو التعارض عندما يعدل نفس السجل من مكانين، وهو التمرين الثالث.

أسئلة المتابعة
- لماذا لا تعتمد على ساعة الجهاز لمعرفة ما تغير؟
- كيف تتعامل مع حذف سجل في السيرفر والجهاز غير متصل؟ (Tombstones)

**علامات تحذير:** يعتبر العمل دون اتصال كاشاً للقراءة فقط.

**مقالات تشرح الموضوع:** [التخزين المؤقت Caching بالتفصيل: الاستراتيجيات والإبطال](https://arabroot.io/articles/%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%A4%D9%82%D8%AA-caching-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B1%D8%A7%D8%AA%D9%8A%D8%AC%D9%8A%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%A7%D8%A8%D8%B7%D8%A7%D9%84/)، و[Idempotency: كيف تجعل العملية آمنة عند تكرارها](https://arabroot.io/articles/idempotency-%D9%83%D9%8A%D9%81-%D8%AA%D8%AC%D8%B9%D9%84-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%A7%D9%85%D9%86%D8%A9-%D8%B9%D9%86%D8%AF-%D8%AA%D9%83%D8%B1%D8%A7%D8%B1%D9%87%D8%A7/).

### 10\. جهازان عدلا نفس الملاحظة دون اتصال، أي التعديلين يبقى؟

خبير **ماذا يختبر:** حل التعارض في المزامنة، وهو التمرين العملي الثالث.

إجابة المبتدئ

التعديل الأحدث حسب الوقت يبقى، أي آخر من عدل.

إجابة الخبير

«آخر كتابة تفوز» Last Write Wins بساعة الجهاز خطيرة لأمرين: ساعات الأجهزة غير دقيقة ويمكن تغييرها، والأهم أنها تحذف تعديل المستخدم الآخر بصمت دون أن يعرف أحد. والبديل المناسب لأغلب التطبيقات التزامن المتفائل: كل سجل له رقم إصدار من السيرفر، والتعديل يرسل الإصدار الذي بني عليه (`If-Match`)، والسيرفر يرفض التعديل المبني على إصدار قديم بـ 412 ويرجع النسخة الحالية، والتطبيق يعرض التعارض أو يدمج الحقول التي لم تتعارض. وللنصوص المشتركة التي يكتب فيها عدة أشخاص معاً توجد هياكل بيانات تدمج تلقائياً CRDT، وثمنها التعقيد والحجم.

أسئلة المتابعة
- كيف تدمج تعديلين على حقلين مختلفين في نفس السجل تلقائياً؟
- متى يكون Last Write Wins مقبولاً فعلاً؟

**علامات تحذير:** يعتمد على ساعة الجهاز دون أن يرى المشكلة.

**مقالات تشرح الموضوع:** [التاريخ والوقت للمطورين ومديري الأنظمة](https://arabroot.io/articles/%D8%A7%D9%84%D8%AA%D8%A7%D8%B1%D9%8A%D8%AE-%D9%88%D8%A7%D9%84%D9%88%D9%82%D8%AA-utc-%D9%88%D8%A7%D9%84%D9%85%D9%86%D8%A7%D8%B7%D9%82-%D8%A7%D9%84%D8%B2%D9%85%D9%86%D9%8A%D8%A9-%D9%88%D8%A7%D9%84%D8%AA%D9%82%D9%88%D9%8A%D9%85-%D8%A7%D9%84%D9%87%D8%AC%D8%B1%D9%8A/)، و[الأنظمة الموزعة للمبتدئين: ماذا يحدث عندما تنقطع الشبكة](https://arabroot.io/articles/%D8%A7%D9%84%D8%A7%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D9%85%D9%88%D8%B2%D8%B9%D8%A9-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-cap-%D9%88%D8%A7%D9%84%D8%A7%D8%AA%D8%B3%D8%A7%D9%82-%D9%88%D8%A7%D9%84%D8%A7%D8%AC%D9%85%D8%A7%D8%B9/).

### 11\. الشبكة في الجوال متقطعة، كيف تعيد محاولة الطلبات دون أن تسبب ضرراً؟

متوسط **ماذا يختبر:** إعادة المحاولة الآمنة والمهلة والتأخير المتزايد.

إجابة المبتدئ

نعيد الطلب ثلاث مرات إذا فشل.

إجابة الخبير

أعيد فقط ما هو آمن للتكرار (GET، أو طلب يحمل مفتاح Idempotency)، وبتأخير متزايد Exponential Backoff مع عشوائية Jitter حتى لا تعود ملايين الأجهزة معاً بعد انقطاع السيرفر فتسقطه مرة أخرى، وأحترم `Retry-After` إذا أرسله السيرفر. ولكل طلب مهلة Timeout مناسبة للجوال، وأميز بين الأخطاء: انقطاع الشبكة يعاد عند عودتها، و4xx لا يعاد لأنه لن ينجح، و5xx يعاد بحد. والطلبات المهمة (إرسال رسالة) تذهب إلى طابور محلي يبقى بعد إغلاق التطبيق، وليس إلى حلقة إعادة في الذاكرة.

أسئلة المتابعة
- ماذا يحدث إذا أعاد مليون جهاز المحاولة في نفس الثانية؟
- كيف تعرف أن الطلب فشل قبل وصوله للسيرفر أو بعد تنفيذه؟

**علامات تحذير:** يعيد كل الطلبات فوراً دون تأخير أو تمييز.

**مقالات تشرح الموضوع:** [كيف تمنع خدمة بطيئة من إسقاط نظامك كله؟](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D9%85%D9%86%D8%B9-%D8%AE%D8%AF%D9%85%D8%A9-%D8%A8%D8%B7%D9%8A%D8%A6%D8%A9-%D9%85%D9%86-%D8%A7%D8%B3%D9%82%D8%A7%D8%B7-%D9%86%D8%B8%D8%A7%D9%85%D9%83-%D9%83%D9%84%D9%87/)، و[Idempotency: كيف تجعل العملية آمنة عند تكرارها](https://arabroot.io/articles/idempotency-%D9%83%D9%8A%D9%81-%D8%AA%D8%AC%D8%B9%D9%84-%D8%A7%D9%84%D8%B9%D9%85%D9%84%D9%8A%D8%A9-%D8%A7%D9%85%D9%86%D8%A9-%D8%B9%D9%86%D8%AF-%D8%AA%D9%83%D8%B1%D8%A7%D8%B1%D9%87%D8%A7/).

## التخزين المحلي والتشفير

### 12\. أين تحفظ توكن تسجيل الدخول في التطبيق؟

مبتدئ **ماذا يختبر:** Keychain وKeystore مقابل التخزين العادي.

إجابة المبتدئ

في Keychain على iOS، وفي EncryptedSharedPreferences أو Keystore على Android، وليس في SharedPreferences العادية.

إجابة الخبير

في iOS في [Keychain](https://developer.apple.com/documentation/security/keychain-services?ref=arabroot.io) مع مستوى وصول مناسب (مثل `AfterFirstUnlockThisDeviceOnly` حتى لا ينتقل إلى جهاز آخر مع النسخة الاحتياطية)، وفي Android المفتاح نفسه في [Android Keystore](https://developer.android.com/privacy-and-security/keystore?ref=arabroot.io) حيث لا يمكن استخراجه من الجهاز، والتوكن يشفر به ويحفظ. وUserDefaults وSharedPreferences ملفات عادية تقرأ من نسخة احتياطية أو من جهاز مكسور الحماية. وأحفظ توكن التجديد الطويل بأعلى حماية، وتوكن الوصول القصير في الذاكرة. ولا يوجد تخزين آمن تماماً على جهاز يملكه المهاجم، فالحماية الحقيقية أن تكون التوكنات قصيرة العمر ويمكن إلغاؤها من السيرفر.

أسئلة المتابعة
- ماذا يحدث للتوكن في Keychain عند حذف التطبيق وإعادة تثبيته على iOS؟
- متى تطلب البصمة قبل استخدام المفتاح؟

**علامات تحذير:** يحفظ التوكن أو كلمة المرور في SharedPreferences أو UserDefaults.

**مقالات تشرح الموضوع:** [كيف يتذكرك الموقع بعد تسجيل الدخول؟ الجلسات وJWT](https://arabroot.io/articles/%D8%A7%D9%84%D8%AC%D9%84%D8%B3%D8%A7%D8%AA-%D9%88%D9%85%D9%84%D9%81%D8%A7%D8%AA-%D8%AA%D8%B9%D8%B1%D9%8A%D9%81-%D8%A7%D9%84%D8%A7%D8%B1%D8%AA%D8%A8%D8%A7%D8%B7-%D9%88-jwt-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)، و[التشفير للمطورين: ماذا تستخدم ولماذا لا تخترع خوارزميتك](https://arabroot.io/articles/%D8%A7%D9%84%D8%AA%D8%B4%D9%81%D9%8A%D8%B1-%D9%84%D9%84%D9%85%D8%B7%D9%88%D8%B1%D9%8A%D9%86-%D8%A7%D9%84%D9%87%D8%A7%D8%B4-%D9%88%D9%83%D9%84%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B1%D9%88%D8%B1-%D9%88%D8%A7%D9%84%D9%85%D9%81%D8%A7%D8%AA%D9%8A%D8%AD/).

### 13\. هل تشفر قاعدة البيانات المحلية؟ ومتى لا يكفي تشفير الجهاز؟

متوسط **ماذا يختبر:** نموذج التهديد للبيانات على الجهاز.

إجابة المبتدئ

نعم، نستخدم SQLCipher لتشفير قاعدة البيانات.

إجابة الخبير

أبدأ بالسؤال: ممن أحمي البيانات؟ تشفير الجهاز الكامل في iOS وAndroid الحديثة يحمي من سرقة جهاز مقفل، فإذا كانت البيانات عادية فهذا يكفي. وأحتاج تشفيراً إضافياً عندما تكون البيانات حساسة جداً (صحية أو مالية) ويجب أن تبقى محمية والجهاز مفتوح، أو من النسخ الاحتياطية، أو على جهاز مكسور الحماية، وهنا SQLCipher مع مفتاح في Keystore أو Keychain، وليس مفتاحاً مكتوباً في الكود. وأتذكر الأماكن الأخرى التي تتسرب منها البيانات: لقطة الشاشة التي يأخذها النظام لقائمة التطبيقات الأخيرة، والسجلات، والحافظة، ولوحة المفاتيح التي تتعلم ما يكتب.

أسئلة المتابعة
- كيف تمنع ظهور بيانات حساسة في لقطة قائمة التطبيقات الأخيرة؟
- لماذا مفتاح التشفير في الكود لا يحمي شيئاً؟

**علامات تحذير:** يضع مفتاح التشفير في الكود أو في ملف الإعدادات.

**مقالات تشرح الموضوع:** [التشفير للمطورين: ماذا تستخدم ولماذا لا تخترع خوارزميتك](https://arabroot.io/articles/%D8%A7%D9%84%D8%AA%D8%B4%D9%81%D9%8A%D8%B1-%D9%84%D9%84%D9%85%D8%B7%D9%88%D8%B1%D9%8A%D9%86-%D8%A7%D9%84%D9%87%D8%A7%D8%B4-%D9%88%D9%83%D9%84%D9%85%D8%A7%D8%AA-%D8%A7%D9%84%D9%85%D8%B1%D9%88%D8%B1-%D9%88%D8%A7%D9%84%D9%85%D9%81%D8%A7%D8%AA%D9%8A%D8%AD/)، و[نمذجة التهديدات Threat Modeling للمبتدئين](https://arabroot.io/articles/%D9%86%D9%85%D8%B0%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D9%87%D8%AF%D9%8A%D8%AF%D8%A7%D8%AA-threat-modeling-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/).

### 14\. مستخدم نقل إلى هاتف جديد فوجد نفسه مسجلاً بحساب لا يعمل، لماذا؟

متوسط **ماذا يختبر:** النسخ الاحتياطي التلقائي وما ينتقل معه.

إجابة المبتدئ

لأن البيانات انتقلت مع النسخة الاحتياطية، فنمنع النسخ الاحتياطي.

إجابة الخبير

Android Auto Backup وiCloud ينقلان ملفات التطبيق وإعداداته افتراضياً، فينتقل معها توكن أو معرف جهاز أو مفتاح مرتبط بالجهاز القديم، فيظن التطبيق أنه مسجل ويفشل كل طلب. والحل أن أحدد ما ينسخ وما لا ينسخ (قواعد النسخ في Android، وخصائص Keychain التي تنتهي بـ `ThisDeviceOnly` في iOS)، وأن يتعامل التطبيق مع توكن غير صالح بإعادة تسجيل الدخول بدلاً من حلقة أخطاء، وأن أعيد تسجيل توكن الإشعارات عند كل تشغيل لأنه يختلف بين الأجهزة.

أسئلة المتابعة
- ما الذي يجب أن ينتقل مع المستخدم إلى الهاتف الجديد؟
- كيف تختبر الاستعادة من نسخة احتياطية؟

**علامات تحذير:** لا يعرف أن بيانات التطبيق تنسخ احتياطياً تلقائياً.

**مقالات تشرح الموضوع:** [النسخ الاحتياطي بقاعدة 3-2-1 واختبار الاستعادة](https://arabroot.io/articles/%D8%A7%D9%84%D9%86%D8%B3%D8%AE-%D8%A7%D9%84%D8%A7%D8%AD%D8%AA%D9%8A%D8%A7%D8%B7%D9%8A-%D8%A8%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-3-2-1-%D9%88%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%B9%D8%A7%D8%AF%D8%A9/)، و[MDM: إدارة أجهزة الموظفين وتأمينها بأدوات مفتوحة](https://arabroot.io/articles/%D8%A7%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D8%AC%D9%87%D8%B2%D8%A9-%D8%A7%D9%84%D9%85%D9%88%D8%B8%D9%81%D9%8A%D9%86-%D9%88%D8%AA%D8%A3%D9%85%D9%8A%D9%86%D9%87%D8%A7-mdm/).

## الشبكة والأمن في الاتصال

### 15\. هل تثبت شهادة السيرفر Certificate Pinning في التطبيق؟

خبير **ماذا يختبر:** الموازنة بين الحماية وخطر تعطيل كل التطبيقات.

إجابة المبتدئ

نعم، لأنه يمنع هجوم الوسيط Man-in-the-Middle.

إجابة الخبير

التثبيت يجعل التطبيق يرفض أي شهادة غير التي يعرفها حتى لو وثق بها النظام، فيحمي من شهادة جهة إصدار CA مزيفة ثبتها المهاجم أو برنامج على الجهاز، ولكن ثمنه كبير: إذا تغيرت الشهادة أو المفتاح دون تحديث التطبيق فكل النسخ المثبتة تتوقف عن الاتصال، ولا يمكن إصلاحها إلا بتحديث من المتجر يستغرق أياماً. لذلك إذا ثبت فأثبت المفتاح العام وليس الشهادة، ومعه مفتاح احتياطي واحد على الأقل، وبتاريخ انتهاء للتثبيت، عبر إعدادات النظام مثل [Network Security Config](https://developer.android.com/privacy-and-security/security-config?ref=arabroot.io) في Android. وأغلب التطبيقات العادية لا تحتاجه، وتطبيقات البنوك والصحة قد تحتاجه، والقرار من نموذج التهديد.

أسئلة المتابعة
- ماذا يحدث لو انتهت شهادة السيرفر والمفتاح الاحتياطي لم يضف؟
- لماذا تثبيت المفتاح العام أفضل من تثبيت الشهادة؟

**علامات تحذير:** يثبت الشهادة دون خطة للتغيير، أو لا يعرف أنه قد يعطل التطبيق بالكامل.

**مقالات تشرح الموضوع:** [الشهادات الرقمية TLS للمبتدئين وصيغ ملفاتها](https://arabroot.io/articles/%D8%A7%D9%84%D8%B4%D9%87%D8%A7%D8%AF%D8%A7%D8%AA-%D8%A7%D9%84%D8%B1%D9%82%D9%85%D9%8A%D8%A9-tls-%D9%88-x509-%D9%88%D8%B5%D9%8A%D8%BA-pem-%D9%88-der/)، و[نمذجة التهديدات Threat Modeling للمبتدئين](https://arabroot.io/articles/%D9%86%D9%85%D8%B0%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D9%87%D8%AF%D9%8A%D8%AF%D8%A7%D8%AA-threat-modeling-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/).

### 16\. كيف تغير ال API والنسخة القديمة من التطبيق ما زالت عند ثلث المستخدمين؟

متوسط **ماذا يختبر:** التوافق مع إصدارات التطبيق التي لا تتحكم في تحديثها.

إجابة المبتدئ

نجبر المستخدمين على التحديث.

إجابة الخبير

على عكس الويب لا أستطيع تحديث كل المستخدمين، فبعضهم يبقى على إصدار قديم لأشهر، لذلك ال API تبقى متوافقة مع الإصدارات القديمة: الإضافة آمنة والتغيير كسر. والتطبيق يرسل إصداره في كل طلب، فأعرف من يستخدم ماذا، وأضع آلية حد أدنى للإصدار يرجعها السيرفر (تحديث مقترح، أو إجباري للضرورة الأمنية فقط)، وأتحكم بالميزات من السيرفر Feature Flags حتى أطفئ ميزة مكسورة دون إصدار جديد. واختبار العقود بين التطبيق وال API يمنع الكسر قبل النشر.

أسئلة المتابعة
- متى يكون التحديث الإجباري مقبولاً للمستخدم؟
- كيف تطفئ ميزة مكسورة في نسخة منشورة دون تحديث؟

**علامات تحذير:** يغير ال API متزامناً مع الإصدار الجديد ويتوقع أن يحدث الجميع.

**مقالات تشرح الموضوع:** [كيف تصمم API لا تضطر إلى كسره بعد سنة](https://arabroot.io/articles/%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-api-rest-%D9%88-grpc-%D9%88-graphql/)، و[Feature Flags: انشر الكود وفعل الميزة لاحقاً](https://arabroot.io/articles/%D9%85%D9%81%D9%87%D9%88%D9%85-feature-flags-%D9%88%D8%A7%D9%84%D8%A7%D8%B7%D9%84%D8%A7%D9%82-%D8%A7%D9%84%D8%AA%D8%AF%D8%B1%D9%8A%D8%AC%D9%8A-gradual-rollout/)، و[اختبار العقود Contract Testing بين الخدمات مع Pact](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%82%D9%88%D8%AF-contract-testing-%D9%85%D8%B9-pact/).

### 17\. هل يمكن إخفاء مفتاح ال API داخل التطبيق؟

متوسط **ماذا يختبر:** فهم أن التطبيق على جهاز المستخدم ليس مكاناً آمناً للأسرار.

إجابة المبتدئ

نضعه في ملف خاص ونشفره أو نستخدم ProGuard حتى لا يقرأه أحد.

إجابة الخبير

لا، فكل ما في التطبيق يمكن استخراجه: بفك ال APK أو IPA، أو بمراقبة الشبكة من جهاز يملكه المهاجم، أو بتشغيل التطبيق تحت أداة مثل Frida. والتشويش Obfuscation يؤخر فقط. لذلك المفاتيح التي تكلف مالاً أو تعطي صلاحيات (مفتاح خدمة دفع، أو مفتاح نموذج ذكاء اصطناعي) تبقى في السيرفر، والتطبيق يستدعي السيرفر الذي يتحقق من المستخدم ويضع حداً لاستخدامه. أما المفاتيح المصممة لتكون عامة (مفتاح الخرائط المقيد بمعرف التطبيق) فتقيد من لوحة المزود. وللتأكد أن الطلب من تطبيقك الأصلي توجد خدمات تحقق مثل Play Integrity وApp Attest، وهي ترفع التكلفة على المهاجم ولا تمنعه.

أسئلة المتابعة
- كيف تحمي مفتاح خدمة ذكاء اصطناعي يستخدمه التطبيق؟
- ما الذي تثبته خدمات مثل App Attest، وما الذي لا تثبته؟

**علامات تحذير:** يعتقد أن التشويش أو التشفير داخل التطبيق يحمي السر.

**مقالات تشرح الموضوع:** [Secrets Scanning: كيف تكتشف الأسرار المسربة في الكود](https://arabroot.io/articles/%D8%A7%D9%83%D8%AA%D8%B4%D8%A7%D9%81-%D8%A7%D9%84%D8%A7%D8%B3%D8%B1%D8%A7%D8%B1-%D8%A7%D9%84%D9%85%D8%B3%D8%B1%D8%A8%D8%A9-%D9%85%D8%B9-gitleaks-%D9%88-trufflehog/)، و[أمن ال API بالأمثلة مع OWASP API Security Top 10](https://arabroot.io/articles/%D8%A7%D9%85%D9%86-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-api-%D9%85%D8%B9-owasp-api-security-top-10/).

## الإشعارات Push Notifications

### 18\. كيف يصل الإشعار من سيرفرك إلى هاتف المستخدم؟

مبتدئ **ماذا يختبر:** دور APNs وFCM والتوكن.

إجابة المبتدئ

السيرفر يرسل الإشعار إلى Firebase، وFirebase يرسله إلى الهاتف.

إجابة الخبير

التطبيق يطلب من النظام توكن جهاز (من APNs في iOS، ومن FCM في Android) ويرسله لسيرفرنا ليحفظه مع المستخدم، وعندما نريد إرسال إشعار يرسل سيرفرنا إلى خدمة المنصة مع التوكن، والخدمة تحافظ على اتصال واحد دائم مع الجهاز لكل التطبيقات وتسلمه. والتوكن يتغير (إعادة تثبيت، أو استعادة نسخة، أو تجديد من النظام) فيرسله التطبيق مع كل تشغيل، والخدمة ترجع خطأ للتوكن غير الصالح فنحذفه. والتسليم ليس مضموناً ولا فورياً دائماً، فلا أعتمد على الإشعار لنقل بيانات مهمة، وإنما لتنبيه التطبيق ليجلبها.

أسئلة المتابعة
- ماذا تفعل بالتوكنات التي ترجع الخدمة أنها غير مسجلة؟
- لماذا قد يتأخر الإشعار في وضع توفير البطارية؟

**علامات تحذير:** لا يعرف أن التوكن يتغير، أو يرسل البيانات الحساسة في نص الإشعار.

**مقالات تشرح الموضوع:** [كيف تعمل الإشعارات الفورية Push Notifications؟](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%B9%D9%85%D9%84-%D8%A7%D9%84%D8%A7%D8%B4%D8%B9%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%81%D9%88%D8%B1%D9%8A%D8%A9-push-notifications/)، و[كيف تبني نظام إشعارات يصل إلى ملايين المستخدمين](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%A8%D9%86%D9%8A-%D9%86%D8%B8%D8%A7%D9%85-%D8%A7%D8%B4%D8%B9%D8%A7%D8%B1%D8%A7%D8%AA-%D9%8A%D8%B5%D9%84-%D8%A7%D9%84%D9%89-%D9%85%D9%84%D8%A7%D9%8A%D9%8A%D9%86-%D8%A7%D9%84%D9%85%D8%B3%D8%AA%D8%AE%D8%AF%D9%85%D9%8A%D9%86/).

### 19\. متى تطلب إذن الإشعارات من المستخدم؟

متوسط **ماذا يختبر:** تجربة المستخدم مع الأذونات وأثرها على المنتج.

إجابة المبتدئ

عند أول تشغيل للتطبيق، حتى نضمن أننا نستطيع إرسال الإشعارات.

إجابة الخبير

طلب الإذن عند أول تشغيل دون سياق يرفضه كثير من المستخدمين، وفي iOS إذا رفض المستخدم لا يمكن أن يظهر الطلب مرة أخرى إلا من الإعدادات، وAndroid 13 أصبح يطلب إذناً صريحاً أيضاً. لذلك أطلبه في اللحظة التي يفهم فيها المستخدم الفائدة (بعد أن يطلب طلباً: «هل تريد أن نخبرك عندما يصل المندوب؟»)، وأعرض شرحاً من التطبيق قبل طلب النظام. وأقسم الإشعارات إلى قنوات Channels حتى يطفئ المستخدم العروض ويبقي حالة الطلب، بدلاً من أن يطفئ كل شيء.

أسئلة المتابعة
- ما هي قنوات الإشعارات في Android ولماذا تهم؟
- كيف تقيس نسبة القبول ونسبة الإيقاف؟

**علامات تحذير:** يطلب كل الأذونات عند أول تشغيل.

**مقالات تشرح الموضوع:** [كيف تعمل الإشعارات الفورية Push Notifications؟](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%B9%D9%85%D9%84-%D8%A7%D9%84%D8%A7%D8%B4%D8%B9%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%81%D9%88%D8%B1%D9%8A%D8%A9-push-notifications/)، و[مقاييس المنتج Product Metrics وكيف تعرف أن الميزة نجحت](https://arabroot.io/articles/%D9%85%D9%82%D8%A7%D9%8A%D9%8A%D8%B3-%D8%A7%D9%84%D9%85%D9%86%D8%AA%D8%AC-%D9%88%D8%AA%D8%AD%D9%84%D9%8A%D9%84%D8%A7%D8%AA-%D8%A7%D9%84%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-product-metrics/).

### 20\. هل تعتمد على الإشعار الصامت لمزامنة البيانات في الخلفية؟

خبير **ماذا يختبر:** حدود الإشعارات الصامتة وأن النظام لا يضمنها.

إجابة المبتدئ

نعم، نرسل إشعاراً صامتاً فيستيقظ التطبيق ويجلب البيانات.

إجابة الخبير

الإشعار الصامت (Background Notification في iOS، ورسالة البيانات في FCM) طريقة جيدة لتنبيه التطبيق، ولكن النظام يعامله بأولوية منخفضة: iOS يحد من عددها ويؤجلها وقد يتجاهلها إذا كان الجهاز في توفير الطاقة أو إذا أغلق المستخدم التطبيق قسراً، وAndroid يؤجل رسائل الأولوية العادية في وضع Doze. لذلك أستخدمه كتحسين وليس كضمان: البيانات تجلب أيضاً عند فتح التطبيق، والإشعار المرئي المهم يحمل ما يكفي للعرض دون أن يحتاج التطبيق للاستيقاظ.

أسئلة المتابعة
- ماذا يحدث للإشعارات الصامتة إذا أغلق المستخدم التطبيق من قائمة التطبيقات في iOS؟
- متى تستخدم الأولوية العالية في FCM، وما ثمن الإفراط فيها؟

**علامات تحذير:** يبني المزامنة كلها على أن الإشعار الصامت يصل دائماً.

**مقالات تشرح الموضوع:** [كيف تعمل الإشعارات الفورية Push Notifications؟](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%B9%D9%85%D9%84-%D8%A7%D9%84%D8%A7%D8%B4%D8%B9%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A7%D9%84%D9%81%D9%88%D8%B1%D9%8A%D8%A9-push-notifications/)، و[إشعارات فورية من سيرفرك إلى هاتفك مع ntfy](https://arabroot.io/articles/%D8%A7%D8%B4%D8%B9%D8%A7%D8%B1%D8%A7%D8%AA-%D9%81%D9%88%D8%B1%D9%8A%D8%A9-%D8%A7%D9%84%D9%89-%D9%87%D8%A7%D8%AA%D9%81%D9%83-%D9%85%D8%B9-ntfy/).

## الروابط العميقة Deep Links

### 21\. ما الفرق بين رابط بمخطط خاص ورابط Universal Link أو App Link؟

متوسط **ماذا يختبر:** التحقق من ملكية النطاق في الروابط العميقة.

إجابة المبتدئ

الرابط الخاص مثل `myapp://` يفتح التطبيق، وUniversal Link رابط https عادي يفتح التطبيق إذا كان مثبتاً.

إجابة الخبير

المخطط الخاص يستطيع أي تطبيق أن يسجله، فقد يعترض تطبيق آخر رابط استعادة كلمة المرور أو رابط OAuth، ولا يعمل إذا لم يكن التطبيق مثبتاً. أما [Universal Links](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app?ref=arabroot.io) في iOS و[App Links](https://developer.android.com/training/app-links?ref=arabroot.io) في Android فروابط https عادية، والتطبيق يثبت ملكية النطاق بملف على الموقع (`apple-app-site-association` و`assetlinks.json`)، فلا يستطيع تطبيق آخر أخذها، وإذا لم يكن التطبيق مثبتاً تفتح الصفحة في المتصفح. لذلك كل رابط يحمل شيئاً حساساً يجب أن يكون من النوع الثاني.

أسئلة المتابعة
- الرابط يفتح المتصفح بدلاً من التطبيق، أين تبحث؟
- لماذا يجب أن يستخدم تسجيل الدخول بـ OAuth في التطبيق PKCE؟

**علامات تحذير:** يستخدم مخططاً خاصاً لرابط استعادة كلمة المرور.

**المقال الذي يشرح الموضوع:** [شرح OAuth وOpenID Connect للمبتدئين](https://arabroot.io/articles/%D8%B4%D8%B1%D8%AD-oauth-%D9%88-openid-connect-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/).

### 22\. الرابط العميق يفتح شاشة الدفع بمبلغ من الرابط، ما الخطر؟

خبير **ماذا يختبر:** معاملة الرابط العميق كمدخل غير موثوق.

إجابة المبتدئ

قد يغير أحد المبلغ في الرابط، فنتحقق منه في السيرفر.

إجابة الخبير

الرابط العميق مدخل خارجي مثل أي طلب HTTP، فأي موقع أو رسالة أو تطبيق يستطيع أن يفتحه بأي معاملات، لذلك لا ينفذ إجراء دون تأكيد المستخدم، ولا يحمل قيمة يثق بها التطبيق (المبلغ والمستلم يأتيان من السيرفر بمعرف الطلب)، ويمر بنفس التحقق من تسجيل الدخول والصلاحية. وفي Android أنتبه لل Activities المصدرة Exported التي تقبل Intents من أي تطبيق، وللروابط التي تفتح WebView بعنوان من المعامل فتعرض صفحة المهاجم داخل التطبيق بثقته.

أسئلة المتابعة
- ما الخطر في رابط عميق يفتح WebView بعنوان من معامل الرابط؟
- كيف تختبر كل الروابط العميقة في التطبيق؟

**علامات تحذير:** يعتبر الرابط العميق آمناً لأنه من داخل التطبيق.

**مقالات تشرح الموضوع:** [البرمجة الآمنة Secure Coding بأمثلة من أربع لغات](https://arabroot.io/articles/%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%A7%D9%84%D8%A7%D9%85%D9%86%D8%A9-secure-coding-%D9%84%D9%84%D9%85%D8%B7%D9%88%D8%B1%D9%8A%D9%86/)، و[أمن ال API بالأمثلة مع OWASP API Security Top 10](https://arabroot.io/articles/%D8%A7%D9%85%D9%86-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-api-%D9%85%D8%B9-owasp-api-security-top-10/).

## الأداء والبطارية وحجم التطبيق

### 23\. التطبيق يأخذ أربع ثوان ليفتح، كيف تقلل ذلك؟

متوسط **ماذا يختبر:** زمن البدء البارد Cold Start وقياسه.

إجابة المبتدئ

نقلل ما يحدث عند التشغيل، ونعرض شاشة البداية حتى تجهز البيانات.

إجابة الخبير

أقيس أولاً على جهاز متوسط وبإصدار Release وليس Debug، وأميز بين البدء البارد (العملية غير موجودة) والدافئ والساخن. ثم أبحث عما يحدث قبل أول إطار: مكتبات تهيأ في `Application.onCreate` أو `didFinishLaunching` (تحليلات، وإعلانات، وحقن الاعتماديات)، وقراءة ملفات وقاعدة بيانات على الخيط الرئيسي، وطلبات شبكة ينتظرها أول عرض. والحل تأجيل ما لا يلزم للإطار الأول، وعرض البيانات المحلية فوراً ثم تحديثها، وفي Android ملفات Baseline Profiles التي تقلل وقت الترجمة عند البدء. وأراقب الزمن في الإنتاج لأن جهازي ليس جهاز المستخدم.

أسئلة المتابعة
- ما الفرق بين البدء البارد والدافئ في Android؟
- كيف تقيس زمن البدء في CI لتمنع التراجع؟

**علامات تحذير:** يقيس على إصدار Debug أو على أحدث هاتف فقط.

**مقالات تشرح الموضوع:** [تتبع الطلب البطيء من المتصفح إلى قاعدة البيانات](https://arabroot.io/articles/%D8%AA%D8%AA%D8%A8%D8%B9-%D8%A7%D9%84%D8%B7%D9%84%D8%A8-%D8%A7%D9%84%D8%A8%D8%B7%D9%8A%D8%A1-%D9%85%D9%86-%D8%A7%D9%84%D9%85%D8%AA%D8%B5%D9%81%D8%AD-%D8%A7%D9%84%D9%89-%D9%82%D8%A7%D8%B9%D8%AF%D8%A9-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA/)، و[أدوات البناء Build Tools وماذا يحدث عندما تبني مشروعك](https://arabroot.io/articles/%D8%A7%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D9%86%D8%A7%D8%A1-build-tools-%D9%85%D9%86-make-%D8%A7%D9%84%D9%89-maven-%D9%88-gradle-%D9%88-dotnet/).

### 24\. التمرير في قائمة المنتجات متقطع، ما الأسباب الشائعة؟

متوسط **ماذا يختبر:** أداء القوائم والصور.

إجابة المبتدئ

الصور كبيرة، فنصغرها ونستخدم مكتبة لتحميل الصور.

إجابة الخبير

أفتح أداة الإطارات (Profile GPU Rendering أو Android Studio Profiler أو Instruments أو Flutter DevTools) لأرى أي إطار تجاوز الميزانية ولماذا. والأسباب الشائعة: فك تشفير صور بحجمها الأصلي (صورة 4000 بكسل في خانة 100 بكسل تأكل الذاكرة والوقت)، وعمل في دالة ربط العنصر (تنسيق تواريخ، أو حسابات، أو قراءة من قاعدة البيانات)، وتخطيطات متداخلة عميقة، وإعادة بناء كل القائمة عند تغيير عنصر واحد (في Compose وFlutter بسبب حالة في المكان الخطأ أو مفاتيح ناقصة). والحل: صور بحجم الخانة مع كاش، وحساب مسبق للبيانات، وقوائم كسولة بمفاتيح ثابتة.

أسئلة المتابعة
- كم ذاكرة تأخذ صورة 4000 في 3000 بكسل بعد فك تشفيرها؟
- لماذا تحتاج القوائم الكسولة مفاتيح ثابتة؟

**علامات تحذير:** يحمل الصور بحجمها الأصلي ويصغرها في العرض.

**مقالات تشرح الموضوع:** [صيغ الصور: كيف تختار الصيغة المناسبة لموقعك](https://arabroot.io/articles/%D8%B5%D9%8A%D8%BA-%D8%A7%D9%84%D8%B5%D9%88%D8%B1-%D9%88%D9%83%D9%8A%D9%81-%D8%AA%D8%AE%D8%AA%D8%A7%D8%B1-%D8%A8%D9%8A%D9%86%D9%87%D8%A7/)، و[الذاكرة للمبرمجين: كيف تعمل ال Stack وال Heap](https://arabroot.io/articles/%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86-stack-%D9%88-heap-%D9%88%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA/).

### 25\. المستخدمون يشتكون أن التطبيق يستنزف البطارية، أين تبحث؟

خبير **ماذا يختبر:** مصادر استهلاك الطاقة في الجوال.

إجابة المبتدئ

نقلل استخدام GPS والطلبات في الخلفية.

إجابة الخبير

أكبر المستهلكين: الموقع بدقة عالية ومستمر، والراديو الخلوي الذي يستيقظ مع كل طلب صغير ويبقى نشطاً ثواني بعده (فعشرون طلباً متفرقة أسوأ من طلب مجمع)، وأقفال الإيقاظ Wake Locks المنسية، والمؤقتات في الخلفية، والرسوم المتحركة التي تعمل على شاشة مخفية. وأقيس بأدوات المنصة (Battery Historian وEnergy Profiler في Android، وEnergy Log في Xcode)، وأصلح بتجميع الطلبات، ودقة موقع أقل عندما تكفي، وتحديث موقع حسب الحركة، وترك الجدولة للنظام عبر WorkManager وBGTaskScheduler.

أسئلة المتابعة
- لماذا يستهلك طلب صغير كل دقيقة أكثر من طلب كبير كل ساعة؟
- كيف تقلل استهلاك تتبع موقع المندوب طوال اليوم؟

**علامات تحذير:** لم يقس البطارية أبداً ويخمن السبب.

**المقال الذي يشرح الموضوع:** [تتبع مواقع المندوبين لحظياً: من الهاتف إلى الخريطة](https://arabroot.io/articles/%D8%AA%D8%AA%D8%A8%D8%B9-%D9%85%D9%88%D8%A7%D9%82%D8%B9-%D8%A7%D9%84%D9%85%D9%86%D8%AF%D9%88%D8%A8%D9%8A%D9%86-%D9%84%D8%AD%D8%B8%D9%8A%D8%A7/).

### 26\. لماذا يهم حجم التطبيق، وكيف تقلله؟

مبتدئ **ماذا يختبر:** أثر الحجم على التثبيت، وأدوات تقليله.

إجابة المبتدئ

التطبيق الكبير يأخذ مساحة ويتأخر تنزيله، فنضغط الصور ونحذف الملفات غير المستخدمة.

إجابة الخبير

الحجم يؤثر على نسبة من يكمل التثبيت، خصوصاً على باقات البيانات والأجهزة ذات المساحة القليلة. وأقلله بالتوزيع حسب الجهاز (Android App Bundle الذي يرسل لكل جهاز ما يناسب معالجه وشاشته ولغته، وApp Thinning في iOS)، وتصغير الكود وحذف غير المستخدم (R8)، وصيغ صور حديثة أو رسوم متجهة، وتحميل الموارد الكبيرة عند الحاجة، ومراجعة المكتبات لأن مكتبة واحدة قد تضيف ميجابايتات من الكود الأصلي لكل معالج. وأضع حداً للحجم في CI يفشل عند زيادة غير متوقعة.

أسئلة المتابعة
- ما الفرق بين حجم التنزيل وحجم التثبيت؟
- كيف تعرف أي مكتبة أضافت أكبر حجم؟

**علامات تحذير:** لا يعرف App Bundle أو لا يهتم بالحجم.

**مقالات تشرح الموضوع:** [كيف يعمل ضغط البيانات Compression؟ من Huffman إلى Zstd](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D9%8A%D8%B9%D9%85%D9%84-%D8%B6%D8%BA%D8%B7-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-compression/)، و[صيغ الصور: كيف تختار الصيغة المناسبة لموقعك](https://arabroot.io/articles/%D8%B5%D9%8A%D8%BA-%D8%A7%D9%84%D8%B5%D9%88%D8%B1-%D9%88%D9%83%D9%8A%D9%81-%D8%AA%D8%AE%D8%AA%D8%A7%D8%B1-%D8%A8%D9%8A%D9%86%D9%87%D8%A7/).

## الأعطال وANR وتسرب الذاكرة

### 27\. وصلك تقرير عطل فيه عناوين ذاكرة دون أسماء دوال، ماذا ينقصك؟

مبتدئ **ماذا يختبر:** ملفات الرموز Symbolication والتشويش Mapping.

إجابة المبتدئ

نحتاج ملف ال Mapping أو dSYM لنرى أسماء الدوال.

إجابة الخبير

إصدار الإنتاج مشوش ومصغر (R8 في Android) أو مترجم دون رموز (iOS)، فتقرير العطل يحتاج ملف `mapping.txt` أو ملفات dSYM من نفس البناء بالضبط ليعيد الأسماء والأسطر. لذلك يرفع خط البناء هذه الملفات تلقائياً لأداة جمع الأعطال مع كل إصدار، وإلا ضاع التقرير، فلا يمكن توليدها من جديد لبناء قديم. ومع التقرير أحتاج سياقاً: إصدار التطبيق والنظام والجهاز، وآخر خطوات المستخدم Breadcrumbs، دون بيانات شخصية. وأرتب الأعطال حسب عدد المستخدمين المتأثرين وليس عدد المرات.

أسئلة المتابعة
- لماذا يجب أن يأتي ملف ال Mapping من نفس البناء بالضبط؟
- ما نسبة المستخدمين الذين لم يواجهوا عطلاً، ولماذا هي أفضل من عدد الأعطال؟

**علامات تحذير:** لا يعرف لماذا التقرير غير مقروء.

**مقالات تشرح الموضوع:** [السجلات Logging للمطورين: ماذا تسجل وماذا لا تسجل](https://arabroot.io/articles/%D8%A7%D9%84%D8%B3%D8%AC%D9%84%D8%A7%D8%AA-logging-%D9%84%D9%84%D9%85%D8%B7%D9%88%D8%B1%D9%8A%D9%86-%D9%88%D9%85%D8%AF%D9%8A%D8%B1%D9%8A-%D8%A7%D9%84%D8%B3%D9%8A%D8%B1%D9%81%D8%B1%D8%A7%D8%AA/)، و[أدوات البناء Build Tools وماذا يحدث عندما تبني مشروعك](https://arabroot.io/articles/%D8%A7%D8%AF%D9%88%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D9%86%D8%A7%D8%A1-build-tools-%D9%85%D9%86-make-%D8%A7%D9%84%D9%89-maven-%D9%88-gradle-%D9%88-dotnet/).

### 28\. نسبة ANR في Google Play ارتفعت، كيف تحقق فيها؟

متوسط **ماذا يختبر:** تشخيص ANR من بيانات المتجر والتقارير.

إجابة المبتدئ

نبحث عن العمليات الثقيلة على الخيط الرئيسي وننقلها إلى الخلفية.

إجابة الخبير

أفتح Android vitals لأرى التقارير مجمعة حسب المكدس Stack Trace للخيط الرئيسي لحظة ANR، والأجهزة وإصدارات النظام الأكثر تأثراً، وهل بدأ الارتفاع مع إصدار معين. وأحياناً المكدس يبين الخيط الرئيسي ينتظر قفلاً يحمله خيط آخر، فالمشكلة في الخيط الآخر. ويهم ذلك للمتجر نفسه: [وثائق Android vitals](https://developer.android.com/topic/performance/vitals?ref=arabroot.io) تحدد حد السلوك السيئ لنسبة ANR التي يلاحظها المستخدم عند 0.47% عموماً و8% لطراز هاتف واحد، وتجاوزه قد يقلل ظهور التطبيق في المتجر ويظهر تحذيراً في صفحته.

أسئلة المتابعة
- المكدس يبين أن الخيط الرئيسي ينتظر قفلاً، كيف تجد من يحمله؟
- لماذا قد يظهر ANR في BroadcastReceiver دون أي واجهة؟

**علامات تحذير:** لا يعرف أين يرى بيانات ANR في الإنتاج.

**مقالات تشرح الموضوع:** [استكشاف الأخطاء وتحليل الأداء للمبتدئين: من العرض إلى السبب](https://arabroot.io/articles/%D8%A7%D8%B3%D8%AA%D9%83%D8%B4%D8%A7%D9%81-%D8%A7%D9%84%D8%A7%D8%AE%D8%B7%D8%A7%D8%A1-%D9%88%D8%AA%D8%AD%D9%84%D9%8A%D9%84-%D8%A7%D9%84%D8%A7%D8%AF%D8%A7%D8%A1-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)، و[Threads وasync/await: كود لا يتجمد ولا يضيع بياناته](https://arabroot.io/articles/%D8%A7%D9%84%D8%AE%D9%8A%D9%88%D8%B7-threads-%D9%88%D8%A7%D9%84%D8%A3%D9%82%D9%81%D8%A7%D9%84-%D9%88-async-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86/).

### 29\. شاشة الملف الشخصي لا تخرج من الذاكرة بعد إغلاقها، لماذا؟

متوسط **ماذا يختبر:** دورات المراجع Retain Cycles في Swift وتسرب ال Context في Android، وهو التمرين العملي الثاني.

إجابة المبتدئ

هناك مرجع ما زال يشير إليها، مثل Closure، فنستخدم `[weak self]`.

إجابة الخبير

في iOS الذاكرة تدار بعد المراجع ARC، والكائن يحرر عندما يصل عدد مراجعه القوية إلى صفر، فإذا كانت الشاشة تملك Closure والـ Closure تمسك الشاشة بقوة فكل منهما يبقي الآخر حياً إلى الأبد، ولا يستدعى `deinit`. والحل `[weak self]` أو `[unowned self]` عندما يكون عمر الطرفين معروفاً. وفي Android نفس الفكرة بشكل آخر: كائن عمره أطول (Singleton، أو مستمع مسجل، أو Coroutine في نطاق عام) يمسك Activity أو View، فلا يحرر بعد الدوران. وأكتشفها بأداة Memory Graph في Xcode، وLeakCanary في Android، وأضع `deinit` مؤقتاً يطبع لأتأكد.

أسئلة المتابعة
- متى تستخدم `unowned` بدلاً من `weak`، وما خطره؟
- لماذا لا يسبب Closure يمرر إلى `DispatchQueue.main.async` دورة مراجع؟

**علامات تحذير:** يضع `[weak self]` في كل مكان دون أن يعرف متى يلزم.

**المقال الذي يشرح الموضوع:** [الذاكرة للمبرمجين: كيف تعمل ال Stack وال Heap](https://arabroot.io/articles/%D8%A7%D9%84%D8%B0%D8%A7%D9%83%D8%B1%D8%A9-%D9%84%D9%84%D9%85%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D9%86-stack-%D9%88-heap-%D9%88%D8%A7%D9%84%D9%85%D8%A4%D8%B4%D8%B1%D8%A7%D8%AA/).

## الإصدارات والنشر في المتاجر

### 30\. لا يمكنك التراجع عن إصدار في المتجر، فكيف تنشر بأمان؟

متوسط **ماذا يختبر:** النشر التدريجي والتحكم من السيرفر.

إجابة المبتدئ

نختبر الإصدار جيداً قبل النشر، ونستخدم TestFlight أو الاختبار الداخلي.

إجابة الخبير

الإصدار الذي ثبته المستخدم لا يمكن سحبه منه، فالأدوات هي: الاختبار الداخلي والتجريبي قبل النشر، ثم النشر التدريجي (Staged Rollout في Google Play وPhased Release في App Store) مع مراقبة نسبة الأعطال وANR في كل مرحلة وإيقافه عند المشكلة، ثم Feature Flags من السيرفر لكل ميزة كبيرة حتى أطفئها دون إصدار، وآلية الحد الأدنى للإصدار للحالات الخطيرة. والإصلاح يمر بمراجعة المتجر من جديد، لذلك أحتاج خط بناء ونشر آلياً يخرج إصلاحاً في ساعات وليس أياماً.

أسئلة المتابعة
- ما المقاييس التي توقف عندها النشر التدريجي؟
- كيف تصلح خطأ في نص أو سعر دون إصدار جديد؟

**علامات تحذير:** ينشر لكل المستخدمين مرة واحدة ويعتمد على الاختبار اليدوي.

**مقالات تشرح الموضوع:** [Feature Flags: انشر الكود وفعل الميزة لاحقاً](https://arabroot.io/articles/%D9%85%D9%81%D9%87%D9%88%D9%85-feature-flags-%D9%88%D8%A7%D9%84%D8%A7%D8%B7%D9%84%D8%A7%D9%82-%D8%A7%D9%84%D8%AA%D8%AF%D8%B1%D9%8A%D8%AC%D9%8A-gradual-rollout/)، و[Trunk-Based Development: فروع قصيرة ودمج يومي](https://arabroot.io/articles/%D8%A7%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%B9%D9%84%D9%89-%D8%A7%D9%84%D9%81%D8%B1%D8%B9-%D8%A7%D9%84%D8%B1%D8%A6%D9%8A%D8%B3%D9%8A-trunk-based/).

### 31\. رفضت Apple التطبيق في المراجعة، ما الأسباب الشائعة؟

مبتدئ **ماذا يختبر:** معرفة عملية بقواعد المتاجر.

إجابة المبتدئ

أسباب مثل الأعطال أو نقص المعلومات أو مخالفة السياسات.

إجابة الخبير

أقرأ سبب الرفض ورقم البند في [إرشادات مراجعة App Store](https://developer.apple.com/app-store/review/guidelines/?ref=arabroot.io)، ومن الأسباب الشائعة عملياً: تطبيق يتعطل أو فيه روابط مكسورة أو محتوى تجريبي، وعدم إعطاء المراجع حساباً تجريبياً يعمل لما هو خلف تسجيل الدخول، وطلب أذونات دون شرح واضح لسببها، وبيع محتوى رقمي داخل التطبيق دون نظام الشراء الداخلي حيث تشترطه القواعد، وتسجيل حساب دون طريقة لحذفه من داخل التطبيق، ووصف خصوصية لا يطابق ما يجمعه التطبيق فعلاً. والحل قائمة تحقق قبل كل إرسال، لأن الرفض يؤخر الإصدار أياماً.

أسئلة المتابعة
- ماذا تعطي المراجع ليختبر ميزة خلف اشتراك مدفوع؟
- كيف تتأكد أن ملصق الخصوصية يطابق ما تجمعه المكتبات الخارجية؟

**علامات تحذير:** يعتبر الرفض حظاً سيئاً ولا يقرأ الإرشادات.

**المقال الذي يشرح الموضوع:** [MDM: إدارة أجهزة الموظفين وتأمينها بأدوات مفتوحة](https://arabroot.io/articles/%D8%A7%D8%AF%D8%A7%D8%B1%D8%A9-%D8%A7%D8%AC%D9%87%D8%B2%D8%A9-%D8%A7%D9%84%D9%85%D9%88%D8%B8%D9%81%D9%8A%D9%86-%D9%88%D8%AA%D8%A3%D9%85%D9%8A%D9%86%D9%87%D8%A7-mdm/).

### 32\. ماذا يحدث إذا فقدت مفتاح توقيع التطبيق؟

خبير **ماذا يختبر:** إدارة مفاتيح التوقيع وأمن خط البناء.

إجابة المبتدئ

لن نستطيع تحديث التطبيق، فنحفظ المفتاح في مكان آمن.

إجابة الخبير

في Android كان فقدان مفتاح التوقيع يعني تطبيقاً جديداً باسم جديد، ومع Play App Signing يحفظ Google مفتاح توقيع التطبيق ونحن نملك مفتاح الرفع Upload Key الذي يمكن استبداله إذا ضاع أو سرب. والخطر الآخر التسريب: من يملك المفتاح يستطيع توقيع تحديث خبيث يقبله الجهاز. لذلك المفاتيح والشهادات لا تكون على أجهزة المطورين، وإنما في خط البناء من مدير أسرار، والتوقيع يحدث في CI فقط، ومع نسخة احتياطية محمية وخطة لمن يملك الوصول عندما يغادر شخص الفريق.

أسئلة المتابعة
- ما الفرق بين مفتاح الرفع ومفتاح توقيع التطبيق؟
- كيف تبني وتوقع تطبيق iOS في CI دون جهاز Mac لكل مطور؟

**علامات تحذير:** المفتاح على حاسب مطور واحد دون نسخة.

**مقالات تشرح الموضوع:** [Secrets Scanning: كيف تكتشف الأسرار المسربة في الكود](https://arabroot.io/articles/%D8%A7%D9%83%D8%AA%D8%B4%D8%A7%D9%81-%D8%A7%D9%84%D8%A7%D8%B3%D8%B1%D8%A7%D8%B1-%D8%A7%D9%84%D9%85%D8%B3%D8%B1%D8%A8%D8%A9-%D9%85%D8%B9-gitleaks-%D9%88-trufflehog/)، و[أمن سلسلة التوريد البرمجية Software Supply Chain للمبتدئين](https://arabroot.io/articles/%D8%A7%D9%85%D9%86-%D8%B3%D9%84%D8%B3%D9%84%D8%A9-%D8%A7%D9%84%D8%AA%D9%88%D8%B1%D9%8A%D8%AF-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A9-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/).

## اختيار التقنية بين الأصلي ومتعدد المنصات

### 33\. شركة ناشئة تريد تطبيقاً على المنصتين، بماذا تنصحها؟

متوسط **ماذا يختبر:** الموازنة بين التقنيات حسب الفريق والمنتج، وليس حسب الموضة.

إجابة المبتدئ

Flutter أو React Native، لأنهما يكتبان مرة واحدة ويعملان على المنصتين، فيوفران الوقت.

إجابة الخبير

أسأل عن الفريق أولاً: إذا كان الفريق مطوري ويب بـ TypeScript فـ React Native يستفيد من خبرتهم، وإذا كان الفريق جديداً فـ Flutter يعطي واجهة متطابقة على المنصتين برسم خاص به، وإذا كان هناك فريقان أصليان قويان أو التطبيق يعتمد على ميزات النظام بعمق (الكاميرا، والبلوتوث، والخلفية، والودجات) فالأصلي، وKotlin Multiplatform يشارك المنطق ويترك الواجهة أصلية. ثم أذكر الثمن الذي لا يذكر في العروض: كل ميزة جديدة في النظام تتأخر حتى تدعمها المكتبة، وتحتاج كوداً أصلياً في النهاية لبعض الأشياء، والتصحيح بين طبقتين أصعب. والقرار يكتب في سجل قرار معماري ADR بأسبابه.

أسئلة المتابعة
- ما الذي يجعلك تنتقل من Flutter إلى الأصلي بعد سنتين؟
- كيف يختلف رسم الواجهة بين Flutter وReact Native؟

**علامات تحذير:** يختار التقنية لأنها الأحدث أو الأشهر دون سؤال عن الفريق.

**مقالات تشرح الموضوع:** [توثيق المعمارية: مخططات C4 وسجلات القرارات المعمارية ADR](https://arabroot.io/articles/%D8%AA%D9%88%D8%AB%D9%8A%D9%82-%D8%A7%D9%84%D9%85%D8%B9%D9%85%D8%A7%D8%B1%D9%8A%D8%A9-%D9%85%D8%AE%D8%B7%D8%B7%D8%A7%D8%AA-c4-%D9%88-adr/)، و[خارطة CTO لشركة SaaS ناشئة من اليوم الأول إلى خمسين موظفاً](https://arabroot.io/articles/%D8%AE%D8%A7%D8%B1%D8%B7%D8%A9-cto-%D9%84%D8%B4%D8%B1%D9%83%D8%A9-saas-%D9%86%D8%A7%D8%B4%D8%A6%D8%A9-%D8%A7%D9%84%D8%B3%D9%8A%D8%B1%D9%81%D8%B1%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D8%A8%D9%8A%D8%A6%D8%A7%D8%AA-%D9%88%D8%A7%D9%84%D9%81%D8%B1%D9%82/).

### 34\. ميزة في التطبيق تحتاج واجهة أصلية لا تدعمها مكتبة Flutter أو React Native، ماذا تفعل؟

خبير **ماذا يختبر:** العمل بين الطبقة المشتركة والطبقة الأصلية.

إجابة المبتدئ

نبحث عن مكتبة من المجتمع، وإذا لم نجد نكتب كوداً أصلياً.

إجابة الخبير

أقيم مكتبات المجتمع أولاً بصيانتها وآخر تحديث، لأن مكتبة متروكة تكسر التطبيق مع إصدار النظام التالي. وإذا كتبت بنفسي فأكتب وحدة أصلية صغيرة بواجهة ضيقة وواضحة (Platform Channels في Flutter، وTurbo Modules في المعمارية الجديدة لـ React Native)، وأنتبه لنقل البيانات عبر الحد بين الطبقتين لأنه مكلف في الاستدعاءات الكثيرة، وللخيوط: الكود الأصلي قد يستدعي على خيط غير الرئيسي. وأكتب اختبارات للوحدة على كل منصة، لأن هذا الكود يحتاج من يعرف Kotlin وSwift في الفريق.

أسئلة المتابعة
- لماذا قد يكون استدعاء الطبقة الأصلية ألف مرة في الثانية مشكلة؟
- من يصون الكود الأصلي إذا كان الفريق كله يعرف Dart فقط؟

**علامات تحذير:** لا يعرف كيف يتواصل الكود المشترك مع الأصلي.

**مقالات تشرح الموضوع:** [أنماط البرمجة وأنظمة الأنواع: لماذا تختلف اللغات؟](https://arabroot.io/articles/%D8%A7%D9%86%D9%85%D8%A7%D8%B7-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%88%D8%A7%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)، و[اللغة شيء والمترجم شيء آخر: من يقرر مستقبل لغات البرمجة؟](https://arabroot.io/articles/%D9%85%D9%88%D8%A7%D8%B5%D9%81%D8%A7%D8%AA-%D9%84%D8%BA%D8%A7%D8%AA-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D9%88%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA%D9%87%D8%A7-%D9%88%D8%AD%D9%88%D9%83%D9%85%D8%AA%D9%87%D8%A7/).

### 35\. ما الذي تشاركه بين تطبيقي Android وiOS الأصليين، وما الذي لا تشاركه؟

متوسط **ماذا يختبر:** تقسيم الطبقات وما يستحق المشاركة.

إجابة المبتدئ

نشارك التصميم وال API، وكل فريق يكتب الكود بنفسه.

إجابة الخبير

أشارك ما يتطابق منطقه ويكلف اختلافه: عقد ال API (ومنه أولد الكود)، ونماذج البيانات، والتحقق من المدخلات، وقواعد العمل الحسابية (حساب السعر والضريبة)، ورموز التصميم. والأداة لذلك قد تكون توليد الكود من OpenAPI، أو Kotlin Multiplatform لطبقة المنطق. ولا أشارك الواجهة والتنقل وتكامل النظام، لأن المستخدم يتوقع سلوك منصته، وفرض نفس السلوك يعطي تطبيقاً غريباً على المنصتين. والخطأ الشائع أن يختلف حساب السعر بين المنصتين وال Backend، والحل أن يحسب في السيرفر أصلاً.

أسئلة المتابعة
- لماذا يجب أن يحسب السعر النهائي في السيرفر وليس في التطبيق؟
- كيف تمنع اختلاف نصوص الأخطاء بين المنصتين؟

**علامات تحذير:** يكرر قواعد العمل في التطبيقين وال Backend.

**مقالات تشرح الموضوع:** [كيف تصمم API لا تضطر إلى كسره بعد سنة](https://arabroot.io/articles/%D8%AA%D8%B5%D9%85%D9%8A%D9%85-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-api-rest-%D9%88-grpc-%D9%88-graphql/)، و[Clean Architecture: كيف تعزل منطق العمل عن قاعدة البيانات](https://arabroot.io/articles/clean-architecture-%D9%88-hexagonal-%D8%A7%D8%AA%D8%AC%D8%A7%D9%87-%D8%A7%D9%84%D8%A7%D8%B9%D8%AA%D9%85%D8%A7%D8%AF%D9%8A%D8%A7%D8%AA/).

## سهولة الوصول والعربية

### 36\. كيف يستخدم شخص كفيف تطبيقك؟

مبتدئ **ماذا يختبر:** TalkBack وVoiceOver والتسميات.

إجابة المبتدئ

باستخدام قارئ الشاشة، فنضيف وصفاً للأزرار والصور.

إجابة الخبير

بقارئ الشاشة (TalkBack في Android وVoiceOver في iOS) يتنقل بين العناصر بالسحب ويسمع اسم كل عنصر ودوره وحالته، لذلك كل عنصر تفاعلي يحتاج تسمية عربية واضحة (`contentDescription` و`accessibilityLabel` و`Semantics` في Flutter)، والصور الزخرفية تخفى عنه، والعناصر المترابطة (بطاقة منتج بها اسم وسعر) تجمع في عنصر واحد حتى لا يمر على عشرة أجزاء، ومساحة اللمس لا تقل عن الحد الذي توصي به المنصة. وأختبر بنفسي بتشغيل القارئ وإغلاق عيني على المهمة الأساسية (تسجيل الدخول والشراء)، لأن الفحص الآلي لا يكتشف إلا جزءاً.

أسئلة المتابعة
- ما مشكلة زر أيقونة دون تسمية في قائمة طويلة؟
- كيف تعلن رسالة خطأ لقارئ الشاشة دون أن تنقل التركيز؟

**علامات تحذير:** لم يشغل قارئ الشاشة على تطبيقه أبداً.

**مقالات تشرح الموضوع:** [اختبار سهولة الوصول Accessibility لموقع عربي](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%B3%D9%87%D9%88%D9%84%D8%A9-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-accessibility-%D9%84%D9%85%D9%88%D9%82%D8%B9-%D8%B9%D8%B1%D8%A8%D9%8A/)، و[اختبار تطبيقات الجوال مع Appium وMaestro](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AC%D9%88%D8%A7%D9%84-%D9%85%D8%B9-appium-%D9%88-maestro/).

### 37\. ما الذي ينكسر عندما تشغل التطبيق بالعربية لأول مرة؟

متوسط **ماذا يختبر:** دعم RTL في الجوال.

إجابة المبتدئ

النصوص تكون على اليسار، فنفعل دعم RTL في ال Manifest.

إجابة الخبير

تفعيل `supportsRtl` واستخدام `start` و`end` بدلاً من `left` و`right` يقلب أغلب التخطيط تلقائياً (وكذلك `leading` و`trailing` في iOS، و`EdgeInsetsDirectional` في Flutter)، وما ينكسر بعد ذلك: الأيقونات التي لها اتجاه ولم تقلب، والرسوم المتحركة والإيماءات بالاتجاه الثابت (السحب للحذف)، والنصوص المختلطة من أسماء إنجليزية وأرقام، والقيم المحسوبة يدوياً بالبكسل، ولوحات الأرقام والتواريخ. وأختبر بالعربية كلغة للنظام على المنصتين، وبنصوص حقيقية لأن العربية تختلف في الطول عن الإنجليزية، ويمكن التحقق من انعكاس الواجهة آلياً في اختبارات E2E.

أسئلة المتابعة
- أي الأيقونات لا تقلب في الواجهة العربية؟
- كيف تعرض رقم هاتف أو بريد داخل نص عربي دون أن ينقلب؟

**علامات تحذير:** يقلب الواجهة بتطبيق مرآة على كل شيء بما فيه الصور والأرقام.

**مقالات تشرح الموضوع:** [اختبار تطبيقات الجوال مع Appium وMaestro](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AC%D9%88%D8%A7%D9%84-%D9%85%D8%B9-appium-%D9%88-maestro/)، و[اختبار سهولة الوصول Accessibility لموقع عربي](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%B3%D9%87%D9%88%D9%84%D8%A9-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-accessibility-%D9%84%D9%85%D9%88%D9%82%D8%B9-%D8%B9%D8%B1%D8%A8%D9%8A/).

### 38\. مستخدم كبر حجم الخط في الإعدادات فاختفت الأزرار، ماذا تصلح؟

متوسط **ماذا يختبر:** حجم الخط الديناميكي Dynamic Type وتصميم مرن.

إجابة المبتدئ

نمنع تكبير الخط في التطبيق حتى يبقى التصميم ثابتاً.

إجابة الخبير

منع التكبير يحرم من يحتاجه من استخدام التطبيق، والصحيح أن أصمم لتكبيره: أحجام الخط بوحدات تتبع إعداد المستخدم (`sp` في Android، وأنماط Dynamic Type في iOS، و`textScaler` في Flutter)، والعناصر تتمدد عمودياً ولا تحجز ارتفاعاً ثابتاً، والنصوص تلتف أو تنتقل إلى سطر جديد بدلاً من القطع، والصفوف الأفقية تصبح عمودية عند الأحجام الكبيرة. والخط العربي يحتاج انتباهاً أكثر لأن ارتفاعه وحروفه الصاعدة والنازلة أكبر، فالقطع يظهر عنده أولاً. وأختبر عند أكبر حجم في الإعدادات.

أسئلة المتابعة
- ما الفرق بين `sp` و`dp` في Android؟
- كيف تختبر كل الشاشات بأكبر حجم خط آلياً؟

**علامات تحذير:** يمنع تكبير الخط.

**المقال الذي يشرح الموضوع:** [اختبار سهولة الوصول Accessibility لموقع عربي](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%B3%D9%87%D9%88%D9%84%D8%A9-%D8%A7%D9%84%D9%88%D8%B5%D9%88%D9%84-accessibility-%D9%84%D9%85%D9%88%D9%82%D8%B9-%D8%B9%D8%B1%D8%A8%D9%8A/).

## الاختبارات

### 39\. ما الاختبارات التي تكتبها لتطبيق جوال، وعلى أي أجهزة تشغلها؟

متوسط **ماذا يختبر:** استراتيجية اختبار الجوال وتكلفتها.

إجابة المبتدئ

اختبارات وحدة للمنطق، واختبارات واجهة على المحاكي.

إجابة الخبير

أغلب الاختبارات على المنطق خارج الواجهة (ViewModel وقواعد العمل وطبقة البيانات) لأنها سريعة وتعمل على JVM أو دون جهاز، ثم اختبارات واجهة للمكونات، ثم عدد قليل من اختبارات E2E للمسارات الأساسية User Flows على محاكيات في CI بأدوات مثل Appium أو Maestro، ثم أجهزة حقيقية في مزرعة أجهزة قبل الإصدار، لأن المحاكي لا يكشف مشكلات الأداء والكاميرا والشبكة الحقيقية وتخصيصات الشركات المصنعة. وأختار الأجهزة من بيانات المستخدمين الفعلية: أكثر الطرز والإصدارات استخداماً، وأقدم جهاز ندعمه، وأصغر شاشة.

أسئلة المتابعة
- ما الذي لا يكشفه المحاكي؟
- كيف تختبر انقطاع الشبكة وموت العملية آلياً؟

**علامات تحذير:** يعتمد على الاختبار اليدوي قبل كل إصدار.

**مقالات تشرح الموضوع:** [اختبار تطبيقات الجوال مع Appium وMaestro](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AC%D9%88%D8%A7%D9%84-%D9%85%D8%B9-appium-%D9%88-maestro/)، و[أنواع الاختبارات للمبتدئين: من Unit إلى E2E مع Playwright](https://arabroot.io/articles/%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-unit-%D9%88-integration-%D9%88-e2e/).

### 40\. أي أداة تختار لاختبارات الواجهة: Appium أم Maestro أم أدوات المنصة؟

خبير **ماذا يختبر:** معرفة الأدوات والموازنة بينها.

إجابة المبتدئ

Appium لأنه يعمل على المنصتين.

إجابة الخبير

أدوات المنصة (Espresso وCompose Testing في Android، وXCUITest في iOS) هي الأسرع والأثبت لأنها تعمل داخل عملية التطبيق أو بجانبه وتعرف متى يكون التطبيق خاملاً، ولكنها تحتاج كتابة الاختبار مرتين. وAppium يكتب مرة للمنصتين عبر بروتوكول WebDriver، وثمنه السرعة والهشاشة. وMaestro يكتب مسارات المستخدم بملفات YAML بسيطة وينتظر تلقائياً، فهو مناسب للمسارات الأساسية وللفرق التي يكتب فيها غير المطورين اختبارات. والاختيار حسب من يكتب الاختبار وكم منصة، وأغلب الفرق تجمع: أدوات المنصة للمكونات، وأداة واحدة مشتركة لعدد قليل من المسارات الكاملة.

أسئلة المتابعة
- لماذا تكون اختبارات Espresso أقل هشاشة من Appium؟
- كيف تختبر الواجهة العربية في نفس الاختبارات؟

**علامات تحذير:** لا يعرف إلا أداة واحدة ويستخدمها لكل شيء.

**مقالات تشرح الموضوع:** [اختبار تطبيقات الجوال مع Appium وMaestro](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AC%D9%88%D8%A7%D9%84-%D9%85%D8%B9-appium-%D9%88-maestro/)، و[أدوات ضمان الجودة QA: أيها يناسب فريقك؟](https://arabroot.io/articles/%D8%A7%D8%AF%D9%88%D8%A7%D8%AA-%D8%B6%D9%85%D8%A7%D9%86-%D8%A7%D9%84%D8%AC%D9%88%D8%AF%D8%A9-qa-%D9%88%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%A7%D9%84%D8%A8%D8%B1%D9%85%D8%AC%D9%8A%D8%A7%D8%AA/).

## الأمن في تطبيقات الجوال

### 41\. كيف تقيم أمن تطبيق جوال قبل إطلاقه؟

متوسط **ماذا يختبر:** معرفة معيار OWASP MASVS ونطاق المراجعة.

إجابة المبتدئ

نختبر أن الاتصال عبر HTTPS وأن كلمات المرور لا تحفظ في الجهاز.

إجابة الخبير

أستخدم [OWASP MASVS](https://mas.owasp.org/MASVS/?ref=arabroot.io) كقائمة لمجالات المراجعة: التخزين، والتشفير، والمصادقة، والشبكة، والتكامل مع النظام (الروابط العميقة، وال WebView، والأذونات)، والكود، والمرونة أمام الهندسة العكسية، والخصوصية. وأتذكر أن أغلب الحماية الحقيقية في ال Backend: التطبيق قد يعدل أو يستبدل، فكل تحقق في التطبيق للراحة، والسيرفر يتحقق من كل شيء. وأفك تطبيقي بنفسي (ال APK بأداة مثل jadx) لأرى ما يراه المهاجم: الروابط، والمفاتيح، والسجلات المتروكة.

أسئلة المتابعة
- ماذا تجد عادة عندما تفك APK منشوراً؟
- ما الذي يضيفه فحص المرونة Resilience، ومتى لا يستحق؟

**علامات تحذير:** يعتمد على التحقق في التطبيق فقط.

**مقالات تشرح الموضوع:** [ما هي OWASP؟ مشاريعها ووثائقها ومتى تستخدم كلاً منها](https://arabroot.io/articles/%D9%85%D8%A7-%D9%87%D9%8A-owasp-%D9%85%D8%B4%D8%A7%D8%B1%D9%8A%D8%B9%D9%87%D8%A7-%D9%88%D9%88%D8%AB%D8%A7%D8%A6%D9%82%D9%87%D8%A7-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)، و[أمن ال API بالأمثلة مع OWASP API Security Top 10](https://arabroot.io/articles/%D8%A7%D9%85%D9%86-%D9%88%D8%A7%D8%AC%D9%87%D8%A7%D8%AA-api-%D9%85%D8%B9-owasp-api-security-top-10/).

### 42\. هل تمنع التطبيق من العمل على جهاز مكسور الحماية Root أو Jailbreak؟

خبير **ماذا يختبر:** الحكم على إجراءات الحماية ذات الأثر المحدود.

إجابة المبتدئ

نعم، لأن الجهاز المكسور غير آمن، فنكشفه ونغلق التطبيق.

إجابة الخبير

كشف كسر الحماية يمكن تجاوزه دائماً بأدوات معروفة، فهو يرفع التكلفة على المهاجم العادي ولا يوقف المهاجم المصمم، ويمنع مستخدمين حقيقيين لهم أسبابهم. لذلك أقرر حسب الخطر: تطبيق بنكي أو تطبيق يحمي محتوى مدفوعاً قد يقيد بعض العمليات الحساسة أو يطلب تحققاً إضافياً بدلاً من الإغلاق الكامل، ويستخدم خدمات التحقق من سلامة الجهاز والتطبيق (Play Integrity وApp Attest) التي يتحقق منها السيرفر وليس التطبيق. وتطبيق عادي لا يحتاج ذلك، والأهم أن السيرفر لا يثق بأي شيء يأتي من الجهاز.

أسئلة المتابعة
- لماذا يجب أن يتحقق السيرفر من نتيجة Play Integrity وليس التطبيق؟
- ما الذي يخسره التطبيق إذا أغلق على كل جهاز مكسور الحماية؟

**علامات تحذير:** يعتبر كشف كسر الحماية حماية كاملة.

**مقالات تشرح الموضوع:** [BYOD: كيف تحمي بيانات الشركة على أجهزة الموظفين الشخصية](https://arabroot.io/articles/%D8%A7%D9%84%D8%A7%D8%AC%D9%87%D8%B2%D8%A9-%D8%A7%D9%84%D8%B4%D8%AE%D8%B5%D9%8A%D8%A9-%D9%81%D9%8A-%D8%A7%D9%84%D8%B9%D9%85%D9%84-byod/)، و[نمذجة التهديدات Threat Modeling للمبتدئين](https://arabroot.io/articles/%D9%86%D9%85%D8%B0%D8%AC%D8%A9-%D8%A7%D9%84%D8%AA%D9%87%D8%AF%D9%8A%D8%AF%D8%A7%D8%AA-threat-modeling-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/).

## مراجعة كود الذكاء الاصطناعي

### 43\. أداة الذكاء الاصطناعي كتبت شاشة كاملة بالشبكة والتخزين، ماذا تراجع؟

متوسط **ماذا يختبر:** الأخطاء التي يكتبها الكود المولد في الجوال تحديداً.

إجابة المبتدئ

أتأكد أنها تعمل على المحاكي وتطابق التصميم.

إجابة الخبير

أراجع ما لا يظهر على المحاكي في دقيقة: أين يحدث العمل (شبكة أو تحليل على الخيط الرئيسي)، وما يحدث عند الدوران وموت العملية والخروج من الشاشة أثناء الطلب، وأين يحفظ التوكن (الكود المولد يحب SharedPreferences وUserDefaults)، وهل هناك دورة مراجع في الـ Closures، وهل تعمل الشاشة بالعربية وبخط كبير وبقارئ الشاشة، وهل أضافت الأداة أذونات أو مكتبات لا نحتاجها، وهل استخدمت واجهات قديمة لم تعد موصى بها في إصدار النظام الحالي. ثم أشغلها على جهاز حقيقي قديم بشبكة بطيئة.

أسئلة المتابعة
- الأداة استخدمت واجهة برمجية أصبحت قديمة في الإصدار الحالي من النظام، كيف تكتشف ذلك؟
- كيف تجعل هذه المراجعة قائمة تحقق في كل Pull Request؟

**علامات تحذير:** يقبل الشاشة لأنها تعمل على المحاكي.

**مقالات تشرح الموضوع:** [اختبارات يكتبها الذكاء الاصطناعي: متى تثق بها؟](https://arabroot.io/articles/%D8%A7%D8%AA%D9%85%D8%AA%D8%A9-%D8%A7%D9%84%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A8%D8%A7%D9%84%D8%B0%D9%83%D8%A7%D8%A1-%D8%A7%D9%84%D8%A7%D8%B5%D8%B7%D9%86%D8%A7%D8%B9%D9%8A/)، و[اختبار تطبيقات الجوال مع Appium وMaestro](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AC%D9%88%D8%A7%D9%84-%D9%85%D8%B9-appium-%D9%88-maestro/).

## تمارين عملية للمقابلة

تشغيل تطبيق Android أو iOS كامل داخل Docker مكلف ولا يناسب جلسة مقابلة، لذلك اخترنا ثلاثة تمارين تعزل الفكرة نفسها وتعمل في دقائق: Dart 3.13.5 (لغة Flutter) لمسألة الخيط الرئيسي، وSwift 6.4 على Linux لمسألة الذاكرة، وNode.js 24.21.0 لمسألة المزامنة، كلها داخل Docker Images الرسمية، والمخرجات المنشورة هي المخرجات الحقيقية. والفكرة في كل تمرين هي نفسها على المنصة الحقيقية، كما نشرح في الإجابة.

```bash
docker run --rm -v "$PWD:/w" -w /w dart:3.13.5 dart run jank.dart
docker run --rm -v "$PWD:/w" -w /w swift:6.4.0-noble sh -c 'swiftc -O Leak.swift -o /tmp/leak && /tmp/leak'
docker run --rm -v "$PWD:/w" -w /w node:24-alpine node sync.mjs
```

### التمرين الأول: التطبيق يتجمد أثناء التحليل

في Flutter يرسم المحرك إطاراً كل 16 ملي ثانية تقريباً على شاشة 60 هرتز، والكود التالي يحاكي ذلك بمؤقت دوري على نفس ال Isolate، ثم ينفذ عملاً حسابياً ثقيلاً (يمثل تحليل ملف JSON كبير أو ضغط صورة) بطريقتين، ويقيس كم إطاراً رسم وما أطول فجوة بين إطارين:

```dart
// jank.dart: a 16 ms "frame" timer on the main isolate, and heavy work in the middle
import 'dart:async';
import 'dart:isolate';
import 'dart:math';

int fib(int n) => n < 2 ? n : fib(n - 1) + fib(n - 2);

Future<void> measure(String label, Future<int> Function() job) async {
  final sw = Stopwatch()..start();
  var frames = 0, last = 0, worstGap = 0;
  final ticker = Timer.periodic(const Duration(milliseconds: 16), (_) {
    final now = sw.elapsedMilliseconds;
    worstGap = max(worstGap, now - last);
    last = now;
    frames++;
  });
  await Future.delayed(const Duration(milliseconds: 100));
  final t0 = sw.elapsedMilliseconds;
  final result = await job();
  final workMs = sw.elapsedMilliseconds - t0;
  await Future.delayed(const Duration(milliseconds: 100));
  ticker.cancel();
  print('${label.padRight(14)} fib=$result work=${workMs}ms total=${sw.elapsedMilliseconds}ms '
      'frames=$frames worst gap=${worstGap}ms');
}

Future<void> main() async {
  await measure('main isolate', () async => fib(38));          // blocks the event loop
  await measure('Isolate.run', () => Isolate.run(() => fib(38))); // runs on another isolate
}
```

والمخرج سوف يكون كما يلي:

```text
Dart SDK version: 3.13.5 (stable) (Tue Sep 29 01:00:52 2026 -0700) on "linux_x64"
main isolate   fib=39088169 work=693ms total=901ms frames=14 worst gap=703ms
Isolate.run    fib=39088169 work=576ms total=777ms frames=48 worst gap=20ms
```

اسأل المرشح: الدالة `job` في الحالة الأولى `async`، فلماذا توقفت الإطارات 703 ملي ثانية؟ وماذا يرى المستخدم خلالها؟ وما ثمن الحل الثاني؟ والصورة التالية تبين الحالتين على خط زمني:

![خطان زمنيان، في الأعلى الخيط الرئيسي يرسم إطارات خضراء كل 16 ملي ثانية ثم كتلة حمراء طويلة لعمل ثقيل تختفي خلالها الإطارات وتظهر فجوة 703 ملي ثانية، وفي الأسفل نفس العمل على Isolate منفصل والخيط الرئيسي يستمر في رسم الإطارات الخضراء بفجوة أقصاها 20 ملي ثانية](https://arabroot.io/content/images/2026/10/interview-mobile-01-main-thread.webp)

العمل الثقيل على الخيط الرئيسي يوقف الإطارات، وعلى Isolate آخر يتركها تعمل

الإجابة 

كلمة `async` لا تنقل العمل إلى خيط آخر، فهي تعني أن الدالة ترجع `Future` وأنها تستطيع أن تنتظر دون أن تحجز الحلقة أثناء الانتظار، ولكن `fib(38)` لا ينتظر شيئاً، وإنما يحسب 693 ملي ثانية متصلة على حلقة الأحداث الوحيدة لل Isolate الرئيسي، فلا يستطيع المؤقت أن يعمل، وكانت أطول فجوة 703 ملي ثانية رسم خلالها صفر إطارات. وفي تطبيق حقيقي هذا يعني أن الشاشة متجمدة وأن اللمس لا يستجيب ومؤشر التحميل نفسه متوقف عن الدوران.

وفي الحالة الثانية `Isolate.run` شغل نفس الحساب في Isolate آخر له ذاكرته وحلقته الخاصة، فاستمرت الإطارات (48 إطاراً بفجوة أقصاها 20 ملي ثانية). وثمن ذلك أن ال Isolates لا تتشارك الذاكرة، فالمدخلات والنتيجة تنسخ بينهما، ومع بيانات ضخمة قد يصبح النسخ نفسه ملحوظاً، وإنشاء Isolate لكل مهمة صغيرة أبطأ من تنفيذها مباشرة، كما تشرح [وثائق Dart](https://dart.dev/language/isolates?ref=arabroot.io) و[وثائق Flutter](https://docs.flutter.dev/perf/isolates?ref=arabroot.io). ولاحظ أن زمن العمل نفسه يختلف بين التشغيلين، فهذه أرقام من جهاز واحد للمقارنة وليست قياساً دقيقاً لأداء Dart.

ونفس الدرس على المنصات الأخرى: في Kotlin نقل العمل الحسابي إلى `Dispatchers.Default` وليس فقط جعله `suspend`، وفي Swift لا يكفي `async` إذا بقي الكود على `@MainActor`، وفي React Native كود JavaScript الثقيل يحجز خيط JavaScript فلا تستجيب الواجهة. وفي Android إذا بقي الخيط الرئيسي محجوباً عن اللمس 5 ثوان يظهر ANR.

### التمرين الثاني: شاشة لا تغادر الذاكرة

شاشة ملف شخصي تحفظ دالة `onUpdate` تستدعى عند تحديث البيانات، والمطور لاحظ أن الذاكرة تزيد كلما فتح المستخدم الشاشة وأغلقها. الكود التالي يفتح الشاشة بطريقتين ويتحقق بعد الخروج من النطاق هل حررت، عبر مرجع ضعيف `weak` لا يبقيها حية:

```swift
// Leak.swift: a screen that never leaves memory
final class ProfileScreen {
    let name: String
    var onUpdate: (() -> Void)?
    init(name: String) { self.name = name; print("init \(name)") }
    deinit { print("deinit \(name)") }

    func bindStrong() {
        onUpdate = { print("update \(self.name)") }          // the closure keeps self alive
    }
    func bindWeak() {
        onUpdate = { [weak self] in
            guard let self else { return }
            print("update \(self.name)")
        }
    }
}

weak var probe: ProfileScreen?
do {
    let screen = ProfileScreen(name: "strong")
    screen.bindStrong()
    screen.onUpdate?()
    probe = screen
}
print("after leaving strong screen: \(probe == nil ? "freed" : "still in memory")")

do {
    let screen = ProfileScreen(name: "weak")
    screen.bindWeak()
    screen.onUpdate?()
    probe = screen
}
print("after leaving weak screen: \(probe == nil ? "freed" : "still in memory")")
```

والمخرج سوف يكون كما يلي:

```text
Swift version 6.4 (swift-6.4-RELEASE)
init strong
update strong
after leaving strong screen: still in memory
init weak
update weak
deinit weak
after leaving weak screen: freed
```

اسأل المرشح: لماذا لم يطبع `deinit strong` أبداً؟ وما الذي تغير في `bindWeak`؟ ومتى لا تحتاج `[weak self]`؟

الإجابة 

Swift تدير الذاكرة بعد المراجع ARC، والكائن يحرر عندما يصل عدد مراجعه القوية إلى صفر، كما تشرح [وثائق Swift](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting/?ref=arabroot.io). وفي `bindStrong` الشاشة تملك ال Closure عبر الخاصية `onUpdate`، وال Closure تمسك الشاشة بقوة لأنها تستخدم `self.name`، فعندما خرج المتغير `screen` من النطاق بقي لكل منهما مرجع قوي من الآخر، وهذه دورة مراجع Retain Cycle، فلم يستدع `deinit` وبقيت الشاشة في الذاكرة إلى أن ينتهي البرنامج.

وفي `bindWeak` تمسك ال Closure الشاشة بمرجع ضعيف `[weak self]` لا يزيد العداد، فعند الخروج من النطاق وصل العداد إلى صفر وحررت الشاشة وطبع `deinit weak`، و`guard let self` يتعامل مع الحالة التي تستدعى فيها ال Closure بعد تحرير الشاشة. ولا تحتاج `[weak self]` عندما لا يخزن الكائن ال Closure، مثل Closure تمرر إلى `DispatchQueue.main.async` أو `map`، فهي تنفذ وتنتهي دون دورة.

وفي تطبيق حقيقي تظهر هذه الدورة مع المؤقتات و`NotificationCenter` والمستمعين في ViewModel، وتكتشف بأداة Memory Graph في Xcode، ونظيرها في Android كائن عمره أطول يمسك Activity، وتكتشفه مكتبة LeakCanary.

### التمرين الثالث: تعديلان دون اتصال على نفس السعر

هاتفان لموظفين في نفس المتجر مزامنتهما الأخيرة كانت على الإصدار 1 من سعر منتج، وكل منهما عدل السعر دون اتصال: الهاتف A في الساعة 10:01، والهاتف B في 10:03 بالوقت الحقيقي ولكن ساعته متأخرة عشر دقائق. ثم اتصل A بالشبكة في 10:05 وB في 10:06\. والكود التالي سيرفر صغير بطريقتين للمزامنة، وطلبات حقيقية من الهاتفين:

```javascript
// sync.mjs: two phones edit the same note offline, then sync
import http from "node:http";

const note = { text: "price: 100", version: 1, updatedAt: Date.parse("2026-10-12T10:00:00Z") };
let mode = "lww";

const server = http.createServer((req, res) => {
  let body = "";
  req.on("data", (c) => (body += c));
  req.on("end", () => {
    const edit = JSON.parse(body);
    if (mode === "lww") {                       // last write wins, by the phone's clock
      if (edit.updatedAt > note.updatedAt) Object.assign(note, { text: edit.text, updatedAt: edit.updatedAt });
      note.version++;
      res.writeHead(200).end(JSON.stringify(note));
    } else {                                    // optimistic concurrency: If-Match on the version
      if (req.headers["if-match"] !== `"${note.version}"`) {
        res.writeHead(412, { etag: `"${note.version}"` }).end(JSON.stringify(note));
        return;
      }
      Object.assign(note, { text: edit.text, version: note.version + 1, updatedAt: Date.now() });
      res.writeHead(200, { etag: `"${note.version}"` }).end(JSON.stringify(note));
    }
  });
});
await new Promise((r) => server.listen(0, "127.0.0.1", r));
const url = `http://127.0.0.1:${server.address().port}/notes/1`;

async function push(phone, text, clock, baseVersion) {
  const r = await fetch(url, { method: "PUT", headers: { "if-match": `"${baseVersion}"` },
    body: JSON.stringify({ text, updatedAt: Date.parse(clock) }) });
  const j = await r.json();
  console.log(`  ${phone} sends "${text}" (phone clock ${clock.slice(11, 16)}) -> ${r.status}, server has "${j.text}" v${j.version}`);
}

// Both phones last synced at version 1. Phone B's clock is 10 minutes behind.
// Real order of edits: A edits at 10:01, B edits at 10:03. A syncs at 10:05, B at 10:06.
console.log("last write wins:");
await push("phone A", "price: 120", "2026-10-12T10:01:00Z", 1);
await push("phone B", "price: 150", "2026-10-12T09:53:00Z", 1);

Object.assign(note, { text: "price: 100", version: 1, updatedAt: Date.parse("2026-10-12T10:00:00Z") });
mode = "version";
console.log("version check (If-Match):");
await push("phone A", "price: 120", "2026-10-12T10:01:00Z", 1);
await push("phone B", "price: 150", "2026-10-12T09:53:00Z", 1);
server.close();
```

والمخرج سوف يكون كما يلي:

```text
last write wins:
  phone A sends "price: 120" (phone clock 10:01) -> 200, server has "price: 120" v2
  phone B sends "price: 150" (phone clock 09:53) -> 200, server has "price: 120" v3
version check (If-Match):
  phone A sends "price: 120" (phone clock 10:01) -> 200, server has "price: 120" v2
  phone B sends "price: 150" (phone clock 09:53) -> 412, server has "price: 120" v2
```

اسأل المرشح: في الطريقة الأولى رجع السيرفر 200 للهاتف B، فأين ذهب سعره؟ وماذا كان سيحدث لو كانت ساعة B صحيحة؟ وماذا يفعل التطبيق عندما يستلم 412؟

الإجابة 

في الطريقة الأولى قارن السيرفر وقت التعديل بساعة كل هاتف، وساعة B المتأخرة جعلت تعديله (الأحدث في الواقع) يبدو أقدم، فاحتفظ السيرفر بسعر A ورجع 200 للهاتف B، فظن موظف B أن سعره حفظ، وهذا أسوأ ما في الطريقة: فقدان صامت لا يعرفه أحد. ولو كانت ساعة B صحيحة لفاز B وضاع تعديل A بصمت بدلاً منه، فالمشكلة ليست في الساعة وحدها، وإنما في أن «آخر كتابة تفوز» تحذف أحد التعديلين دائماً دون أن تخبر أحداً.

وفي الطريقة الثانية كل تعديل يرسل الإصدار الذي بني عليه في ترويسة `If-Match`، والسيرفر يقبل التعديل فقط إذا كان الإصدار الحالي هو نفسه، وإلا يرجع 412 Precondition Failed مع النسخة الحالية، كما تحدد [مواصفة HTTP](https://www.rfc-editor.org/rfc/rfc9110?ref=arabroot.io#name-if-match). والتطبيق عندما يستلم 412 لا يعيد المحاولة تلقائياً، وإنما يعرض على المستخدم أن السعر تغير إلى 120 من جهاز آخر ويسأله هل يطبق 150 فوقه، أو يدمج تلقائياً إذا كان التعديلان على حقلين مختلفين.

والمرشح الخبير يضيف: الإصدار يأتي من السيرفر وليس من ساعة الجهاز، والطابور المحلي يحتفظ بالإصدار الأصلي لكل تعديل، ومع النصوص التي يحررها عدة أشخاص في نفس الوقت توجد هياكل CRDT تدمج دون تعارض، ولكنها أثقل بكثير من حاجة شاشة سعر.

## الخلاصة

وصلنا لنهاية الموضوع، وأهم ما فيه:

- اختر ثمانية أسئلة تقريباً لكل جلسة من مجالات مختلفة، واسأل عن الفكرة في منصة المرشح نفسها.
- الفرق بين المبتدئ والخبير في الجوال يظهر في دورة الحياة والخيط الرئيسي والعمل دون اتصال والنشر، وليس في أسماء الواجهات البرمجية.
- التمارين العملية تكشف ما لا تكشفه الأسئلة: فجوة 703 ملي ثانية خلف دالة `async`، وشاشة لا يستدعى لها `deinit`، و200 تخفي تعديلاً ضائعاً.
- كل سؤال مرتبط بمقال يشرح الموضوع في عرب رووت، ومنها [اختبار تطبيقات الجوال مع Appium وMaestro](https://arabroot.io/articles/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D8%AC%D9%88%D8%A7%D9%84-%D9%85%D8%B9-appium-%D9%88-maestro/) للأسئلة عن الاختبارات.

وبقية مقالات السلسلة:

[![](https://arabroot.io/content/images/2026/10/interview-guide-cover-cover.webp)دليلكيف تدير مقابلة تقنية تكشف مستوى المرشح؟كيف تبني مقابلة تقنية منظمة تفرق بين المبتدئ والخبير بأسئلة المتابعة والتقييم بالدرجات بدلاً من الانطباع.](https://arabroot.io/articles/%D9%83%D9%8A%D9%81-%D8%AA%D8%AF%D9%8A%D8%B1-%D9%85%D9%82%D8%A7%D8%A8%D9%84%D8%A9-%D8%AA%D9%82%D9%86%D9%8A%D8%A9-%D8%AA%D9%83%D8%B4%D9%81-%D9%85%D8%B3%D8%AA%D9%88%D9%89-%D8%A7%D9%84%D9%85%D8%B1%D8%B4%D8%AD/)[![](https://arabroot.io/content/images/2026/10/interview-backend-cover-cover.webp)دليلأسئلة مقابلة مطور Backend من المبتدئ إلى الخبيربنك أسئلة لمقابلة مطور Backend، لكل سؤال إجابة المبتدئ وإجابة الخبير وأسئلة متابعة، مع تمارين عملية شغلناها فعلاً.](https://arabroot.io/articles/%D8%A7%D8%B3%D8%A6%D9%84%D8%A9-%D9%85%D9%82%D8%A7%D8%A8%D9%84%D8%A9-%D9%85%D8%B7%D9%88%D8%B1-backend-%D9%85%D9%86-%D8%A7%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6-%D8%A7%D9%84%D9%89-%D8%A7%D9%84%D8%AE%D8%A8%D9%8A%D8%B1/)[![](https://arabroot.io/content/images/2026/10/interview-frontend-cover-cover.webp)دليلأسئلة مقابلة مطور Frontend من المبتدئ إلى الخبيربنك أسئلة لمقابلة مطور Frontend، لكل سؤال إجابة المبتدئ وإجابة الخبير وأسئلة متابعة، مع تمارين عملية شغلناها فعلاً.](https://arabroot.io/articles/%D8%A7%D8%B3%D8%A6%D9%84%D8%A9-%D9%85%D9%82%D8%A7%D8%A8%D9%84%D8%A9-%D9%85%D8%B7%D9%88%D8%B1-frontend-%D9%85%D9%86-%D8%A7%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6-%D8%A7%D9%84%D9%89-%D8%A7%D9%84%D8%AE%D8%A8%D9%8A%D8%B1/)

## سجل التحديثات

- أكتوبر 2026: كتابة المقال، وتشغيل التمارين على Dart 3.13.5 وSwift 6.4 وNode.js 24.21.0 داخل Docker على Windows 11، والتحقق من حدود Android vitals وANR من وثائق Android.