النوع 1: إصلاح الدولة. النوع 2: تقرير هزيل.
يضرب قلب مقيّد على الساعة. ليس بحاجة. ليس بتغيير. على موازين زمنية.
نوعان، سبب مشترك: وظيفة مُجددة تُستبدل بدلاً من تصميم صحيح.
النوع 1: إصلاح الدولة
يتأخر انتقال الحالة من الانتهاء الذاتي. بدلاً من تصليح الانتقال، تُجرى وظيفة خلفية على تأخير وتسوية. يرون حالة مكسورة خلال نافذة التسوية.
مثال GitHub (2026-04-08): تم تغيير مخزن طلب المساعدة إلى خاص. حاول GitHub انتقال الحالة: إغلاق طلب المساعدة، تحديث حالة الفرع، إزالة حالة الانضمام. لم يكتمل الانتقال الذاتيًا. ظهرت حالة طلب المساعدة 'مغلقة بواسطة الفرع' و'لا يمكن تحميل حالة الانضمام' في نفس الوقت. تُجرى وظيفة Sidekiq الخلفية دقيقةً لاحقة وتكتمل التسوية. رأى المراقبون حالة مكسورة طوال مدة النافذة.
قلب مقيّد: تُجرى وظيفة Sidekiq على جدول الزمن. لم تُجرى بسبب كشف GitHub للحالة المكسورة؛ تُجرى على موازين زمنية. شاهد المستخدم طلب المساعدة في الوقت الفعلي حالة تتناقض مع نفسها حتى تنفيذ التالية للوظيفة.
النوع 2: تقرير هزيل
يتحسب تقرير أو تجميع من الصفر على فترات محددة. لا يوجد فحص التخزين. لا حارس استقلال. لا تحديث تدرجي. كل إجراء: فحص كامل.
مثل: وظيفة كرون يومية تُحسب المبلغ الإجمالي لشراء كل مستخدم من الصفر عن طريق فحص جميع الطلبات منذ البداية. وظيفة تحليليات يومية تُجندرر لوحة التحكم من سجلات الحوادث الناتجة. بريد أسبوعي يُستدعى كل سطر في جدول الأنشطة.
كلها تشتعل بغض النظر عن تغيير البيانات منذ الإجراء الأخير. كلها تفحص التاريخ الكامل حتى عندما يكون هناك بيانات جديدة فقط في الـ 24 ساعة الماضية. كلها تستبدل التكرار المُجدّد تصميم تدرجي.
الجذر المشترك
لا يمكن للقلب المقيّد أن يكذب على حالتِه الخاصة. يعرف فقط الساعة. النوع 1: تُجرى وظيفة إصلاح الحالة في T+5 دقائق بغض النظر عن كون الحالة مكسورة في T+0. النوع 2: تُجرى وظيفة التقرير في الساعة 2 صباحاً بغض النظر عن تغيير البيانات منذ الأمس.
الساعة لا تحمل معلومات عن ما يحتاج إلى القيام به. يحمل الحدث تلك المعلومات: 'فشل انتقال الحالة مؤخرًا'، 'توصلت أوامر جديدة مؤخرًا'. يفقد قلب مقيّد تلك المعلومات وينقدها باستبدالها بساعة.
استنزاف الرأسمال
يجرف قلب القياس الرأسمال الحي: المهندسون في حالة الاستعداد لتقديم الخدمات في حال وقوع الحالات المكسورة. يؤذي الثقة الاجتماعية: يرون بيانات متضاربة ويرفعون الشكاوى التي تتصالح بنفسها. يُضاعف الآثار السلبية الأخرى: مهمة إصلاح الحالة التي تسوق جميع السجلات للبحث عن حالة مكسورة غالباً تحتوي على MOAD-0001 (التنسج O(N²)). مهمة تقرير قد تسبب MOAD-0005 (الفرار من الخانة المشتركة). تُضاعف الآثار السلبية MOAD-0009 للعيوب الأخرى.
الجذر المشترك
تبدو حالة 1 وحالة 2 مختلفة على السطح: إحداهما ترميم الحالة والأخرى إعادة الحساب. يربط السبب المشترك بينهما.
الاشتعال على التغيير وليس على الساعة
تصميم الحدثي يشتعل عند حدوث تغيير. التغيير هو الحدث. الحدث هو المفتاح.
حالة 1: استبدال الوظيفة الخلفية بالانتقال الذاتي.
إذا كان الانتقال يمكن أن يترك النظام في حالة مكسورة، فإن العيب موجود في الانتقال وليس في عدم وجود وظيفة ترميم. قم بإصلاح الانتقال ليكون تمامًا (أو متسلسلاً). عندما يكون الانتقال تمامًا، لا توجد حالة مكسورة أبداً. ليس لديها وظيفة ترميم.
# العيب: انتقال غير ذو تأثير يترك حالة مكسورة
def close_pr_on_repo_private(pr_id):
pr = PR.get(pr_id)
pr.status = 'branch-forced-closed' # الخطوة 1: حالة جزئية
pr.save() # مرئي للمستخدمين الآن
# ... قد تفشل الخطوات الأخرى ...
pr.merge_status = 'not_applicable'
pr.save() # step 2: now consistent
# Sidekiq job reconciles if step 2 fails
# FIX: atomic transition; no intermediate state visible
def close_pr_on_repo_private(pr_id):
with db.transaction():
pr = PR.get(pr_id)
pr.status = 'branch-forced-closed'
pr.merge_status = 'not_applicable'
pr.save() # both fields commit atomically; never half-written
Form 2: the incremental update replaces the full recompute.
A report that recomputes from scratch fires because old data + new data = new result. But the old result + delta = same new result, computed incrementally. The event: new data arrived. The trigger: update the aggregate for the new data only.
# DEFECT: full recompute on schedule
def nightly_totals_job():
for user in all_users():
total = sum(o.amount for o in user.orders) # scan all time
user.total_purchases = total
user.save()
# FIX: event-driven incremental update
def on_order_placed(order):
order.user.total_purchases += order.amount # delta only
order.user.save()
The incremental update fires when an order arrives, not at 2 AM. It updates only the affected user. It reads only the new order, not all orders from all time. The nightly job disappears.
Why Form 1 Reveals a Broken Transition
A Form 1 Metered Heart reveals that a state transition was left incomplete. The repair job exists because an engineer noticed broken state & added a reconciliation mechanism rather than fixing the transition. The repair job: a patch over a broken architectural decision.
MOAD-0009 as Amplifier
MOAD-0009 amplifies other MOADs. A state-repair job that scans all records to find broken state: MOAD-0001 (O(N) or O(N²) scan per job run). A report job that recomputes everything cold: MOAD-0005 (cache stampede when the job starts & hits a warm upstream). MOAD-0009 does not just harm by itself; it delivers other MOADs on a schedule.
Diagnose & Redesign
فريق يعمل على وظيفة كرون ليلةً في الساعة 2 ص. يتم فحص جميع الطلبات لجميع المستخدمين و إعادة تقدير المبلغ الإجمالي للشراء لكل مستخدم من الصفر. تستغرق الوظيفة 4 ساعات. حتى الساعة 6 ص، يظهر الجدول التحليلي الأعداد الكاملة الجديدة. بين الساعة 2 ص والساعة 6 ص، يظهر الجدول التحليلي الأعداد الكاملة من يوم أمس.