Tohum

دليل تطوير منتجات IoT من البداية إلى النهاية – المرحلة 4: تطوير برمجيات Firmware والسحابة

رسم توضيحي: متحكم دقيق يتبادل البيانات مع سحابة تحمل تروساً

المرحلة 4 تنقل المنتج إلى الجانب الأيمن من حرف V: التنفيذ والاختبار. في نموذج V الكلاسيكي لتطوير المنتجات، الجانب الأيسر عن التصميم (الذي عالجناه في المراحل السابقة)؛ هنا نطوّر firmware (البرنامج العامل على جهاز IoT خاصتك) وبرمجيات السحابة (الخوادم، قواعد البيانات، APIs، واجهات المستخدم) التي تبث معاً الحياة في منتجك. وعلى خلاف تطوير العتاد، الذي يصبح غير مرن نسبياً بمجرد طباعته على لوحة دائرة، فإن تطوير البرمجيات تكراري ورشيق بدرجة عالية. في الحقيقة، ربما كنت قد كتبت أجزاء من firmware بالفعل في المرحلة 3 أثناء اختبار تصميم عتادك. حان الآن وقت تكثيف ذلك. البرمجيات كالطين – سهلة التشكيل والتغيير أثناء العمل – لذا يمكننا التكرار بسرعة والتنقية باستمرار. ستعمل هذه المرحلة بالتوازي مع أي عمل عتاد متبقٍّ، كلٌّ يغذي الآخر عند الحاجة.

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

4.1 معمارية البرمجيات وتصميمها

قبل أن يقفز أي شخص إلى البرمجة، من الحكمة رسم خطة لعبة لمعمارية البرمجيات – لكل من firmware على الجهاز ومكونات السحابة. فكّر فيها كتخطيط رحلة برية. لن تبدأ القيادة دون خريطة؛ وبالمثل، نرسم المكونات الرئيسية وكيف تتفاعل قبل أن نكتب آلاف أسطر الكود. هذا مماثل لمعمارية النظام التي أنجزناها في المرحلة 2، لكن الآن على مستوى الكود. تخطيط معمارية برمجيات جيد يساعدك على تجنب «كود السباغيتي» لاحقاً. إليك الأشياء الأساسية التي يجب تأسيسها في هذه المرحلة:

معمارية Firmware (النهج متعدد الطبقات واعتبارات RTOS)

عرّف بنية كودك المدمج بـ تقسيمه إلى طبقات. نهج شائع في firmware هو المعمارية متعددة الطبقات، التي تساعد على تنظيم الكود منطقياً وتسهّل صيانته. على سبيل المثال، في المستوى الأدنى قد يكون لديك حزمة دعم اللوحة (BSP) أو طبقة تجريد عتاد – تحتوي تعريفات منخفضة المستوى خاصة بمتحكمك الدقيق ووحداته الطرفية. فوق ذلك، قد تكون لديك تعريفات أجهزة أو وحدات لحساساتك ومشغّلاتك (معالجة أشياء مثل قراءة حساس حرارة أو قيادة LED). فوق التعريفات، قد تكون طبقة خدمات – قد تنفذ بروتوكولات اتصال، وخوارزميات معالجة بيانات، أو منطقاً آخر يعالج البيانات الخام. وأخيراً، في القمة، لديك طبقة التطبيق، وهي عقل جهازك: تربط كل شيء معاً لتحقيق غرض منتجك (مثلاً قراءة قيم الحساسات، اتخاذ قرارات، إرسال بيانات إلى السحابة، والاستجابة للأوامر). فكّر في رسم مخطط بسيط (مخطط مكونات UML أو حتى صناديق وأسهم على سبورة) لتصوير هذه الطبقات. يساعد هذا الجميع في الفريق على فهم فصل الاهتمامات – كبناء منزل بأساس وهيكل وسقف، لكل طبقة عملها.

