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

يوم لا يُنسى في مكتبي: قهوة باردة وكود عالق

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

صراخ الإشعارات لم يتوقف: “المهمة X فشلت!”، وبعدها بدقائق: “فشل في المهمة Y”. كان الفريق في حالة ضياع. المبرمج المسؤول عن المهمة X يصرخ: “المهمة فشلت عشان المهمة Z لسا ما جهزت الملفات المطلوبة!”. ويرد عليه زميله: “بس Z بتستنى نتيجة من المهمة A اللي أنت لسا ما شغلتها!”. فوضى عارمة، أو كما نقول بلهجتنا “عجقة ما إلها أول من آخر”.

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

شو قصة ‘الفرز الطوبولوجي’ هاد؟

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

تبسيط المفهوم: من وصفة الكنافة إلى الكود

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

  1. تجهيز العجينة وفركها بالسمنة.
  2. فرد نصف العجينة في الصينية.
  3. وضع الجبنة.
  4. تغطيتها بالنصف الآخر من العجينة.
  5. خبزها في الفرن حتى تتحمر.
  6. قلبها في صينية التقديم.
  7. تشريبها بالقطر الساخن.

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

الأساس النظري: الرسوم البيانية الموجهة غير الدائرية (DAGs)

في عالم الخوارزميات، نحن نمثل هذه الاعتماديات باستخدام ما يسمى بـ “الرسم البياني الموجه غير الدائري” أو (Directed Acyclic Graph – DAG). لا تدع الاسم يخيفك، فالأمر بسيط:

  • رسم بياني (Graph): مجموعة من النقاط (Nodes/Vertices) التي تمثل المهام، وخطوط (Edges) تصل بينها تمثل الاعتماديات.
  • موجه (Directed): كل خط له اتجاه. سهم من المهمة (أ) إلى المهمة (ب) يعني أن (أ) يجب أن تنتهي قبل أن تبدأ (ب).
  • غير دائري (Acyclic): أهم شرط! لا يمكن أن توجد حلقة. أي لا يمكن أن تعتمد (أ) على (ب)، و(ب) على (ج)، ثم (ج) تعود وتعتمد على (أ). هذا سيؤدي إلى حلقة لا نهائية من الانتظار، وهو ما نسميه “الاعتمادية الدائرية” (Circular Dependency)، وهو العدو اللدود للفرز الطوبولوجي.

إذن، الفرز الطوبولوجي هو خوارزمية تأخذ هذا الرسم البياني (DAG) وتعطيك ترتيباً خطياً واحداً (أو أكثر) ممكناً للمهام.

طيب يا أبو عمر، كيف بتشتغل هاي الخوارزمية؟

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

المكونات الأساسية: شو بيلزمنا؟

لتطبيق الخوارزمية، نحتاج إلى تمثيل الرسم البياني، وأهم شيء هو تتبع “درجة الدخول” (In-degree) لكل مهمة. درجة الدخول هي ببساطة عدد المهام التي تشير إلى مهمة معينة (أي عدد المهام التي يجب أن تنتهي قبلها).

الطريقة خطوة بخطوة (خوارزمية كان)

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

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

ورجينا الكود، يا حج!

الكلام النظري جميل، لكن الكود هو الذي يوضح كل شيء. إليك مثال بسيط بلغة Python يطبق خوارزمية كان:


from collections import deque

def topological_sort(graph):
    """
    يقوم بتنفيذ الفرز الطوبولوجي على رسم بياني موجه.
    الرسم البياني يمثل كقاموس حيث المفاتيح هي العقد (المهام)
    والقيم هي قوائم بالعقد التي تعتمد عليها.
    مثال: graph = {'C': ['A', 'B'], 'D': ['C']}
    """
    
    # الخطوة 0: بناء قائمة مجاورة وحساب درجة الدخول
    in_degree = {node: 0 for node in graph}
    # ملاحظة: سنحتاج إلى كل العقد، حتى التي لا تعتمد على شيء
    all_nodes = set(graph.keys())
    for dependencies in graph.values():
        for node in dependencies:
            all_nodes.add(node)
    
    in_degree = {node: 0 for node in all_nodes}
    adj_list = {node: [] for node in all_nodes}

    for node, dependencies in graph.items():
        for dep in dependencies:
            # السهم من dep إلى node
            adj_list[dep].append(node)
            in_degree[node] += 1

    # الخطوة 1: إيجاد كل العقد التي درجة دخولها صفر
    queue = deque([node for node in all_nodes if in_degree[node] == 0])
    
    sorted_order = []
    
    # الخطوة 2: بدء المعالجة
    while queue:
        current_node = queue.popleft()
        sorted_order.append(current_node)
        
        # لكل جار، قلل درجة دخوله
        for neighbor in adj_list[current_node]:
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)
    
    # الخطوة 3: التحقق من وجود حلقة دائرية
    if len(sorted_order) == len(all_nodes):
        return sorted_order
    else:
        return "خطأ: الرسم البياني يحتوي على حلقة دائرية!"

# --- مثال الاستخدام ---
# لنعد إلى مشكلة فريقنا
tasks = {
    'المهمة X': ['المهمة Z'],    # X تعتمد على Z
    'المهمة Y': ['المهمة X'],    # Y تعتمد على X
    'المهمة Z': ['المهمة A'],    # Z تعتمد على A
    'المهمة A': []             # A لا تعتمد على شيء
}

sorted_tasks = topological_sort(tasks)
print("الترتيب الصحيح لتنفيذ المهام:")
print(sorted_tasks)
# الإخراج المتوقع (أحد الاحتمالات): ['المهمة A', 'المهمة Z', 'المهمة X', 'المهمة Y']

