كانت تبعيات مهامنا كابوساً لا ينتهي: كيف أنقذنا ‘الفرز الطوبولوجي’ من جحيم التنفيذ العشوائي؟

يا جماعة، السلام عليكم ورحمة الله وبركاته.

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

كنا نعمل على نظام معالجة بيانات ضخم، يتكون من عشرات المهام (Tasks) التي يجب أن تعمل بتسلسل معين. مهمة تنظف البيانات، وأخرى تستخلص السمات، وثالثة تدرب النموذج، ورابعة تقوم بالتقييم… وهكذا. كل مهمة تعتمد على مخرجات مهمة أو أكثر قبلها. في البداية، كنا ندير هذا التسلسل يدوياً. كان أحد المطورين يصرخ في القناة العامة على “سلاك”: “يا شباب، مهمة تنظيف البيانات جاهزة، مين يشغل مهمة استخلاص السمات؟”.

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

وهنا، بعد ليلة طويلة من البحث والتفكير، لمع في ذهني مصطلح كنت قد درسته في الجامعة ونسيته تقريباً: “الفرز الطوبولوجي” (Topological Sort). كانت تلك هي اللحظة التي بدأ فيها كل شيء يتغير.

ما هو الجحيم الذي كنا نعيشه؟ (فوضى التبعيات)

قبل أن نغوص في الحل، دعونا نفهم المشكلة بشكل أعمق. التبعيات موجودة في كل مكان في عالم البرمجيات:

  • أنظمة البناء (Build Systems): لا يمكنك بناء البرنامج النهائي قبل تجميع (compile) كل وحداته (modules) التي يعتمد عليها.
  • مديرو الحزم (Package Managers): عند تثبيت مكتبة برمجية (مثل requests في بايثون)، يجب على مدير الحزم تثبيت تبعياتها أولاً (مثل urllib3).
  • خطوط أنابيب البيانات (Data Pipelines): كما في قصتنا، يجب معالجة البيانات الأولية قبل تجميعها، ويجب تجميعها قبل تدريب النموذج عليها.
  • جداول البيانات: الخلية C1 التي تحتوي على المعادلة =A1+B1 تعتمد على قيم الخلايا A1 و B1.

المشكلة تكمن في أنه عندما يزداد عدد المهام والتبعيات، يصبح من المستحيل على الإنسان تتبع الترتيب الصحيح للتنفيذ. نحن بحاجة إلى طريقة منهجية وآلية لتحديد هذا الترتيب. هذا هو بالضبط ما يفعله الفرز الطوبولوجي.

المنقذ: الفرز الطوبولوجي (Topological Sort) ببساطة

الفرز الطوبولوجي ليس خوارزمية فرز بالمعنى التقليدي (مثل فرز الأرقام من الأصغر للأكبر). بل هو خوارزمية لترتيب عقد (nodes) في “مخطط موجّه غير حلقي” بشكل خطي.

لحظة، ما كل هذه المصطلحات؟ دعنا نبسطها.

شروط اللعبة: ما هو المخطط الموجه غير الحلقي (DAG)؟

لنفهم الفرز الطوبولوجي، يجب أن نمثل مشكلتنا على شكل مخطط (Graph). هذا المخطط له ثلاث صفات أساسية:

  1. المخطط (Graph): هو ببساطة مجموعة من “العقد” (النقاط) و “الحواف” (الخطوط التي تصل بينها). في حالتنا، كل “مهمة” هي عقدة.
  2. موجه (Directed): كل حافة لها اتجاه (يتم تمثيلها بسهم). إذا كانت المهمة “ب” تعتمد على المهمة “أ”، نرسم سهماً من “أ” إلى “ب” (أ ← ب). هذا يعني “يجب إكمال ‘أ’ قبل البدء في ‘ب'”.
  3. غير حلقي (Acyclic): هذا هو الشرط الأهم. يعني أنه لا يمكنك أن تبدأ من عقدة وتتبع الأسهم وتعود إلى نفس العقدة مرة أخرى. فكّر فيها منطقياً: لو كانت المهمة “أ” تعتمد على “ب”، و “ب” تعتمد على “أ”، فمتى ستبدأ؟ هذه حلقة مفرغة ومستحيلة التنفيذ. نسمي هذا المخطط اختصاراً DAG (Directed Acyclic Graph).

إذن، الفرز الطوبولوجي يأخذ هذا الـ DAG ويعطينا قائمة مرتبة من المهام تضمن أنه عند تنفيذها بهذا الترتيب، ستكون كل تبعيات المهمة الحالية قد اكتملت بالفعل.

كيف يعمل الفرز الطوبولوجي؟ (الخوارزمية خطوة بخطوة)