قرار معماري كبير آخر في جانب firmware هو استخدام RTOS (نظام تشغيل زمن حقيقي) أم لا. RTOS كأن يكون لديك نظام تشغيل صغير على جهازك يستطيع تشغيل مهام متعددة تبدو متزامنة (عبر الجدولة). إذا كان جهازك يفعل شيئاً واحداً في كل مرة، فقد لا تحتاج RTOS. لكن معظم أدوات IoT لديها كثير يحدث بالتزامن – على سبيل المثال، قراءة حساسات دورياً، والاستماع لرسائل واردة (من السحابة أو إدخال المستخدم)، وتحديث شاشة أو LED، ومعالجة اتصال الشبكة. مع RTOS، تستطيع تصميم firmware كمجموعة من المهام أو الخيوط (threads). على سبيل المثال، يمكن أن تكون لديك مهمة مخصصة لأخذ عينات الحساسات، وأخرى مخصصة لمعالجة الاتصالات (إرسال بيانات أو استقبال أوامر)، وأخرى للمهام المنزلية أو تحديثات واجهة المستخدم. وكجزء من تصميم معماريتك، حدد هذه المهام وكيف ستتواصل مع بعضها – تشمل الآليات الشائعة message queues، وأعلام أحداث، أو بيانات مشتركة محمية بـ mutexes (لتجنب التعارضات). تقرير هذا مبكراً يساعد على ضمان ألا يصبح firmware خاصتك كومة من الكود المتشابك حيث كل شيء يعتمد على كل شيء آخر (المعروف بكود السباغيتي). بدلاً من ذلك، سيكون لديك واجهات واضحة: ربما تضع مهمة الحساس القراءات الجديدة في queue، وتلتقطها مهمة الاتصالات لإرسالها إلى السحابة. إذا صممت هذه الواجهات بوضوح الآن، ستكون البرمجة أكثر سلاسة وستشكر نفسك لاحقاً عندما يحين وقت استكشاف الأخطاء أو توسيع firmware.

لماذا كل هذا الضجيج المعماري لـ firmware؟ لأنه يجعل حياتك أسهل على الطريق. بتصميم متعدد الطبقات جيد وبنية مهام سليمة، تستطيع تعديل جزء واحد (مثلاً استبدال تعريف حساس أو تعديل خوارزمية) دون كسر كل شيء آخر. كما يسهّل عمل عدة مطوّرين معاً دون الدوس على أقدام بعضهم – يستطيع شخص تطوير تعريف الحساس بينما يعمل آخر على كود الاتصال السحابي، ما دمتم قد اتفقتم على كيفية تعامل تلك الأجزاء (مثلاً تنسيق بيانات رسالة قراءة حساس). باختصار، قليل من التخطيط هنا يساعد على تجنب كثير من الألم لاحقاً.

معمارية السحابة (بنية الخادم وقاعدة البيانات والتطبيق)

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

من المفيد رسم مخطط لهذه المنظومة – نموذج C4 (Client-Container-Component-Code) طريقة شائعة لرسم معمارية البرمجيات. ارسم على الأقل مستوى Container: أظهر الجهاز والخادم وقاعدة البيانات وتطبيق العميل، وكيف تتدفق البيانات بينها. على سبيل المثال، قد يحتوي مخططك على سهم من الجهاز إلى Cloud API («يرسل بيانات الحساس عبر HTTP/MQTT»)، وسهم من السحابة إلى قاعدة البيانات («يخزن البيانات»)، وسهم من تطبيق العميل إلى السحابة («المستخدم يطلب بيانات أو يرسل أمراً»)، وهكذا. هذه الخريطة البصرية تضمن أن للجميع (مطوّري firmware، ومهندسي السحابة، ومطوّري الواجهة الأمامية، إلخ) نموذجاً ذهنياً واضحاً لكيفية اتصال الأشياء. من الأسهل بكثير مناقشة مشكلات التصميم والتقاطها مبكراً مع مخطط كهذا بين اليدين. ربما تكتشف في هذه المرحلة أنك تحتاج مكوناً إضافياً – على سبيل المثال، وسيط رسائل (message broker) أو خدمة منصة IoT – للتعامل مع التوسع إلى أجهزة كثيرة. الأفضل التفكير فيه الآن لا عندما تكون غارقاً في البرمجة.

جانب آخر: فكّر في قابلية التوسع والموثوقية في تصميم سحابتك. إذا كنت تتوقع اتصال 100,000 جهاز في النهاية، هل تستطيع قاعدة بياناتك وتصميم خادمك المختاران التعامل مع ذلك؟ إذا تعطل خادم، ماذا يحدث (هل لديك تكرار)؟ قد تُفصَّل تلك التفاصيل أكثر في مرحلة لاحقة، لكن التصميم عالي المستوى يجب أن يحسب حساب مخاوف كبرى كهذه مقدماً. على سبيل المثال، قد تقرر استخدام منصة IoT سحابية (AWS IoT، Azure IoT Hub، إلخ) بدلاً من بناء كل شيء من الصفر، وهذا سيصبح جزءاً من قرار معماريتك.