وين كمان ممكن نستخدمها؟

جمال هذه الخوارزمية يكمن في بساطتها وتعدد استخداماتها. هي ليست مجرد حل لمشكلة واجهتني، بل هي نمط تفكير لحل الكثير من المشاكل الواقعية:

  • أنظمة البناء (Build Systems): أدوات مثل `make` أو `Maven` أو `Gradle` تستخدمها لتحديد أي الملفات يجب ترجمتها (compile) أولاً.
  • مديرو الحزم (Package Managers): عندما تقوم بتثبيت مكتبة باستخدام `npm` أو `pip`، يقوم مدير الحزم بحل شجرة الاعتماديات باستخدام الفرز الطوبولوجي ليثبت المكتبات بالترتيب الصحيح.
  • جدولة المهام (Task Schedulers): في أنظمة التشغيل أو أدوات مثل Apache Airflow، لتنفيذ سلسلة من المهام بالترتيب الصحيح.
  • المتطلبات الدراسية في الجامعات: لإنشاء مسار دراسي صحيح، لا يمكنك أخذ “خوارزميات 2” قبل “خوارزميات 1”.
  • جداول البيانات (Spreadsheets): عندما تغير قيمة في خلية `A1`، يجب إعادة حساب كل الخلايا التي تعتمد عليها (`=A1*2`) بالترتيب الصحيح.

نصايح من الخُبرة (من الشايب)

بعد سنوات من التعامل مع هذه المشاكل، اسمحوا لي أن أقدم لكم بعض النصائح العملية:

  1. اكتشاف الحلقات هو الأهم: تأكد دائماً من أن الكود الخاص بك يكتشف الحلقات الدائرية. هذه هي أكبر مصدر للمشاكل، والإبلاغ عنها بوضوح يوفر ساعات من التنقيح (debugging).
  2. لا يوجد حل واحد “صحيح”: قد يكون هناك عدة ترتيبات طوبولوجية صالحة. إذا كانت المهمة (أ) والمهمة (ب) لا تعتمدان على شيء، فيمكن البدء بأي منهما. لا تبحث عن “الترتيب الأمثل” بل عن “ترتيب صالح”.
  3. ارسمها لترىها: عندما تشعر بالضياع في الاعتماديات، أحضر قلماً وورقة (أو استخدم سبورة) وارسم المهام والأسهم بينها. مرات كثيرة، “الرسمة بتوضح اللي ألف سطر كود ما بيوضحه”.
  4. تعامل مع البيانات جيداً: تأكد من أن هيكل البيانات الذي يمثل الرسم البياني (graph) صحيح ونظيف. القمامة في المدخلات تعني قمامة في المخرجات (Garbage in, garbage out).

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

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

فيا صديقي المبرمج، المرة الجاي اللي بتشوف فيها الدنيا “معجوقة” والمهام بتضرب ببعض، خذ نفس عميق، ارسم الاعتماديات، وجرب الفرز الطوبولوجي. يمكن يكون هو فنجان القهوة الساخن اللي محتاجه الكود تبعك. 😉

أبو عمر

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

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

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

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

آخر المدونات

برمجة وقواعد بيانات

تحديثات قاعدة البيانات بدون توقف: كيف أنقذنا نمط التوسيع والتعاقد (Expand/Contract) من جحيم التوقفات المجدولة؟

هل سئمت من إيقاف الخدمة مع كل تحديث لهيكلة قاعدة البيانات؟ أشارككم قصة حقيقية وكيف أنقذنا نمط التوسيع والتعاقد (Expand/Contract) من ليالي النشر الطويلة والمُجهدة،...

4 يونيو، 2026 قراءة المزيد
الشبكات والـ APIs

كانت إعادة المحاولة كارثة: كيف أنقذتنا مفاتيح عدم تكرار العمليات (Idempotency Keys) من جحيم الفواتير المزدوجة؟

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

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

من التوقف التام إلى النجاة: كيف أنقذتنا استراتيجية “الضوء المرشد” (Pilot Light) يوم انقطعت السحابة؟

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

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

كانت مهمتي البرمجية للاختبار مجرد كود: كيف أنقذني توثيق القرارات من جحيم الصمت بعد المقابلة؟

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

4 يونيو، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

من الانتظار لأيام إلى الدفع في ثوانٍ: كيف أنقذتنا شبكات الدفع الفوري من جحيم التحويلات البنكية؟

أسرد لكم من واقع تجربتي كـ "أبو عمر"، كيف عانينا من بطء وتكلفة التحويلات البنكية الدولية، وكيف جاءت شبكات الدفع الفوري ومعيار ISO 20022 لتكون...

4 يونيو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

كان كل خادم لدينا ‘ندفة ثلج’ فريدة: كيف أنقذنا ‘الكود كبنية تحتية’ (IaC) من جحيم الانجراف اليدوي؟

في هذه المقالة، أشارككم قصة حقيقية من قلب المعركة التقنية مع "خوادم ندفات الثلج" الفوضوية. سنغوص في مفهوم "الكود كبنية تحتية" (IaC) وكيف أن أدوات...

4 يونيو، 2026 قراءة المزيد
اختبارات الاداء والجودة

كانت تغطية الاختبارات 100% لكن الأخطاء تتسرب: كيف أنقذنا “الاختبار الطفري” من جحيم الثقة الزائفة؟

كنا نظن أن تغطية الاختبار بنسبة 100% هي درعنا الواقي، لكن الأخطاء كانت تتسلل إلى الإنتاج كاللصوص في ليل بهيم. اكتشف كيف أنقذنا "الاختبار الطفري"...

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