هناك عدة طرق لتنفيذ الفرز الطوبولوجي، لكن أشهرها وأكثرها بديهية هي “خوارزمية كان” (Kahn’s Algorithm). لنشرحها بأسلوب عملي وبسيط.

خوارزمية كان (Kahn’s Algorithm): الطريقة العملية

تخيل أنك مدير لمجموعة من العمال (المهام). بعض العمال لا يمكنهم البدء في عملهم إلا بعد أن ينتهي عمال آخرون.

  1. الخطوة الأولى: حساب “الدرجة الواردة” (In-degree)
    لكل مهمة (عقدة)، قم بحساب عدد المهام التي تعتمد عليها مباشرة. أي، كم عدد الأسهم التي تشير إلى هذه العقدة؟ هذا الرقم يسمى “الدرجة الواردة”.
  2. الخطوة الثانية: تحديد نقطة البداية
    ابحث عن جميع المهام التي لها درجة واردة تساوي صفر. هذه هي المهام “المستقلة” التي لا تعتمد على أي شيء آخر. ضعها في قائمة انتظار (Queue) للبدء بها.
  3. الخطوة الثالثة: المعالجة والترتيب
    طالما أن قائمة الانتظار ليست فارغة، قم بما يلي:
    • أخرج مهمة من قائمة الانتظار. هذه هي المهمة التالية التي سيتم تنفيذها. أضفها إلى قائمتك النهائية المرتبة.
    • الآن، اعتبر هذه المهمة قد “اكتملت”. انظر إلى جميع المهام التي كانت تعتمد عليها (أي، جميع العقد التي يخرج منها سهم من هذه العقدة).
    • لكل مهمة “تابعة” من هذه المهام، قم بإنقاص “الدرجة الواردة” الخاصة بها بمقدار واحد (لأن إحدى تبعياتها قد اكتملت للتو).
    • إذا أصبحت الدرجة الواردة لأي من هذه المهام التابعة تساوي صفرًا، فهذا يعني أن جميع تبعياتها قد اكتملت الآن. أضفها إلى قائمة الانتظار.
  4. الخطوة الرابعة: التحقق من النتيجة
    استمر في تكرار الخطوة 3 حتى تفرغ قائمة الانتظار. في النهاية، إذا كان عدد المهام في قائمتك النهائية المرتبة يساوي العدد الإجمالي للمهام، فقد نجحت! لديك ترتيب طوبولوجي صحيح. أما إذا كان العدد أقل، فهذا يعني وجود “حلقة” (cycle) في مخططك، ولا يمكن حلها.

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

لنكتب بعض الكود: تطبيق الفرز الطوبولوجي بلغة Python

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

لنفترض أن لدينا المهام التالية وتبعياتها:

  • `إعداد قاعدة البيانات` (لا تبعيات)
  • `تجميع الواجهات` (لا تبعيات)
  • `بناء الواجهة الخلفية` (تعتمد على `إعداد قاعدة البيانات`)
  • `تشغيل الاختبارات` (تعتمد على `بناء الواجهة الخلفية`)
  • `نشر التطبيق` (تعتمد على `تشغيل الاختبارات` و `تجميع الواجهات`)

from collections import defaultdict, deque

def topological_sort(tasks, dependencies):
    """
    يقوم بتنفيذ الفرز الطوبولوجي باستخدام خوارزمية كان
    :param tasks: قائمة بجميع المهام (العقد)
    :param dependencies: قائمة من الأزواج (أ, ب) حيث ب تعتمد على أ
    :return: قائمة بالمهام مرتبة طوبولوجياً أو رسالة خطأ في حال وجود حلقة
    """
    # 1. بناء المخطط وحساب الدرجة الواردة (in-degree)
    graph = defaultdict(list)
    in_degree = {task: 0 for task in tasks}

    for prereq, task in dependencies:
        graph[prereq].append(task)
        in_degree[task] += 1

    # 2. إضافة العقد التي ليس لها تبعيات (in-degree = 0) إلى قائمة الانتظار
    queue = deque([task for task in tasks if in_degree[task] == 0])
    
    sorted_order = []

    # 3. المعالجة والترتيب
    while queue:
        current_task = queue.popleft()
        sorted_order.append(current_task)

        # إنقاص درجة الجيران وإضافتهم إلى قائمة الانتظار إذا أصبحت درجتهم صفر
        for neighbor in graph[current_task]:
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)
    
    # 4. التحقق من وجود حلقات
    if len(sorted_order) == len(tasks):
        return sorted_order
    else:
        return "خطأ: توجد حلقة (cycle) في التبعيات ولا يمكن إيجاد ترتيب صحيح."