بروتوكولات الاتصال وتنسيقات البيانات (اتصال الجهاز بالسحابة)

يتضمن منتج IoT بطبيعته اتصالاً بين الجهاز والسحابة. جزء من تصميم البرمجيات هو تقرير كيف يتحدثان مع بعضهما وبأي لغة (البروتوكولات وتنسيقات البيانات). هناك بضعة خيارات بروتوكولات شائعة في IoT: على سبيل المثال، هل سيستخدم جهازك MQTT، أم HTTP/REST، أم CoAP، أم ربما شيئاً مثل WebSockets؟ لكل منها إيجابيات وسلبيات. MQTT بروتوكول نشر/اشتراك خفيف رائع لـ IoT لأنه فعال ويعمل جيداً عبر الشبكات غير الموثوقة – تستخدمه أنظمة IoT كثيرة للرسائل من الجهاز إلى السحابة. HTTP/REST شائع جداً وسهل الاختبار (لأنك تستطيع ضربه بأدوات ويب قياسية) لكنه قد يحمل حملاً زائداً أكبر قليلاً لكل رسالة. CoAP بروتوكول IoT متخصص يشبه HTTP خفيفاً، ويُستخدم غالباً في الشبكات المقيدة. المفتاح اختيار ما يناسب قدرات جهازك وبنيتك التحتية السحابية. على سبيل المثال، على جهاز شديد التقييد (متحكم دقيق 8-bit يعمل بالبطارية)، غالباً ما يكون MQTT أو CoAP أنسب من HTTP. وإذا كان جهازك أكثر قدرة (يعمل بـ Linux أو ما شابه)، فـ HTTP/REST جيد وأكثر مباشرة.

بمجرد اختيار البروتوكول، صمّم تنسيق البيانات للرسائل. تستخدم مشاريع كثيرة JSON لأنه مقروء بشرياً وسهل العمل به (معظم اللغات لديها محللات JSON). على سبيل المثال، قد تبدو رسالة قراءة حرارة بصيغة JSON هكذا:

{ "device_id": "ABC123", "timestamp": 1692124800, "temperature_c": 22.5 }

هذا يوضح الحقول بوضوح. لكن JSON مطوّل بعض الشيء، لذا إذا كان جهازك يحتاج إرسال البيانات بكفاءة عالية (فكّر في آلاف الرسائل أو عرض نطاق منخفض جداً)، قد تستخدم تنسيقاً ثنائياً (مثل Protocol Buffers، أو CBOR، أو مخططاً ثنائياً مخصصاً) لتوفير البايتات. سيؤثر خيار التصميم هنا في كل من firmware والسحابة: يجب أن يتفق الجانبان على البروتوكول والتنسيق. إنه يشبه الاتفاق على لغة قبل بدء محادثة – إذا كان الجهاز «يتحدث» MQTT+JSON والخادم يتوقع شيئاً آخر، فلن يفهما بعضهما.

أداة مفيدة في هذه المرحلة هي رسم مخطط تسلسل أو مجرد كتابة تسلسل مثال للاتصال. على سبيل المثال: «يأخذ الجهاز قراءة حساس، ينسّق حمولة JSON، يرسلها عبر MQTT إلى الموضوع X؛ يستقبلها وسيط السحابة، تعالجها خدمة السحابة وتؤكدها، ثم تخزنها في قاعدة البيانات؛ لاحقاً، يطلب مستخدم البيانات عبر التطبيق، يجلب API السحابي من قاعدة البيانات ويعيد JSON إلى التطبيق.» كتابة هذا التدفق تضمن أن الجميع (مطوّر firmware ومطوّر السحابة خصوصاً) على الصفحة نفسها حرفياً حول كيفية تحرك البيانات. يمكن أن يمنع سوء فهم مثل «آه، ظننت أننا نستخدم طابع UNIX بالثواني لا بالميلي ثانية» أو «هل كنا نضغط البيانات أم لا؟». التقطها الآن في التصميم، وستوفر وقت تصحيح لاحقاً.

معايير البرمجة وأدوات الفريق (الجودة منذ البداية)

