الهندسة
لماذا تفشل تطبيقات الميدان: بناء برمجيات هاتف تعمل دون اتصال أولاً
تطبيق يُعرَض على واي فاي المكتب ويُستخدم عند بوابة موقع بإشارة 4G ضعيفة هما برمجيتان مختلفتان. واحدة منهما فقط هي التي اختُبرت.
النمط متكرّر بما يكفي للتنبّؤ به. يُطلَب تطبيق ميداني، ويُبنى بكفاءة، ويُعرَض بنجاح، ويُنشَر. وخلال شهرين يعود فريق الميدان إلى الورق، والسبب المعلَن أنه «كان يفقد البيانات باستمرار». وعادةً لم يفقد البيانات — بل عجز عن استلامها، ثم لم يقل شيئاً، والفارق غير مرئي لمن يقف عند البوابة.
ما إن يفقد موظفو الميدان الثقة بتطبيق حتى يتوقّفوا عن استخدامه، ولا يعيدهم أي قدر من المزايا. الثقة هي المنتج الحقيقي، وتتحدّد بما يفعله التطبيق على اتصال سيّئ.
لماذا تنهار الافتراضات حول الاتصال
واي فاي المكتب ليس نسخة ضعيفة من اتصال الموقع — بل شيء مختلف. الموقع فيه تغطية متقطّعة لا تغطية بطيئة: إشارة كاملة عند المكتب، ولا شيء خلف البناء، وبوابة أسيرة في مبنى العميل تعيد HTTP 200 لكل طلب مع صفحة تسجيل دخول في المتن.
تستحق الحالة الأخيرة وقفة، لأنها تهزم الفحوص الساذجة للاتصال. الجهاز يبلّغ عن وجود شبكة. والطلب ينجح. والاستجابة ليست ما طُلب. والتطبيق الذي يفحص «هل يوجد اتصال» بدل «هل أجاب الخادم فعلاً» سيصدّق بسرور أنه حفظ شيئاً لم يحفظه.
الافتراض الآخر الذي ينهار هو المدة. تطبيق يتوقّع المزامنة خلال دقائق يسلك سلوكاً مختلفاً عن تطبيق عليه التعامل مع جهاز منقطع ثلاثة أيام، تحرّكت بيانات الخادم خلالها. والثاني هو الحالة الطبيعية في العمل الميداني وهو يغيّر التصميم.
العمل دون اتصال أولاً يعني أن المخزن المحلي هو الحقيقة
الفارق ليس «نخزّن بعض الأشياء مؤقتاً». في تصميم يعمل دون اتصال أولاً، قاعدة البيانات المحلية هي حيث يعيش عمل المستخدم، وتكتمل عمليات الكتابة محلياً وتعود فوراً، والمزامنة عملية خلفية توفّق مع الخادم لاحقاً. الشبكة تحسين لا شرط مسبق.
هذا يقلب المعالجة المعتادة للأخطاء. في تطبيق يفترض الاتصال أولاً، انعدام الشبكة خطأ على المستخدم أن يتعامل معه. أما في تطبيق يعمل دون اتصال أولاً، فانعدام الشبكة حالة طبيعية وتعكسها الواجهة — السجل محفوظ، وهو في الطابور، وسيذهب حين يستطيع، والمستخدم يرى أن ذلك صحيح.
اختر نموذج التعارض عن قصد
شخصان يعدّلان السجل نفسه وكلاهما دون اتصال. ما يحدث بعد ذلك هو نموذج التعارض لديك، وإن لم يخترْه أحد فقد اختاره الكود مصادفةً — عادةً «آخر كتابة تفوز»، بصمت، مع اختفاء عمل أحدهما.
الخيارات العملية قليلة، والخيار الصحيح يتوقّف على البيانات.
- «آخر كتابة تفوز» مقبول للحقول المستقلة فعلاً وللبيانات التي لا يضرّ تأخّرها قليلاً. وهو الخيار الخاطئ لأي شيء مالي أو أي شيء قد يترتّب عليه نزاع.
- سجلات الأحداث بنمط الإضافة فقط تتجنّب التعارض كلياً لأنها لا تعدّل في مكانها أبداً. تسجيل الحضور وصرف المواد وحركات المخزون وسجلات التفتيش كلها تناسب ذلك طبيعياً — مشرفان يسجّلان الحضور يضيفان وقائع، لا يتنافسان على الكتابة فوق واحدة.
- الدمج على مستوى الحقول ينفع حين يكون للسجل أقسام تخصّ أدواراً مختلفة، فلا يمسّ تعديلان الحقول نفسها عادةً.
- الحل الصريح — عرض النسختين والسؤال — صحيح حيث تكون البيانات مهمة بما يكفي لتستحق انتباه أحد، وغير محتمل إن تكرّر كثيراً. وإن احتاجه نموذجك كثيراً فالنموذج خاطئ.
في العمليات الميدانية، يناسب نمط الإضافة فقط حالات أكثر بكثير ممّا تتوقّع الفرق، وهو يزيل أصعب صنف من العلل بحكم البنية. ويستحق الأمر إعادة هيكلة البيانات للوصول إليه.
الطابور هو الجزء الذي يجب أن يكون محكماً تماماً
كل ما يفعله المستخدم دون اتصال يدخل طابوراً، وكل نمط فشل خطير يسكن هناك. والطابور يجب أن ينجو من إغلاق نظام التشغيل للتطبيق، ومن إعادة تشغيل الهاتف، ومن تحديث التطبيق إلى إصدار تغيّر شكل بياناته.
ثلاث قواعد تجعله موثوقاً. كل عملية في الطابور تحمل معرّفاً يولّده العميل كي يتعرّف الخادم على المكرّر — فإعادة المحاولة مؤكّدة، وبلا عدم تأثّر بالتكرار تُنشئ سجلات مكرّرة. والعمليات تُطبَّق بالترتيب حيث يهمّ الترتيب، لأن عملية إنشاء تصل بعد تحديثها هي علّة فقدان بيانات. والعنصر الفاشل بصفة دائمة يجب عزله وإظهاره بدل إعادة محاولته إلى الأبد، وإلا حجب سجل سيّئ واحد كل سجل سليم خلفه.
الحالة الأخيرة هي أشيع فشل في الإنتاج. سجل يرفضه الخادم لسبب تحقّق يبقى في مقدّمة الطابور يعيد المحاولة، بينما ينتظر كل ما خلفه. الهاتف يعرض «تتم المزامنة». ولم تتم أي مزامنة منذ يومين.
اجعل حالة المزامنة ظاهرة
المستخدمون يسامحون تطبيقاً لا يستطيع بلوغ الخادم. ولا يسامحون تطبيقاً لا يستطيع إخبارهم إن بلغه. كل سجل ينبغي أن يُظهر إن كان محفوظاً محلياً أو في الطابور أو مزامَناً أو فاشلاً، وينبغي أن تكون هناك شاشة واحدة تقول متى تمّت آخر مزامنة ناجحة وكم عنصراً ينتظر.
وهذا يغيّر الدعم أيضاً. «لم يُحفظ» لا إجابة له. أما «ثلاثة عناصر في الطابور، آخر مزامنة قبل يومين، وواحد فشل بسبب خطأ تحقّق» فسؤال له إجابة، ويمكن تقديمها عبر الهاتف.
عمل في الخلفية يجري فعلاً
تحسين البطارية في Android سيوقف أي مزامنة خلفية ساذجة، وواجهات المصنّعين أشدّ عدوانية من Android الأصلي — بعضها بفارق كبير. وتطبيق يزامن بموثوقية على Pixel قد لا يزامن إطلاقاً على هاتف يقتل مصنّعه العمل في الخلفية بعدوانية، وتلك الهواتف شائعة تحديداً في الفئة السعرية التي يحملها موظفو الميدان.
استخدم مجدوِل المنصّة نفسه بدل مؤقّت، وزامن انتهازياً حين يكون التطبيق في المقدّمة، ولا تعتمد أبداً على العمل في الخلفية وحده لشيء يحتاج المستخدم أن يكون قد تمّ. واختبر على الهواتف الرخيصة التي يحملها مستخدموك، لا على جهاز المطوّر.
اختبر على الشبكة التي لديهم
- وضع الطيران ليوم عمل كامل، ثم أعد الاتصال وتحقّق من وصول كل شيء مرة واحدة بالضبط.
- اتصال مقيّد وفاقد للبيانات لا مجرّد بطيء — أنماط الفشل مختلفة، وفقدان البيانات هو ما يكسر الأمور.
- بوابة أسيرة تعيد 200 مع صفحة تسجيل دخول، للتأكّد من أن التطبيق يفحص الاستجابة لا مجرّد وجود الاتصال.
- أغلق التطبيق أثناء المزامنة، وأعد تشغيل الهاتف أثناء المزامنة، وتأكّد من بقاء الطابور في الحالتين.
- جهازان يعدّلان السجل نفسه دون اتصال، للتأكّد من أن نموذج التعارض يفعل ما قرّرته أنت لا ما يفعله إطار العمل افتراضياً.
- إصدار قديم من التطبيق يزامن مع خادم حديث، لأن في عمليات النشر الميدانية هناك دائماً من هو متأخّر بثلاثة إصدارات.
الخلاصة
العمل دون اتصال ليس ميزة تُضاف قرب النهاية. فهو يحدّد نموذج البيانات وقواعد التعارض وتصميم الطابور والواجهة، وإضافته لاحقاً أقرب إلى إعادة كتابة. قرّره أولاً، واجعل المخزن المحلي هو المرجع، واختر نموذج التعارض صراحةً، واجعل حالة المزامنة ظاهرة، واختبر على اتصال سيّئ لا جيّد.