# تعريف المهام والتبعيات لمثالنا
tasks = [
    "إعداد قاعدة البيانات", 
    "تجميع الواجهات", 
    "بناء الواجهة الخلفية", 
    "تشغيل الاختبارات", 
    "نشر التطبيق"
]
dependencies = [
    ("إعداد قاعدة البيانات", "بناء الواجهة الخلفية"),
    ("بناء الواجهة الخلفية", "تشغيل الاختبارات"),
    ("تشغيل الاختبارات", "نشر التطبيق"),
    ("تجميع الواجهات", "نشر التطبيق")
]

# تنفيذ الفرز
sorted_tasks = topological_sort(tasks, dependencies)

print("الترتيب الصحيح لتنفيذ المهام:")
print(sorted_tasks)

# مثال على إحدى النتائج الممكنة:
# ['إعداد قاعدة البيانات', 'تجميع الواجهات', 'بناء الواجهة الخلفية', 'تشغيل الاختبارات', 'نشر التطبيق']

لاحظ أن هناك ترتيباً آخر ممكناً وصحيحاً أيضاً: ['تجميع الواجهات', 'إعداد قاعدة البيانات', ...] لأن كلتا المهمتين ليس لهما تبعيات ويمكن البدء بأي منهما.

نصائح من خبرة أبو عمر 🧔

  • ليس هناك حل واحد فريد دائماً: كما رأينا في المثال، قد يكون هناك أكثر من ترتيب طوبولوجي صحيح. الخوارزمية ستعطيك واحداً منها. المهم هو أن أي ترتيب تنتجه سيكون صحيحاً ويحترم جميع التبعيات.
  • الأداء مهم في المشاريع الكبيرة: تعقيد هذه الخوارزمية هو O(V + E) حيث V هو عدد العقد (المهام) و E هو عدد الحواف (التبعيات). هذا يعني أنها سريعة جداً وفعالة حتى مع آلاف المهام.
  • أبعد من مجرد كود: الفرز الطوبولوجي هو نموذج عقلي قوي. يمكنك استخدامه لتخطيط أي مشروع معقد، سواء كان ذلك إعداد وجبة عشاء من عدة أطباق، أو تنظيم حفل زفاف، أو إدارة مراحل مشروع بناء. فكّر في المهام والتبعيات، وستجد أنك ترسم مخطط DAG في عقلك.
  • استخدم الأدوات الجاهزة: معظم أنظمة إدارة المهام وخطوط أنابيب البيانات الحديثة (مثل Apache Airflow, Luigi, dbt) تستخدم الفرز الطوبولوجي خلف الكواليس. فهمك لهذه الخوارزمية سيجعلك تستخدم هذه الأدوات بفعالية أكبر بكثير.

الخلاصة: من الفوضى إلى النظام ✅

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

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

لذا في المرة القادمة التي تجد فيها نفسك غارقاً في فوضى المهام المترابطة، تذكر قصة أبو عمر، وخذ نفساً عميقاً، وارسم مخططك، ودع الفرز الطوبولوجي يعيد النظام إلى عالمك. إنه شغل مرتب على الآخر!

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

تجربة المستخدم والابداع البصري

كانت واجهاتنا خليطاً عجيباً: كيف أنقذتنا ‘رموز التصميم’ (Design Tokens) من فوضى التناقضات؟

أشارككم قصة من قلب المعركة البرمجية، كيف انتقلنا من فوضى الألوان والأحجام المتضاربة بين تطبيقاتنا على الويب وiOS وأندرويد، إلى نظام متناغم وموحد. الفضل يعود...

29 أبريل، 2026 قراءة المزيد
الحوسبة السحابية

كانت الأحمال المفاجئة تكسر ظهرنا: كيف أنقذتنا الحوسبة بدون خوادم (Serverless) من جحيم التوسع اليدوي؟

أشارككم قصة حقيقية من الميدان، يوم كادت الخوادم أن تنهار تحت ضغط المستخدمين المفاجئ. اكتشفوا كيف غيرت بنية "Serverless" طريقة عملنا، وحولتنا من إطفاء الحرائق...

29 أبريل، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

كنت أعرف الإجابة التقنية ولكني أرسب: كيف أنقذتني طريقة ‘STAR’ من جحيم المقابلات السلوكية؟

أنا أبو عمر، مبرمج فلسطيني خبير، وأشارككم قصتي مع المقابلات السلوكية التي كادت أن تدمر مسيرتي المهنية. اكتشفوا كيف حولت طريقة 'STAR' فشلي المتكرر إلى...

29 أبريل، 2026 قراءة المزيد
البودكاست