الآن، لنتحدث عن كيفية كتابتنا للكود وتعاوننا، لأن اتباع الفريق كله لممارسات مشتركة ضروري. اتفقوا على معايير برمجة لكل جزء من النظام. لتطوير C المدمج، تتبنى فرق كثيرة إرشادات MISRA-C – مجموعة قواعد للغة C تهدف إلى تجنب البنى غير الآمنة أو المعرضة للأخطاء (نشأت من صناعة السيارات، لكنها مفيدة على نطاق واسع في أي firmware حرج). إذا كنت تستخدم C++ في firmware، قد تقرر أي ميزات C++ الحديثة مسموحة أو محظورة (لإبقاء الأمور آمنة للأنظمة المدمجة – على سبيل المثال، ربما تجنب التخصيص الديناميكي في وقت التشغيل، إلخ). للغات الأعلى مستوى في جانب السحابة مثل Python، ستتبع غالباً دليل أسلوب PEP 8، ولـ JavaScript/TypeScript ربما تستخدم linter بأسلوب Airbnb أو أي معيار كان. الهدف ليس التدقيق المزعج، بل ضمان الاتساق والتقاط المزالق الشائعة. عندما يكتب الجميع بأسلوب متسق، تبدو قاعدة الكود كمنتج واحد لا خليط مرقع، ويسهل قراءة وصيانة كود كتبه شخص آخر في الفريق.

بالإضافة إلى أسلوب الكود، اختر الأدوات والبنية التحتية التي سيستخدمها الفريق. يشمل هذا التحكم بالإصدارات وإعداد المستودع (على الأرجح Git – إن لم تكن تستخدمه بالفعل، افعل بالتأكيد، فهو العمود الفقري للبرمجة التعاونية). قرروا استراتيجية التفريع: هل ستستخدمون تطويراً قائماً على trunk بسيطاً، أم GitFlow بفروع ميزات، إلخ؟ أيضاً، أعدّوا نظام تتبع قضايا إن لم يكن موجوداً (مثل Jira، Trello، Asana، أو GitHub Issues) لإدارة المهام وقصص المستخدم في سباقاتكم. إنه أيضاً وقت جيد لإعداد مسار التكامل المستمر (CI): اختر خدمة CI (GitHub Actions، GitLab CI، Jenkins، CircleCI، إلخ) واضبطها لبناء كودك واختباره تلقائياً كلما دُفعت تغييرات. سنتحدث أكثر عن CI في القسم التالي، لكن مرحلة المعمارية هي حيث تتأكد أن جميع المطوّرين لديهم وصول إلى هذه الأدوات ويعرفون كيفية استخدامها.

جزء آخر من «ثقافة الكود» هو تقرير أشياء مثل ممارسات مراجعة الكود – على سبيل المثال، هل سيتطلب كل pull request موافقة مطوّر آخر واحد على الأقل؟ وضع هذه التوقعات مبكراً صحي. قد يبدو رسمياً، لكنه ينقذك من سيناريوهات يُدخِل فيها كود غير مراجَع من شخص خللاً بشعاً أو ثغرة أمنية لا يلتقطها الآخرون إلا متأخراً جداً. بالاتفاق على أن «نحن دائماً نجري مراجعات كود، ونتبع دليل الأسلوب، ونشغّل الاختبارات قبل الدمج»، تغرس عقلية جودة منذ اليوم الأول. التطوير الحديث يتعلق بالتعاون الجماعي والانضباط بقدر ما يتعلق بعبقرية البرمجة.

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

4.2 سباقات التطوير – كرّر وانتصر

ومع مخطط المعمارية بين اليدين، يبدأ البناء – كتابة كود، واختباره، ومشاهدة منتج IoT خاصتك يفعل أشياء فعلاً. نقارب هذا في تكرارات، باستخدام سباقات (sprints) كما في منهجية Agile عادة. السباق دورة قصيرة ثابتة الطول (شائعاً أسبوعان، لكن قد تكون أسبوعاً أو حتى 4 أسابيع بحسب تفضيل فريقك) يلتزم فيها الفريق بتسليم مجموعة ميزات أو تحسينات بنهايتها. الفكرة تقسيم مهمة «تطوير كل البرمجيات» الضخمة إلى قطع صغيرة يمكن التعامل معها بثبات وإحراز تقدم ملموس عليها.

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

تخطيط السباق وقصص المستخدم

يبدأ كل سباق باجتماع تخطيط سباق. ينظر الفريق إلى backlog المنتج – القائمة الرئيسية لكل الميزات والإصلاحات والمهام المطلوبة، المشتقة مبدئياً من المتطلبات التي جمعناها في المرحلة 1 (والمضاف إليها مع تعلمنا المزيد في المراحل اللاحقة). في التخطيط، يختار الفريق مجموعة من قصص المستخدم أو المهام من backlog للتعامل معها في السباق، بناءً على الأولوية وسعة الفريق.

