تطوّر المخططات الآمن عند إعادة التوليد لهجرات متوقعة
يضمن تطور المخطط الآمن عند إعادة التوليد بقاء بيانات الإنتاج صالحة عندما يُعاد توليد كود الواجهة الخلفية. تعرّف على طريقة عملية لتخطيط تغييرات المخطط والهجرات.

لماذا تبدو تغييرات المخطط محفوفة بالمخاطر عند إعادة توليد الواجهة الخلفية
عندما تُعاد توليد الواجهة الخلفية من نموذج بصري، يمكن أن يشعر تغيير قاعدة البيانات كأنك تسحب خيطاً من سترة. تعدل حقلًا في Data Designer، تضغط على إعادة التوليد، وفجأة لا تغيّر جدولًا فقط. أنت تغيّر أيضًا واجهة البرمجة المولّدة، قواعد التحقق، والاستعلامات التي يستخدمها تطبيقك لقراءة وكتابة البيانات.
ما يحدث عادةً ليس أن الكود الجديد يفشل في البناء. العديد من منصات الـ no-code (بما في ذلك AppMaster، التي تولد كود Go حقيقي للواجهات الخلفية) ستولد مشروعًا نظيفًا في كل مرة. الخطر الحقيقي هو أن بيانات الإنتاج موجودة بالفعل، ولا تعيد تشكيل نفسها تلقائيًا لتتناسب مع أفكارك الجديدة.
الفشلان اللذان يلاحظهما الناس أولاً بسيطان:
- قراءة مكسورة: لا يستطيع التطبيق تحميل السجلات لأن عمودًا تحرّكَ، تغير نوع، أو أن استعلامًا يتوقع شيئًا غير موجود.
- كتابة مكسورة: تفشل السجلات الجديدة أو المحدثة لأن القيود أو الحقول المطلوبة أو الصيغ تغيرت، والعمِلاء القدامى ما زالوا يرسلون الشكل القديم.
كلا الفشلين مؤلم لأنهما قد يخفيان حتى يصل المستخدمون الحقيقيون إليهما. قد تكون قاعدة اختبار مُنشأة أو فارغة، فيظهر كل شيء سليماً. الإنتاج يحتوي على حالات هامشية: null حيث افترضت وجود قيمة، نصوص enum قديمة، أو صفوف نُشئت قبل وجود قاعدة جديدة.
لهذا السبب يهم تطور المخطط الآمن عند إعادة التوليد. الهدف هو جعل كل تغيير آمنًا حتى عندما يُعاد توليد كود الواجهة الخلفية تمامًا، بحيث تظل السجلات القديمة صالحة وتستمر السجلات الجديدة في الإنشاء.
"الهجرات المتوقعة" تعني ببساطة أن بإمكانك الإجابة عن أربعة أسئلة قبل النشر: ما الذي سيتغير في قاعدة البيانات، ماذا سيحدث للسجلات الحالية، أي إصدار من التطبيق يمكن أن يعمل أثناء الطرح، وكيف ستتراجع إذا ظهر شيء غير متوقع.
نموذج بسيط: المخطط، الهجرات، والكود المعاد توليده
عندما تستطيع منصتك إعادة توليد الواجهة الخلفية، يساعدك فصل ثلاثة أشياء في ذهنك: مخطط قاعدة البيانات، خطوات الهجرة التي تغيّره، والبيانات الحية الموجودة في الإنتاج. الخلط بينها هو سبب شعور التغييرات بأنها غير متوقعة.
فكّر في إعادة التوليد على أنها "إعادة بناء كود التطبيق من النموذج الأحدث". في أداة مثل AppMaster، قد يحدث هذا البناء كثيرًا أثناء العمل: تعدّل حقلًا، تضبط منطق العمل، تضيف نقطة نهاية، تعيد التوليد، تختبر، وتكرر. إعادة التوليد متكررة. قاعدة بيانات الإنتاج يجب ألا تكون كذلك.
إليك النموذج البسيط.
- المخطط: بنية جداول قاعدة البيانات، الأعمدة، الفهارس، والقيود. هو ما تتوقعه قاعدة البيانات.
- الهجرات: خطوات مرتبة وقابلة للتكرار تنقل المخطط من إصدار إلى آخر (وأحيانًا تنقل بيانات أيضًا). هذا ما تشغّله على كل بيئة.
- بيانات التشغيل: السجلات الحقيقية التي أنشأها المستخدمون والعمليات. يجب أن تظل صالحة قبل وأثناء وبعد التغيير.
يجب أن يُعامل الكود المعاد توليده كـ "التطبيق الحالي الذي يتحدث مع المخطط الحالي". الهجرات هي الجسر الذي يحافظ على توازن المخطط وبيانات التشغيل أثناء تغير الكود.
لماذا تغيّر إعادة التوليد قواعد اللعبة
إذا كنت تعيد التوليد كثيرًا، ستجري بطبيعة الحال الكثير من تعديلات المخطط الصغيرة. هذا طبيعي. يظهر الخطر عندما تعني تلك التعديلات تغييرًا في قاعدة البيانات غير متوافق مع الرجوع للخلف، أو عندما لا تكون هجرتك حتمية.
طريقة عملية لإدارة هذا هي التخطيط لتطور مخطط آمن عند إعادة التوليد كسلسلة من خطوات صغيرة وقابلة للعكس. بدل أن تقوم بتحويل كبير مرة واحدة، قم بتحركات مسيطرة تبقي مسارات الكود القديمة والجديدة تعمل معًا لفترة قصيرة.
على سبيل المثال، إذا أردت إعادة تسمية عمود يستخدمه API حي، لا تسمّه فورًا. أضف أولًا العمود الجديد، اكتب إلى العمودين معًا، اعمل backfill للصفوف الموجودة، ثم حول القراءة إلى العمود الجديد. فقط بعد ذلك أزل العمود القديم. كل خطوة سهلة الاختبار، وإذا حدث خطأ يمكنك الإيقاف دون إفساد البيانات.
هذا النموذج الذهني يجعل الهجرات متوقعة، حتى عندما يحدث إعادة توليد للكود يوميًا.
أنواع تغييرات المخطط وأيها يكسر الإنتاج
عندما يُعاد توليد الواجهة الخلفية من المخطط الأحدث، يفترض الكود عادةً أن قاعدة البيانات تطابق ذلك المخطط الآن. لذلك، تطور المخطط الآمن عند إعادة التوليد يتعلق أكثر بـ "هل يمكن للبيانات والطلبات القديمة النجاة أثناء طرح التغيير؟".
بعض التغييرات آمنة بطبيعتها لأنها لا تبطل الصفوف أو الاستعلامات الحالية. أخرى تغير معنى البيانات أو تزيل شيئًا ما ما زال التطبيق الجاري يتوقعه، وهنا تقع الحوادث في الإنتاج.
منخفضة المخاطر، عادةً آمنة (إضافية)
التغييرات الإضافية هي الأسهل لأنّها يمكن أن تتعايش مع البيانات القديمة.
- جدول جديد لا يعتمد عليه شيء بعد.
- عمود جديد يمكن أن يقبل NULL دون شرط افتراضي.
- حقل API جديد اختياري في كل المسارات.
مثال: إضافة عمود middle_name قابل لأن يكون NULL إلى جدول users عادةً آمن. تظل الصفوف القديمة صالحة؛ الكود المعاد توليده يمكنه قراءته عندما يكون موجودًا، والصفوف القديمة ببساطة تحتوي على NULL.
متوسطة المخاطر (تغيّر المعنى)
هذه التغييرات غالبًا "تعمل" تقنياً ولكن تكسر السلوك. تحتاج لتنسيق لأن إعادة التوليد تحدّث التحققات، النماذج المولّدة، وافتراضات منطق العمل.
إعادة التسمية فخ كلاسيكي: إعادة تسمية phone إلى mobile_phone قد تؤدي إلى كود مولّد لم يعد يقرأ phone بينما البيانات في الإنتاج ما زالت هناك. بالمثل، تغيير الوحدات (تخزين السعر بالدولار مقابل السنت) يمكن أن يفسد الحسابات صامتًا إذا حدّثت الكود قبل البيانات، أو البيانات قبل الكود.
الـ enums حافة حادة أخرى. تشديد enum (إزالة قيم) يمكن أن يجعل الصفوف الحالية غير صالحة. توسيع enum غالبًا آمن، لكن فقط إذا كانت كل مسارات الكود قادرة على التعامل مع القيمة الجديدة.
نهج عملي هو معاملة تغييرات المعنى كـ "أضف جديدًا، املأ، حول، ثم ازل لاحقًا".
عالية المخاطر (تدميرية)
التغييرات التدميرية هي التي تكسر الإنتاج فورًا في أغلب الأحيان، خاصة عندما يعيد النظام توليد الكود الذي يتوقف عن توقع الشكل القديم.
حذف عمود، حذف جدول، أو تحويل عمود من قابل لأن يكون NULL إلى غير قابل لذلك يمكن أن يفشل الكتابات فورًا عندما يحاول أي طلب إدراج صف بدون تلك القيمة. حتى لو ظننت "كل الصفوف لديها ذلك"، فإن الحالة التالية أو وظيفة خلفية قد تثبت العكس.
إذا اضطررت إلى تغيير ليصبح الحقل غير قابل لأن يكون NULL، قم به على مراحل: أضف العمود كقابل لأن يكون NULL، املأه، حدّث منطق التطبيق دائمًا ليضبطه، ثم طبق NOT NULL.
تغييرات الأداء والسلامة (قد توقف الكتابات)
الفهارس والقيود ليست "تغييرات شكل البيانات" لكن يمكنها أن تسبب توقفًا. إنشاء فهرس على جدول كبير أو إضافة قيد فريد قد يقفل الكتابات لوقتٍ كافٍ يسبب انتهاء مهلات. في PostgreSQL، بعض العمليات أكثر أمانًا عند تنفيذها بطرق صديقة للعملية عبر الإنترنت، لكن النقطة الأساسية هي التوقيت: نفّذ العمليات الثقيلة خلال وقت حركة منخفضة وقس المدة في نسخة اختبار.
عندما تحتاج تغييرات لرعاية إضافية في الإنتاج، خطط لـ:
- نشر على مرحلتين يبقى متوافقًا (المخطط أولًا ثم الكود أو العكس).
- backfills تُشغّل على دفعات.
- مسار تراجع واضح (ماذا يحدث لو دخلت الواجهة الخلفية المعاد توليدها حيًا مبكرًا).
- استعلامات تحقق تثبت أن البيانات تطابق القواعد الجديدة.
- تذكرة "إزالة الحقل القديم لاحقاً" حتى لا يحدث التنظيف في نفس عملية النشر.
إذا كنت تستخدم منصة مثل AppMaster التي تعيد توليد كود الواجهة الخلفية من Data Designer، فإن أسلوب التفكير الأكثر أمانًا هو: أرسل تغييرات يمكن أن تتعايش معها بياناتك القديمة اليوم، ثم شدد القواعد فقط بعد أن يتكيف النظام فعليًا.
مبادئ للتغييرات الآمنة عند إعادة التوليد
الواجهات الخلفية المعاد توليدها رائعة حتى يصل تغيير مخطط إلى الإنتاج وتصبح الصفوف القديمة لا تطابق الشكل الجديد. هدف تطور المخطط الآمن واضح: أبقِ تطبيقك يعمل بينما تتوافق قاعدة البيانات والكود المعاد توليده معًا على خطوات صغيرة ومتوقعة.
افترض دائمًا "التوسيع، الترحيل، التقليص"
عامل كل تغيير ذي مغزى كحركة من ثلاث خطوات. أولًا، وسّع المخطط بحيث يعمل الكود القديم والجديد معًا. ثم نزّل البيانات. وبعد ذلك، قلّص بإزالة الأعمدة القديمة أو الإعدادات أو القيود.
قاعدة عملية: لا تدمج "هيكل جديد" و"تنظيف كاسر" في نفس النشر.
ادعم الشكل القديم والجديد لفترة
افترض أنه ستتصادف فترة يكون فيها:
- بعض السجلات تحتوي الحقول الجديدة وبعضها لا
- بعض مثيلات التطبيق تعمل بالكود القديم وبعضها تم تحديثه
- الوظائف الخلفية أو الواردات أو عملاء المحمول قد يتأخرون
صمّم قاعدة البيانات بحيث تكون الأشكال القديمة والجديدة صالحة أثناء هذا التداخل. هذا مهم أكثر عندما تعيد منصة توليد واجهتك الخلفية من النموذج الأحدث (مثلاً AppMaster عندما تحدّث Data Designer وتعيد توليد backend بـ Go).
اجعل القراءة متوافقة قبل الكتابة
ابدأ بجعل الكود الجديد قادرًا على قراءة البيانات القديمة بأمان. فقط بعد ذلك غيّر مسارات الكتابة لإنتاج الشكل الجديد.
مثال: إذا قسمت حقل status إلى status + status_reason، أطلق كودًا يستطيع التعامل مع غياب status_reason أولًا. بعد ذلك، ابدأ بكتابة status_reason للتحديثات الجديدة.
قرر كيف تتعامل مع البيانات الجزئية والمجهولة
عند إضافة enums أو أعمدة غير قابلة لأن تكون NULL أو قيود أشد، قرر مسبقًا ماذا يحدث عند غياب القيم أو عند وجود قيم غير متوقعة:
- السماح بـ NULL مؤقتًا ثم عمل backfill
- وضع قيمة افتراضية آمنة لا تغير المعنى
- الحفاظ على قيمة "غير معروفة" لتجنب فشل القراءة
هذا يمنع الفساد الصامت (قيم افتراضية خاطئة) والأخطاء الصارخة (قيود جديدة ترفض الصفوف القديمة).
احرص على وجود قصة تراجع لكل خطوة
التراجع أسهل أثناء مرحلة التوسيع. إذا احتجت للتراجع، يجب أن يعمل الكود القديم مقابل المخطط الموسع. اكتب ما ستتراجعه (الكود فقط أو الكود بالإضافة إلى الهجرة)، وتجنب التغييرات التدميرية حتى تتأكد أنك لن تحتاج للتراجع.
خطوة بخطوة: خطط لتغيير يصمد أمام إعادة التوليد
الواجهات الخلفية المعاد توليدها لا ترحم: إذا اختلف المخطط والكود المولد، الإنتاج عادةً ما يكتشف ذلك أولًا. الأسلوب الأكثر أمانًا هو معاملة كل تغيير كطرح صغير وقابل للعكس، حتى لو كنت تبني بأدوات بدون كود.
ابدأ بكتابة النية بلغة بسيطة وما تبدو عليه بياناتك اليوم. اختر 3 إلى 5 صفوف حقيقية من الإنتاج (أو تفريغ حديث) ودوّن الأجزاء الفوضوية: قيم فارغة، صيغ قديمة، افتراضات مفاجئة. هذا يمنعك من تصميم حقل جديد مثالي لا تستطيع البيانات الحقيقية تلبيته.
إليك تسلسل عملي يعمل جيدًا عندما تعيد منصتك توليد الواجهة الخلفية:
-
وسّع أولًا، لا تستبدل. أضف أعمدة أو جداول جديدة بطريقة إضافية. اجعل الحقول الجديدة قابلة لأن تكون NULL في البداية، أو أعطها قيمًا افتراضية آمنة. إذا أدخلت علاقة جديدة، اترك المفتاح الأجنبي فارغًا حتى تعبئه.
-
انشر المخطط الموسّع دون إزالة أي شيء. نفّذ تغيير قاعدة البيانات بينما الكود القديم ما زال يعمل. الهدف: يستمر الكود القديم في كتابة الأعمدة القديمة، وقاعدة البيانات تقبل ذلك.
-
املأ البيانات عبر عملية مراقبة. عَبِّأ الحقول الجديدة باستخدام عملية دفعية يمكنك مراقبتها وإعادة تشغيلها. اجعلها قابلة للتكرار (التشغيل مرتين لا يفسد البيانات). قم بذلك تدريجيًا إذا كان الجدول كبيرًا وسجّل عدد الصفوف المحدثة.
-
حوّل القراءات أولًا، مع إمكانية الرجوع. حدّث المنطق المولد ليُفضّل الحقول الجديدة، لكن يعود للحقل القديم عندما تكون البيانات الجديدة مفقودة. بعد استقرار القراءات، حول الكتابات إلى الحقول الجديدة.
-
نظّف في الأخير. بعد أن تتأكد ولديك خطة تراجع، أزل الحقول القديمة وشدّد القيود: ضع NOT NULL، أضف قيود فريدة، وفعل المفاتيح الأجنبية.
مثال ملموس: تريد استبدال حقل نصي حر status بـ status_id يشير إلى جدول statuses. أضف status_id كقابل لأن يكون NULL، املأه من القيم النصية الحالية، حدّث التطبيق لقراءة status_id ولكن الاعتماد على status عند وجود null، ثم احذف status واجعل status_id مطلوبًا. هذا يحفظ أمان كل مرحلة لأن قاعدة البيانات تبقى متوافقة.
أنماط عملية يمكنك إعادة استخدامها
عندما تُعاد توليد الواجهة الخلفية، يمكن لتعديلات المخطط الصغيرة أن تنتشر لتؤثر على APIs، قواعد التحقق، ونماذج الواجهة. هدف تطور المخطط الآمن هو إجراء تغييرات تبقي البيانات القديمة صالحة أثناء نشر الكود الجديد.
النمط 1: إعادة تسمية غير مكسرة
إعادة تسمية مباشرة مخاطرة لأن الصفوف والكود القديم غالبًا ما لا تزال تتوقع الحقل الأصلي. نهج أكثر أمانًا هو معاملتها كفترة هجرة قصيرة.
- أضف العمود الجديد (مثلاً
customer_phone) واحتفظ بالقديم (phone). - حدّث المنطق للكتابة إلى العمودين: عند الحفظ، اكتب لكليهما.
- املأ الصفوف الحالية بحيث يمتلئ
customer_phoneلكل السجلات. - حول القراءة إلى العمود الجديد عندما يكون التغطية مرتفعة.
- احذف العمود القديم في إصدار لاحق.
هذا يعمل جيدًا في أدوات مثل AppMaster حيث سيعيد التوليد بناء النماذج والنقاط النهائية من المخطط الحالي. الكتابة المزدوجة تبقي كلا الجيلين من الكود سعيدين أثناء الانتقال.
النمط 2: تقسيم حقل إلى اثنين
تقسيم full_name إلى first_name وlast_name مشابه، لكن الـ backfill أصعب. احتفظ بـ full_name حتى تتأكد أن الانقسام مكتمل.
قاعدة عملية: لا تزل الحقل الأصلي حتى تُملأ كل السجلات أو يكون هناك طريقة واضحة للرجوع. مثلاً، إذا فشل التحليل، خزّن السلسلة كاملة في last_name وعلّم السجل للمراجعة.
النمط 3: جعل الحقل مطلوبًا
تحويل حقل قابل لأن يكون NULL إلى مطلوب كسر شائع للإنتاج. الترتيب الآمن: املأ أولًا، ثم فرض القيد.
يمكن أن يكون الـ backfill ميكانيكيًا (ضبط قيمة افتراضية) أو بدافع تجاري (طلب من المستخدمين إكمال البيانات). بعد اكتمال البيانات، أضف NOT NULL وحدّث التحققات. إذا أضافت الواجهة الخلفية المولّدة تحققًا أشد تلقائيًا، فإن هذا الترتيب يمنع الفشل المفاجئ.
النمط 4: تغيير enum بأمان
تكسر تغييرات enum حين يرسل الكود القديم قيمًا قديمة. أثناء الانتقال، اقبل القيمتين معًا. إذا تستبدل "pending" بـ "queued"، احتفظ بالقيمة القديمة صالحة وخريطة لها. بعد التأكد من عدم إرسال العملاء للقيمة القديمة، أزلها.
إذا اضطر التغيير إلى النزول في إصدار واحد، قلل المخاطرة بتقليص مجال التأثير:
- أضف حقولًا جديدة واحتفظ بالقديمة حتى لو أصبحت غير مستخدمة.
- استخدم قيمة افتراضية في قاعدة البيانات حتى تستمر الإدخالات بالعمل.
- اجعل الكود متسامحًا: اقرأ من الجديد وارجع للقديم.
- أضف طبقة تحويل مؤقتة (القيمة القديمة تدخل وتخزن على شكل جديد).
تُبقي هذه الأنماط الهجرات متوقعة حتى عندما يتغير سلوك الكود المولد بسرعة.
أخطاء شائعة تسبب مفاجآت
أكبر المفاجآت تحدث عندما يعامل الناس إعادة التوليد كزر سحري لإعادة التعيين. يمكن أن تحافظ الواجهات الخلفية المولّدة على نظافة كود التطبيق، لكن قاعدة بيانات الإنتاج ما زالت تحتوي بيانات الأمس، بشكلها القديم. تطور المخطط الآمن يعني التخطيط لكل منهما: الكود الجديد الذي سيتولد والسجلات القديمة التي ستبقى في الجداول.
فخ شائع هو افتراض أن المنصة "ستتعامل مع الهجرات" نيابة عنك. مثلاً، في AppMaster يمكنك إعادة توليد backend بـ Go من نموذج Data Designer المحدث، لكن المنصة لا تستطيع أن تعرف كيف تريد تحويل بيانات العملاء الحقيقية. إذا أضفت حقلًا مطلوبًا جديدًا، لا تزال بحاجة لخطة واضحة لكيفية إعطاء قيمة للصفوف الحالية.
مفاجأة أخرى هي حذف أو إعادة تسمية الحقول مبكرًا. قد يبدو الحقل غير مستخدم في الواجهة الرئيسية، لكن لا يزال يُقرأ في تقرير، تصدير مجدول، معالج webhook، أو شاشة إدارة نادراً ما تُفتح. التغيير يظهر آمناً في الاختبار، ثم يفشل في الإنتاج لأن مسارًا واحدًا نُسي.
إليك خمسة أخطاء تؤدي عادةً إلى تراجعات في منتصف الليل:
- تغيير المخطط وإعادة توليد الكود دون كتابة أو التحقق من هجرة البيانات التي تجعل الصفوف القديمة صالحة.
- إعادة تسمية أو حذف عمود قبل تحديث كل قارئ وكاتب ونشره.
- ملء جدول كبير دون التحقق من مدة التشغيل وما إذا كان سيعرقل كتابات أخرى.
- إضافة قيد جديد (NOT NULL, UNIQUE, مفتاح أجنبي) أولًا ثم اكتشاف بيانات قديمة تكسره.
- نسيان الوظائف الخلفية، التصديرات، والتقارير التي لا تزال تقرأ الحقول القديمة.
سيناريو بسيط: تعيد تسمية phone إلى mobile_number، تضيف قيد NOT NULL، وتعيد التوليد. قد تعمل شاشات التطبيق، لكن تصدير CSV قديم لا يزال يختار phone، وآلاف السجلات لها mobile_number فارغ. الحل عادةً تغيير مرحلي: أضف العمود الجديد، اكتب إلى كلاهما لفترة، املأ البيانات بأمان، ثم شدّد القيود وأزل الحقل القديم بعد دليل أن لا شيء يعتمد عليه.
قائمة تفقد سريعة قبل النشر لهجرات أكثر أمانًا
عندما تُعاد توليد الواجهة الخلفية، يمكن للكود أن يتغير بسرعة، لكن بيانات الإنتاج لن تغفر المفاجآت. قبل أن تنشر تغييرًا في المخطط، أجرِ فحصًا سريعًا "هل يمكن أن يفشل هذا بأمان؟". يجعل ذلك تطور المخطط الآمن مملاً (وهذا ما تريده).
الفحوص الخمسة التي تكتشف معظم المشاكل
- حجم وسرعة الbackfill: قدّر كم صفًا تحتاج لتحديثه وكم سيستغرق في الإنتاج. ما يعمل على قاعدة صغيرة قد يستغرق ساعات في البيانات الحقيقية ويبطئ التطبيق.
- المخاطر المتعلقة بالقفل ووقت التوقف: حدّد ما إذا كان التغيير قد يوقف الكتابات. بعض العمليات (تغيير نوع عمود، تعديل جداول كبيرة) قد تحجز أقفالًا زمنية. إذا كان هناك احتمال للحجب، خطط لطرح آمن (أضف عمودًا أولًا، املأ لاحقًا، غيّر الكود آخرًا).
- توافق الكود القديم مع المخطط الجديد: افترض أن النسخة القديمة من الواجهة الخلفية قد تعمل لفترة قصيرة ضد المخطط الجديد أثناء النشر أو التراجع. اسأل: هل سيقرأ ويكتب الإصدار السابق دون تعطل؟ إذا لا، تحتاج لإصدار على مرحلتين.
- السلوك الافتراضي وNULL: للحقول الجديدة، قرر ماذا سيحدث للسجلات القديمة. هل ستكون NULL أم تحتاج قيمة افتراضية؟ تأكد أن المنطق يتعامل مع القيم المفقودة كأمر طبيعي، خاصة للعلامات، حقول الحالة والطوابع الزمنية.
- إشارات المراقبة التي تراقبها: اختر المنبهات الدقيقة التي ستتابعها بعد النشر: معدل الأخطاء (فشل API)، استعلامات قاعدة بيانات بطيئة، فشل طوابير/وظائف، وأي إجراء مستخدم أساسي (الدفع، تسجيل الدخول، إرسال نموذج). راقب أيضًا الأخطاء الصامتة مثل ارتفاع أخطاء التحقق.
مثال سريع
إذا أضفت حقل مطلوب جديد مثل status إلى جدول orders، لا تدفعه كـ "NOT NULL بدون افتراضي" دفعة واحدة. أضفه أولًا كقابل لأن يكون NULL مع افتراضي للصفوف الجديدة، انشر كودًا يتعامل مع غياب status، ثم املأ الصفوف القديمة، وبعدها شدّد القيود. في AppMaster، هذا التفكير مفيد لأن الواجهة الخلفية قد تُعاد توليدها كثيرًا. عامل كل تغيير كمشروع صغير مع تراجع سهل، فتظل هجراتك متوقعة.
مثال: تطوّر تطبيق حي دون كسر السجلات الحالية
تخيّل أداة دعم داخلية حيث يعلّم الوكلاء التذاكر بحقل نصي حر priority (أمثلة: high, urgent, HIGH, p1). تريد التحول إلى enum صارم حتى تتوقف التقارير وقواعد التوجيه عن التخمين.
النهج الآمن هو تغيير على مرحلتين يحافظ على صلاحية السجلات القديمة أثناء إعادة توليد الواجهة الخلفية.
الإصدار 1: وسّع، اكتب في الحقلين، واملأ
ابدأ بتوسيع المخطط دون إزالة أي شيء. أضف حقل enum جديد، مثلاً priority_enum بقيم مثل low, medium, high, urgent. احتفظ بالحقل النصي الأصلي priority_text.
ثم حدّث المنطق بحيث تكتب التذاكر الجديدة والمعدلة إلى الحقلين. في أداة دون كود مثل AppMaster، يعني ذلك عادةً تعديل نموذج Data Designer وتحديث عملية العمل لتخطيط المدخلات إلى enum وأيضًا حفظ النص الأصلي.
بعد ذلك املأ التذاكر الموجودة على دفعات صغيرة. خرائط القيم الشائعة إلى enum (p1 وurgent -> urgent, HIGH -> high). أي شيء غير معروف يمكن مؤقتًا أن يُحول إلى medium أثناء المراجعة.
ما يراه المستخدمون: في الغالب لا شيء يتغير. يمكن للواجهة عرض نفس عنصر التحكم بالأولوية، لكن خلف المشهد تملأ enum الجديد. يمكن للتقارير أن تبدأ باستخدام enum بمجرد بدء backfill.
الإصدار 2: تقليص وإزالة المسار القديم
بعد أن تطمئن، حول القراءات لاستخدام priority_enum فقط، حدّث الفلاتر ولوحات البيانات، ثم احذف priority_text في هجرة لاحقة.
قبل الإصدار 2، تحقق بعينة صغيرة لتلتقط حالات الحافة:
- اختر 20 إلى 50 تذكرة عبر فرق وأعمار مختلفة.
- قارِن الأولوية المعروضة مع قيمة enum المخزنة.
- راجع التوزيع حسب enum لاكتشاف قفزات مريبة (مثلاً، كثرة
medium).
إن ظهرت مشكلات، فالتراجع بسيط لأن الإصدار 1 احتفظ بالحقل القديم: أعد نشر منطق الإصدار 1 واجعل الواجهة تقرأ من priority_text بينما تصلح الخرائط وتعيد تشغيل الbackfill.
الخطوات التالية: اجعل تطور المخطط عادة قابلة للتكرار
إذا أردت هجرات متوقعة، عامل تغييرات المخطط كمشروع صغير، لا كتعديل سريع. الهدف بسيط: يجب أن يكون كل تغيير سهل الشرح، قابل للتدريب، وصعب أن يكسر بالخطأ.
نموذج بيانات بصري يساعد لأنه يجعل التأثير مرئيًا قبل النشر. عند رؤية الجداول والعلاقات وأنواع الحقول في مكان واحد، تلاحظ أمورًا قد تغفل عنها في سكربت، مثل حقل مطلوب بلا قيمة افتراضية، أو علاقة ستتسبب في يتيم للسجلات القديمة. قم بجولة سريعة "من يعتمد على هذا؟": APIs، الشاشات، التقارير، وأي وظائف خلفية.
عندما تحتاج تغيير حقل مستخدم بالفعل، فضل فترة انتقالية قصيرة مع حقول مكررة. مثلاً، أضف phone_e164 بينما تحتفظ بـ phone_raw لواحد أو إصدارين. حدّث منطق العمل لقراءة الحقل الجديد عند وجوده، والاعتماد على القديم كنسخة احتياطية. اكتب إلى كلا الحقلين خلال الانتقال، ثم أزل القديم بعد التحقق من إتمام الملء.
الانضباط بين البيئات هو ما يحوّل النوايا الحسنة إلى إصدارات آمنة. اجعل dev وstaging وproduction متقاربة لكن لا تعاملها كمتطابقة.
- Dev: تأكد أن الواجهة الخلفية المعاد توليدها تبدأ نظيفة وتعمل التدفقات الأساسية بعد إعادة التوليد.
- Staging: نفّذ خطة الهجرة كاملة على بيانات شبيهة بالإنتاج وتحقق من الاستعلامات والتقارير والاستيرادات الأساسية.
- Production: انشر عندما تمتلك خطة تراجع، مراقبة واضحة، ومجموعة صغيرة من اختبارات "يجب أن تنجح".
اجعل خطة الهجرة وثيقة فعلية، حتى لو كانت قصيرة. تضمّن: ما الذي سيتغير، الترتيب، كيفية الملء، كيفية التحقق، وكيفية التراجع. ثم نفّذها تمامًا في بيئة اختبار قبل أن تلمس الإنتاج.
إذا كنت تستخدم AppMaster، اعتمد على Data Designer للتفكير بصريًا في النموذج ودع إعادة التوليد تحافظ على اتساق كود الواجهة الخلفية مع المخطط المحدث. العادة التي تحافظ على التوقع هي جعل الهجرات صريحة: يمكنك التكرار بسرعة، لكن يبقى لكل تغيير مسار مخطط لبيانات الإنتاج الحالية.