قصة المستخدم وصف بحجم لقمة لميزة من منظور المستخدم النهائي، غالباً باتباع القالب «بصفتي <نوع مستخدم>، أريد <قدرة ما> حتى <فائدة>.» على سبيل المثال، قد تكون إحدى القصص: «بصفتي مستخدماً، أريد أن يرسل جهاز IoT بيانات الحرارة إلى السحابة كل 10 دقائق، حتى أستطيع مراقبة حرارة غرفتي عن بُعد.» من هذه القصة، سيكتشف الفريق ما يجب فعله في جانبي الجهاز والسحابة: ربما في firmware الجهاز، نحتاج قراءة حساس الحرارة وإرسال رسالة MQTT كل 600 ثانية؛ وفي السحابة، نحتاج API أو معالج رسائل لاستقبال تلك البيانات ومكاناً لتخزينها، وربما واجهة بسيطة لعرضها.

أثناء تخطيط السباق، يناقش الفريق كل قصة مختارة لضبط معايير القبول – بشكل أساسي، كيف نعرف متى تكون هذه القصة «منتهية» وتعمل؟ للمثال أعلاه، قد تكون المعايير: «بافتراض جهاز بحساس حرارة، عندما يعمل لمدة 10 دقائق، يجب أن تكون قراءة حرارة واحدة على الأقل مرئية في قاعدة بيانات السحابة وعلى لوحة متابعة المستخدم.» التحديد يساعد الفريق على فهم ما يجب بناؤه بالضبط وكيفية التحقق منه. هذا هو الوقت لطرح الأسئلة وتوضيح التفاصيل. من الأفضل بكثير تسوية الغموض الآن من اكتشاف لاحقاً أن فريقي firmware والسحابة كانا لديهما افتراضات مختلفة (مثلاً مطوّر ظن أنها كل 10 دقائق عند النشاط، وآخر ظن أنها دائماً، إلخ). بمجرد انتهاء التخطيط، يجب أن يعرف الجميع أي قطعة عمل مسؤول عنها في السباق القادم.

التنفيذ والتكامل المستمر (CI)

الآن نصل إلى التنفيذ – كتابة الكود لإحياء قصص المستخدم تلك. سيبدأ المطوّرون ببرمجة ميزات firmware وميزات السحابة المحددة للسباق. ممارسة فضلى أساسية هنا هي أيضاً كتابة اختبارات آلية لكل ميزة جديدة أثناء تطويرها. بالنسبة لـ firmware، قد يشمل هذا كتابة اختبارات وحدة لمنطق يمكن أن يعمل على جهاز مضيف (على سبيل المثال، إذا كانت لديك دالة تحلل قراءة حساس أو آلة حالات للجهاز، يمكنك غالباً كتابة اختبار PC لها). بالنسبة لبرمجيات السحابة، الأمر أسهل عادة: تستطيع كتابة اختبارات وحدة لدوالك، واختبارات تكامل لـ APIs خاصتك.

بينما يكتب المطوّرون الكود، يودعونه في التحكم بالإصدارات (Git). هنا يتألق إعداد التكامل المستمر (CI) خاصتنا. مع CI، في كل مرة يُدفَع كود أو يُفتَح pull request، يبدأ بناء آلي على خادم CI. سيرجم CI كود firmware (ربما في تكوينات متعددة إن لزم)، ويشغّل أي اختبارات وحدة لديك، وربما حتى ينشر firmware إلى محاكي أو جهاز اختبار. سيبني أيضاً برمجيات السحابة، ويشغّل كل اختباراتها، ويفحص أسلوب الكود (linters)، ويشغّل أدوات التحليل الساكن، بل ويقيس تغطية الكود (كم من الكود تمرّ عليه الاختبارات). الهدف التقاط أي مشاكل فوراً. إذا أدخل مطوّر خطأً يجعل اختباراً يفشل، سيبلّغ نظام CI عنه فوراً – خلال دقائق من كتابة الكود – بحيث يستطيع الفريق إصلاحه قبل أن يصبح مشكلة أكبر. حلقة الملاحظات السريعة هذه حاسمة. إصلاح خلل أدخلته قبل ساعة أسهل بكثير (لا تزال تتذكر ما كنت تفعله) من خلل من الشهر الماضي (الذي يكون قد تشابك حينها في تغييرات كثيرة أخرى).

على سبيل المثال، تخيّل أن مطوّراً يغيّر كود قراءة الحساس ويُدخل عرضاً خلل overflow للقيم العالية جداً. إذا كان لديك اختبار وحدة يدفع قيمة حساس إلى الحد الأقصى، سيلتقط CI ذلك الفشل وينبّه الفريق. يستطيع المطوّر حينها معالجته فوراً. بدون ذلك الاختبار، قد يكمن الخلل حتى تضرب موجة حر، وتبلغ الحساسات الحقيقية عن حرارات عالية، وينهار الجهاز في الميدان. CI والاختبار موجودان لالتقاط هذه اللحظات بالضبط – آلياً، وخلال دقائق من كتابة الكود.

التكامل المستمر ليس عن الاختبارات فحسب؛ تدمج الفرق غالباً أدوات تحليل ساكن في CI أيضاً. تفحص أدوات التحليل الساكن كودك المصدري بحثاً عن أخطاء محتملة وثغرات أمنية أو انتهاكات أسلوب دون تشغيله. على سبيل المثال، تستطيع التقاط أشياء مثل «احتمال إلغاء مرجعية مؤشر فارغ» أو «متغيرات غير مستخدمة» أو روائح كود أخرى. تستخدم فرق مدمجة كثيرة أدوات مثل PC-Lint أو Cppcheck أو clang-tidy لـ C/C++، وتستخدم فرق السحابة linters (ESLint لـ JavaScript، Pylint لـ Python، إلخ) وربما ماسحات أمنية. يمكن ضبط CI لـ إفشال البناء إذا وجدت هذه الأدوات مشكلات خطيرة. هذا يضع معيار جودة – مثلاً، «يجب أن يحتوي كود firmware على 0 تحذيرات جديدة وأن تنجح كل الاختبارات ليُسمَح بالدمج.» قد يبدو صارماً، لكنه يُبقي قاعدة الكود صحية.

بالإضافة إلى ذلك، يستطيع CI تتبع تغطية الكود – أي نسبة مئوية من كودك تمرّ عليها الاختبارات الآلية. ومع أن تغطية 100% غير واقعية غالباً (وليس كل الكود متساوي السهولة في الاختبار)، تضع فرق كثيرة هدفاً (مثلاً تغطية 80% على الأقل) وتتبعها. إذا أسقط إيداع كود جديد التغطية فجأة من 80% إلى 50%، فهذه راية حمراء – ربما قُفزت الاختبارات أو أُضيف كثير من الكود غير المختبَر. بمراقبة هذه المقاييس سباقاً بسباق، تحافظ على جودة كود عالية طوال التطوير، بدلاً من محاولة «تثبيتها» لاحقاً.

باختصار، التنفيذ في سياق تكراري ليس مجرد «اكتب كومة كود». إنه اكتب كوداً، وادمجه باستمرار في قاعدة كود مشتركة، واختبره باستمرار، وأصلح المشكلات أثناء تقدمك. النتيجة نظام برمجي دائماً في حالة شبه عاملة، يتحسن قليلاً كل يوم.

البناءات التزايدية وعروض نهاية السباق

بنهاية كل سباق، تستهدف أن يكون لديك بناء تزايدي للنظام كله يمثل تحسناً ملموساً على سابقه. مثالياً، هو شيء يمكنك فعلاً تشغيله على جهاز وبيئة سحابية اختبار لرؤية الميزات الجديدة تعمل. شعار جيد هو «كن دائماً مستعداً للعرض» في نهاية السباق. لمثال منتج IoT خاصتنا، ربما في نهاية السباق 1، يكون لدينا تدفق أساسي من الطرف إلى الطرف يعمل: يقرأ الجهاز قيمة حساس وهمية (ربما رقماً ثابتاً أو قيمة اختبار عشوائية) ويرسلها إلى السحابة، تستقبلها السحابة وتخزنها، ونستطيع التحقق يدوياً من أن البيانات مرت (حتى لو كان مجرد النظر مباشرة في قاعدة البيانات أو السجلات). هذا معلم ذو معنى بالفعل – يثبت أن كل الأنابيب من الجهاز إلى السحابة تعمل!

بحلول السباق 2، قد نستبدل القيمة الوهمية بقراءة حساس حقيقية من العتاد، ونعدّ صفحة ويب بسيطة تعرض أحدث قراءة. الآن نستطيع فعلاً رؤية بيانات حية. بحلول السباق 3، ربما نضيف ميزة تنبيه: إذا تجاوزت الحرارة عتبة، يُطلق الجهاز أو السحابة تنبيهاً (ربما يومض LED أو يُرسل بريد إلكتروني). وهكذا. كل سباق، نضيف وظائف إضافية: ربما سباق يضيف دعم التحكم عن بُعد (إرسال أوامر من السحابة إلى الجهاز)، وآخر يضيف رسم UI جميلاً للبيانات التاريخية، إلخ. بعد عدد من السباقات، سيكون لديك نظام مكتمل الميزات. لكن حاسماً، في كل مرحلة، كان لديك نظام عامل تضيف إليه. هذا يعني أنه إذا ساء شيء، تعرف أنه على الأرجح يتعلق بأحدث الإضافات، ما يسهّل التصحيح. ويعني أيضاً أن أصحاب المصلحة (مثل مديرك، أو مدير المنتج، أو حتى مستخدم اختبار) يستطيعون رؤية التقدم وإعطاء ملاحظات طوال الوقت.

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

الاختبار ومعايير تعريف «تم»

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

هذه قائمة طويلة، وللوهلة الأولى قد تبدو كثير من الصناديق لتعليمها. لكن هذه الممارسات تنقذك من قدر كبير من المتاعب لاحقاً. التقاط وإصلاح خلل أو عيب تصميم الآن، أثناء السباق الحالي، أفضل بكثير من تسلله إلى الاختبار النهائي أو الإنتاج. تعريف «تم» يعني أساساً «هذه الميزة ليست نصف مخبوزة؛ بل مدمجة ومختبَرة وجاهزة حقاً.» عندما يستوفي كل جزء من نظامك هذه المعايير أثناء تقدمك، تحمل مرحلة التكامل والاختبار النهائية (المرحلة 5) مفاجآت قليلة – فقد كنت تتحقق طوال الوقت.

التوثيق المستمر (أبقِ الخرائط محدّثة)

على الأرجح أنشأت بعض المخططات والوثائق في مرحلة المعمارية (المرحلة 4.1). الآن، مع تقدم التطوير، من المهم إبقاء وثائق التصميم تلك محدّثة. البرمجيات لها طريقة طريفة في التطور – ربما تكتشف حاجة لمهمة جديدة في RTOS لوظيفة مراقبة (watchdog)، أو تتغير معمارية السحابة لإضافة طبقة تخزين مؤقت للأداء. إذا لم تحدّث وثائق المعمارية، ستنحرف عن الواقع ولن يثق بها أحد في النهاية. لتجنب مشكلة التوثيق البالي الكلاسيكية، عامل الوثائق كجزء حي من المشروع. تكتب بعض الفرق حتى مخططاتها ككود باستخدام أدوات مثل PlantUML أو Mermaid. بهذه الطريقة، يعيش «مصدر» المخطط في المستودع بجانب الكود. عندما يُجرى تغيير يؤثر في المعمارية، يحدّث الفريق كود المخطط ويعيد توليد الصورة.

خيار عملي (إن كان فريقك مستعداً) هو دمج التوثيق في عملية CI أيضاً. على سبيل المثال، يمكن أن يكون لديك فحص: إذا تغيرت ملفات أساسية معينة (مثل مخطط رسالة أو واجهة مكون) ولم يُحدَّث ملف التوثيق المقابل، يذكّرك CI أو يفشل البناء. ينبّه هذا المطوّرين: «مرحباً، غيّرت كيف يعمل النظام – من فضلك حدّث المخطط كي يعرف الجميع.» قد يبدو مبالغاً، لكن إبقاء الوثائق متزامنة يؤتي ثماره عندما ينضم مطوّر جديد أو عندما تعود إلى المشروع بعد ستة أشهر وتحاول تذكر كيف الأشياء منظمة. حتى لو لم تؤتمتها، جدول على الأقل مراجعة سريعة للمخططات كل بضعة سباقات للتأكد من أنها لا تزال تعكس الواقع.

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

العروض المنتظمة وحلقات الملاحظات

تطرقنا إلى العروض في سياق السباقات، لكن لنؤكد قيمة تأسيس إيقاع منتظم لـ إظهار التقدم. في Agile، هذا غالباً اجتماع مراجعة السباق: في نهاية السباق، يعرض الفريق ما بناه على أصحاب المصلحة (مديري منتجات، فرق أخرى، أحياناً حتى مستخدمين حقيقيين أو تنفيذيين). لمشروع IoT، قد يكون العرض حرفياً – إظهار الجهاز والنظام يعملان معاً. على سبيل المثال، قد تعرض لوحة متابعة الويب على شاشة ويكون الجهاز الفيزيائي على الطاولة. ثم تفعل شيئاً مثل تحفيز الجهاز (اضغط زراً أو اخلق شرطاً) وتُظهر أن السحابة تتحدث في الزمن الحقيقي. هذه اللحظات مؤكِّدة فعلاً للفريق وقيمة لأصحاب المصلحة. تجعل المشروع يبدو حقيقياً وتُبقي الجميع منخرطين. إنها أيضاً فرصة للحصول على ملاحظات: ربما يقول صاحب مصلحة: «هذا رائع، لكن هل يمكننا أيضاً عرض بيانات الرطوبة في النسخة التالية؟» أو «التنبيه استغرق وقتاً طويلاً قليلاً ليظهر، هل يمكننا تسريعه؟». ملاحظات مبكرة كهذه قيّمة خصوصاً، لأنه يمكن تغذيتها في السباقات التالية، قبل أن يصبح المنتج نهائياً بكثير، عندما تكون التغييرات أسهل بكثير.

لا تقلق إذا كانت الوظائف في العروض المبكرة بدائية جداً – هذا متوقع. المفتاح أنها تتحسن تكرارياً. قد يكون العرض الأول مجرد إثبات أن جهازاً واحداً يستطيع التحدث إلى خادم واحد. العرض التالي يُظهر UI بسيطاً. العروض اللاحقة تُظهر صقلاً وميزات إضافية. إذا لم يعمل شيء كما هو متوقع في عرض، فلا بأس أيضاً، لأنك اكتشفت مشكلة تكامل أمام الجميع والآن يمكنك إعطاء أولوية لإصلاحها. (في الحقيقة، لدى بعض الفرق مقولة: «اعرض مبكراً، اعرض كثيراً – أخفق كثيراً، أصلح أسرع!») يفرض العرض أيضاً شيئاً من الانضباط: يضع ضغطاً إيجابياً على الفريق لإنهاء العمل إلى حالة قابلة للشحن كل سباق، بدلاً من المماطلة في التكامل إلى لاحقاً. اكتشاف أن JSON الجهاز لا يُحلَّل بشكل صحيح على الخادم الآن أفضل بكثير من اكتشافه أثناء مرحلة اختبار نهائية كبيرة. لذا، عامل العروض ليس كاستعراض فحسب، بل كجزء من عملية التطوير ذاتها.

أخيراً، احتفلوا بتلك العروض. بناء منتج IoT عمل شاق، يتضمن قطعاً كثيرة، وكل قطعة عاملة – حتى ميزة صغيرة – تستحق لحظة تقدير. هذا يُبقي الروح المعنوية عالية ويبني ثقة الفريق: تبقى الطاقة أعلى عندما يرى الجميع بانتظام النتائج الملموسة لعملهم.


لتلخيص المرحلة 4: تطوير برمجيات firmware والسحابة يدور كله حول التقدم التكراري والتحسين المستمر. باستثمار وقت في معمارية البرمجيات مقدماً، تمنح فريقك مخططاً متيناً (وتتجنب برمجة أنفسكم في زاوية). باستخدام سباقات التطوير والتكامل المستمر وتعريف «تم» قوي، تضمن أن الجودة مبنية في كل خطوة – تُلتقط الأخطاء مبكراً، والبرمجيات دائماً على بعد خطوة واحدة من منتج عامل. بنهاية المرحلة 4 (التي تشمل على الأرجح عدة دورات سباق)، يجب أن يكون لديك firmware مكتمل الميزات ومختبَر على عتاد حقيقي أو تمثيلي، ونظام سحابي مكتمل الميزات ومختبَر في بيئة واقعية. بعبارة أخرى، جانب البرمجيات الكامل لمنتجك جاهز للانطلاق.

بفضل هذا النهج التكراري، تمكنت من التعديل والتحسين باستمرار، بدلاً من المراهنة بكل شيء على انفجار كبير واحد في النهاية. هذا يقلل المخاطر ويجعل عملية التطوير أكثر قابلية للتنبؤ. وهو أيضاً ببساطة طريقة عمل أفضل: بدلاً من امتداد طويل من عدم اليقين، شاهدت منتج IoT خاصتك يعود إلى الحياة خطوة بخطوة، من LED وامض إلى جهاز ذكي متصل بالكامل ومدمج سحابياً. العمل الثقيل في جانب البرمجيات تم، وتم بصرامة ورشاقة. إلى المرحلة التالية، حيث نأخذ هذا العتاد والبرمجيات المصنوعة بإتقان ونجهّزها للإطلاق في العالم.